I have table with fields: email, password_hash and salt.
On user registration, I want to validate password and password confirmation, but I always get error that says can't be blank for password input.
This is my model:
attr_accessor :password
validates :password, :presence     => true,
                     :confirmation => true,
                     :length       => { :within => 6..40 }
I guest I can't validate field that is not it the database, but how to make validation on password that user enters?
I don't have password field in database because I encypt that password input I get and then I store it in db password_hash field.
EDIT :
This is database table:
class CreateStudents < ActiveRecord::Migration
  def change
    create_table :students do |t|
      t.string :first_name
      t.string :last_name
      t.string :email
      t.string :password_hash
      t.string :salt
      t.boolean :activated
      t.timestamps
    end
  end
end
This is the view for registration:
<%= form_for @student, url: {action: 'create'} do |form| %>
  <p style="font-size:smaller; color:red">
  <% @student.errors.messages.each do |att, msg| %>
    <%= msg[0] %><br>
  <% end %>
  <p>
  <%= form.label :first_name %><br>
  <%= form.text_field :first_name %>
  <p>
  <%= form.label :last_name %><br>
  <%= form.text_field :last_name %>
  <p>
  <%= form.label :email %><br>
  <%= form.text_field :email %>
  <p>
  <%= form.label :password %><br>
  <%= form.password_field :password %>
  <p>
  <%= form.label :password_confirmation %><br>
  <%= form.password_field :password_confirmation %>
  <p>
  <%= form.submit %>
<% end %>
This is the controller:
def sing_up
    @student = Student.new
    render 'sing_up'
end
def create
    @student = Student.new
    @student.first_name = params[:student][:first_name]
    @student.last_name = params[:student][:last_name]
    @student.email = params[:student][:email]
    @student.salt = BCrypt::Engine.generate_salt
    @student.password_hash = BCrypt::Engine.hash_secret(params[:student][:password], @student.salt)
    if @student.save
        redirect_to controller: 'singups', action: 'index'
    else
        render 'sing_up'
    end
end
and finnaly, this is model
class Student < ActiveRecord::Base
  attr_accessor :password
    validates :first_name, :length => { minimum: 3, message: "first_name" }
    validates :last_name, :length => { minimum: 3, message: "last_name" }
    validates :email, :presence => { message: 'email' },
              :format => { with: /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/ , message: 'email format' },
              :uniqueness => { message: 'uniqueness?' }
    validates :password, :confirmation => { message: 'password' },
                         :length => { minimum: 6, message: 'password length' }
end
Every time user enters password it fails on first validation :confirmation => { message: 'password' }, no matter what the password was.
If I remove validates :password part, everything works fine, but password that user enters is not validated.
You're validating the user's password attribute but you never assign it a value in the first place. At the least, you need to add this to your controller code:
@student.password = params[:student][:password]
@student.password_confirmation = params[:student][:password_confirmation]
However, the more concise way is to use mass-assignment - get rid of all the @student.xxx = yyy and replace it with this:
@student = Studen.new(params[:student])
Then, move your password hashing method into the model and trigger it automatically before each save if the password attribute is present:
class User < ActiveRecord::Base
  # ...
  before_save :hash_password, :if => proc{ |u| !u.password.blank? }
  # ....
  protected
  def hash_password
    self.salt = BCrypt::Engine.generate_salt
    self.password_hash = BCrypt::Engine.hash_secret(password, salt)
  end
end
This way you don't have to do anything in the controller but this:
def create
  @student = Student.new(params[:student])
  if @student.save
    redirect_to controller: 'singups', action: 'index'
  else
    render 'sing_up'
  end
end
and have all other logic in your model, where it belongs.
EDIT: In order for mass assignment to work with the latest versions of Rails 3, you'll need to make the attributes you want to assign like that attr_accessible, like this:
class Student < ActiveRecord::Base
  # ...
  attr_accessible :first_name, :last_name, :email, :password # but NOT the hash or salt!!
end
                        You should check out the Railscast dealing with authentiction from scratch, all is explained here http://railscasts.com/episodes/250-authentication-from-scratch
There is an updated cast here http://railscasts.com/episodes/250-authentication-from-scratch-revised but you will need a subscription to view that (Well worth it IMO)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With