I have this code in my user model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates :email, :presence => true,
:uniqueness => { :case_sensitive => false },
:format => { :with => /\A[^#]+#[^#]+\z/ },
:length => 7..128
validates :password, :presence => true,
:confirmation => true,
:length => 6..128
private
def encrypt_password
return unless password
self.encrypted_password = BCrypt::Password.create(password)
end
end
Now in my controller when I'm updating some user fields with
#user.update_attributes(params[:user])
the password field is always validated, even when it is not set in the params hash. I figured that this is happening because of the attr_accesor :password which always sets password = "" on update_attributes.
Now I could simply skip the validation of password if it is an empty string:
validates :password, :presence => true,
:confirmation => true,
:length => 6..128,
:if => "password.present?"
But this doesn't work because it allows a user to set an empty password.
Using update_attribute on the field I'd like to change is not a solution because i need validation on that attribute.
If I pass in the exact parameter with
#user.update_attributes(params[:user][:fieldname])
it doesn't solve the problem because it also triggers password validation.
Isn't there a way to prevent attr_accesor :password from always setting password = "" on update?
New answer
This works for me:
validates :password, :presence => true,
:confirmation => true,
:length => { :minimum => 6 },
:if => :password # only validate if password changed!
If I remember correctly it also took me some time to get this right (a lot of trial and error). I never had the time to find out exactly why this works (in contrast to :if => "password.present?").
Old answer - not really useful for your purpose (see comments)
I get around this problem by using a completely different action for password update (user#update_password). Now it is sufficient to only validate the password field
:on => [:create, :update_password]
(and also only make it accessible to those actions).
Here some more details:
in your routes:
resources :users do
member do
GET :edit_password # for the user#edit_password action
PUT :update_password # for the user#update_passwor action
end
end
in your UsersController:
def edit_password
# could be same content as #edit action, e.g.
#user = User.find(params[:id])
end
def update_password
# code to update password (and only password) here
end
In your edit_password view, you now have a form for only updating the password, very similar to your form in the edit view, but with :method => :put and :url => edit_password_user_path(#user)
The solution I have started using to get round this problem is this:
Start using ActiveModel's built in has_secure_password method.
At console
rails g migration add_password_digest_to_users password_digest:string
rake db:migrate
In your model:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :login_name, :password, :password_confirmation
# secure_password.rb already checks for presence of :password_digest
# so we can assume that a password is present if that validation passes
# and thus, we don't need to explicitly check for presence of password
validates :password,
:length => { :minimum => 6 }, :if => :password_digest_changed?
# secure_password.rb also checks for confirmation of :password
# but we also have to check for presence of :password_confirmation
validates :password_confirmation,
:presence=>true, :if => :password_digest_changed?
end
And finally,
# In `config/locales/en.yml` make sure that errors on
# the password_digest field refer to "Password" as it's more human friendly
en:
hello: "Hello world"
activerecord:
attributes:
user:
password_digest: "Password"
Oh, one more thing: watch the railscast
Related
I have this password validation in my user.model.rb:
class User < ActiveRecord::Base
validates :password, presence: true, confirmation: true,
format: {:with => /^(?=.*[0-9])(?=.*[A-Z])(?=.*[&%$##*])[a-zA-Z0-9&%$##*]{8,}$/,
:multiline => true, :message => I18n.t('invalid_password')}
end
I want this validation to be called in each and every method but not in my update action.
I have a users_controller.rb that has a create and update action. I want it to check in the create action but not in the update action.
I also have a password_resets_controller.rb in which I want it to check in both create and update actions but not in users_controller update action. I am using the authlogic gem. Both controllers use the same model.
Please guide me on how to do this. Thanks in advance.
validates :password, :presence => {:on => :create}
OR
##here new_user is a method that you can create and use condionally
## or validates_presence_of :password, :if => :new_user?
validates_presence_of :password, :unless => :new_user?
def new_user?
//your code
end
OR Using with_options
class User < ActiveRecord::Base
has_many :roles
# Normal Validations
validates_presence_of :password
with_options :if => :customer? do |customer|
customer.validates_presence_of :password
end
def customer?
roles.any? { |role| role.name == "customer" }
end
end
Probably the most close answer can be:
class User < ActiveRecord::Base
validates :password, presence: true, if: :not_persisted?
def not_persisted?
!self.persisted?
end
end
This code will actually trigger the validation only if the object is not persisted (on create)
Ive got the following problem. I have a model called user which has a column named activated. Im trying to update that value whith the method activated?, but it gives me the error: Validation failed: Password can't be blank, Password is too short (minimum is 6 characters) Which doesnt make sense to me, because im not touching the password field! I just want to update the activated column. Im putting here the code I think its relevant, but if you think you need more just ask :)
Thank you very much in advance!
Model:
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :activated
has_many :sucu_votes
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:length => { :within => 6..15 },
:confirmation => true
before_save :encrypt_password
def activated?
self.update_attributes!(:activated => true)
return self.activated
end
Controller from which the method activated? is called
def activate
if request.get?
user=User.find_by_id(params[:id])
if user.activated?
flash[:notice]="Your account has been activated"
#redirect_to :controller => 'sessions', :action => 'new'
else
flash[:error]="We couldnt activate the account"
redirect_to :controller => 'sessions', :action => 'new'
end
end
end
Two things, first the ruby convention is to use predicate methods to return true or false only and not to do anything more like update a record. That is not causing your problem but is a deviation from what other programmers would expect. Secondly, instead of calling update_attributes try just calling:
update_attribute(:activated, true)
This should skip the rest of the callbacks for the record
I'm trying to use has_secure_password on my user model, but have found that while it works in the application it breaks all my tests. I have a simple user model:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email
validates :email, :presence => true,
:format => { :with => /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true
end
My first test (rspec) simply confirms that I can create a new user with valid attributes:
describe User do
before(:each) do
#attr = { :forename => "Captain",
:surname => "Hook",
:email => "email#test.com",
:password => "password",
:password_confirmation => "password" }
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
end
This doesn't work, however when I do
user = User.new(#attr);
user.password = "password";
user.save
it works fine. I believe this is because has_secure_password adds a new method, password, which deals with the generation of the password_digest, so calling it directly like this generates the fields that I need. Is there any way I can use User.create but still call this method?
Turns out the problem was really simple. Since I hadn't added :password to attr_accessible it wasn't populating the field when I called User.create or User.new. The only modification I needed to make to the code was
attr_accessible :email, :password
I'm a newbie watching a Lynda.com video about rails 3. The teacher creates a method like this to find a user
def name
"#{first_name} #{last_name}"
end
He says this will return first name and last name for this user, but I don't understand how this function accesses first_name last_name, since there is no call to a database or no form parameters.
I know that without looking at the whole application it will be impossible for you to explain this, but you may be able to guess what this function might be dependent on.
this is the whole AdminUser model
require 'digest/sha1'
class AdminUser < ActiveRecord::Base
# To configure a different table name
# set_table_name("admin_users")
has_and_belongs_to_many :pages
has_many :section_edits
has_many :sections, :through => :section_edits
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
# standard validation methods
# validates_presence_of :first_name
# validates_length_of :first_name, :maximum => 25
# validates_presence_of :last_name
# validates_length_of :last_name, :maximum => 50
# validates_presence_of :username
# validates_length_of :username, :within => 8..25
# validates_uniqueness_of :username
# validates_presence_of :email
# validates_length_of :email, :maximum => 100
# validates_format_of :email, :with => EMAIL_REGEX
# validates_confirmation_of :email
# new "sexy" validations
validates :first_name, :presence => true, :length => { :maximum => 25 }
validates :last_name, :presence => true, :length => { :maximum => 50 }
validates :username, :length => { :within => 8..25 }, :uniqueness => true
validates :email, :presence => true, :length => { :maximum => 100 },
:format => EMAIL_REGEX, :confirmation => true
# only on create, so other attributes of this user can be changed
validates_length_of :password, :within => 8..25, :on => :create
before_save :create_hashed_password
after_save :clear_password
scope :named, lambda {|first,last| where(:first_name => first, :last_name => last)}
scope :sorted, order("admin_users.last_name ASC, admin_users.first_name ASC")
attr_protected :hashed_password, :salt
def name
"#{first_name} #{last_name}"
end
def self.authenticate(username="", password="")
user = AdminUser.find_by_username(username)
if user && user.password_match?(password)
return user
else
return false
end
end
# The same password string with the same hash method and salt
# should always generate the same hashed_password.
def password_match?(password="")
hashed_password == AdminUser.hash_with_salt(password, salt)
end
def self.make_salt(username="")
Digest::SHA1.hexdigest("Use #{username} with #{Time.now} to make salt")
end
def self.hash_with_salt(password="", salt="")
Digest::SHA1.hexdigest("Put #{salt} on the #{password}")
end
private
def create_hashed_password
# Whenever :password has a value hashing is needed
unless password.blank?
# always use "self" when assigning values
self.salt = AdminUser.make_salt(username) if salt.blank?
self.hashed_password = AdminUser.hash_with_salt(password, salt)
end
end
def clear_password
# for security and b/c hashing is not needed
self.password = nil
end
end
The method "name" is not finding a user from the database, however the variables inside it (first_name and last_name) are read from the corresponding database table fields. In this case assuming the usual rails conventions are followed you will find a database table called "AdminUsers" and inside it some fields of which one is first_name and another is second_name.
How this all works and why it is so can be found in the Ruby on Rails documentation for ActiveRecord
I have a user model like this:
class User < ActiveRecord::Base
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
.
.
.
end
In the User model, I have a billing_id column I want to save into from a OrdersController which looks like this:
class OrdersController < ApplicationController
.
.
.
def create
#order = Order.new(params[:order])
if #order.save
if #order.purchase
response = GATEWAY.store(credit_card, options)
result = response.params['billingid']
#thisuser = User.find(current_user)
#thisuser.billing_id = result
if #thisuser.save
redirect_to(root_url), :notice => 'billing id saved')
else
redirect_to(root_url), :notice => #thisuser.errors)
end
end
end
end
Because of validates :password in the User model, #thisuser.save doesn't save. However, once I comment out the validation, #thisuser.save returns true. This is an unfamiliar territory for me because I thought this validation only worked when creating a new User. Can someone tell me if validates :password is supposed to kick in each time I try to save in User model? Thanks
You need to specify when you want to run your validations otherwise they will be run on every save call. This is easy to limit, though:
validates :password,
:presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:on => :create
An alternative is to have this validation trigger conditionally:
validates :password,
:presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:if => :password_required?
You define a method that indicates if a password is required before this model can be considered valid:
class User < ActiveRecord::Base
def password_required?
# Validation required if this is a new record or the password is being
# updated.
self.new_record? or self.password?
end
end
It's likely because you are validating that the password has been confirmed (:confirmation => true), but the password_confirmation does not exist.
You can break this out to something like:
validates_presence_of :password, :length => { :within => 6..40 }
validates_presence_of :password_confirmation, :if => :password_changed?
I like this approach because if the user ever changes their password, it will require the user entered the same password_confirmation.