has_secure_password: how to require minimum length - ruby-on-rails-3

How do I require that a password has a minimum length when using has_secure_password in Rails 3.1? I tried to do:
class User < ActiveRecord::Base
...
validates :password, presence: { on: create }, length { minimum: 8 }
...
end
but then the validation fails with password "too short" on update when password is not provided in form params. For other attributes length is only validated if the value for the field is provided. Is this a bug in the has_secure_password implementation or am I doing something wrong?

You should do something like:
validates :password, presence: { on: create }, length { minimum: 8 }, :if => :password_changed?

Use this:
validates :password, length: { minimum: 8 }, if: :password_digest_changed?
Now this validation will run on any attempt to update the password, and it wont break otherwise, #apneaddiving's answer was good but it had some small sintax issues and would only run on creation, so any update on password wouldn't be validated.

Related

Update user attributes without changing password in rails_admin

In my app, I use devise to generate User model, add some attributes like job, role,... to this.
In rails_admin page, when edit an user, I only show fields: email, job, role, idcard (no include password and password_confirmation fields)
When I click Save, it show error because the password is shorter than 6 characters.
In the console, if I try
User.last.update_attributes(:role => "admin", "idcard" => "1233131")
It'll get the same error!
How can I fix this? Because the rails_admin (I think and be sure) didn't use update in registrations_controller to update user!
oh, yeah! I just fix my own problem by delete two validate of user model
validates :password, :length => { :minimum => 6 }
validates :password, :confirmation => true
and when I type
User.first.update_attributes(:email => "abc#yahoo.com")
It worked!!!

validates syntax in rails 3

This is my model:
class User < ActiveRecord::Base
attr_accessible :email, :name
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
end
The author of Rails Tutorial Example said "Curly braces are optional when passing hashes as the final argument in a method", but here the presence validation is not final argument, but it can be used without curly braces and is valid code. The format validation of email attribute also works.
Anybody can explain me why?
:name, :presence: true, length: { maximum: 50 } is the last argument passed to validates, so you don't need curly braces for it.
A case where you would need curly braces would be if you were passing arguments after that hash:
validates { :name, presence: true, length: { maximum: 50 } }, some_other_argument
Where some_other_argument here is some hypothetical argument that comes after the hash. To process this correctly you would need the curly braces around the hash.

Rails 3: Apply the same validation rules to multiple table fields

I have created a model with several fields that should accept the same data format (strings, but can be anything, FWIW). I'd like to apply the same validation rule to all those fields. Of course, I can just go ahead and copy/paste stuff, but that would be against DRY principle, and common sense too...
I guess this one is pretty easy, but I'm a Rails newcomer/hipster, so excuse-moi for a trivial question. =)
So if you had say three fields to validate:
:first_name
:last_name
:age
And you wanted them all to be validated? So something like this:
validates_presence_of :first_name, :last_name, :age
Edit: There are numerous different validation methods in Rails )and they're wonderfully flexible). For the format of the field you can use validates_format_of, and then use a Regular Expression to match against it. Here's an example of matching an email:
validates_format_of :email, :with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
I'd check out the Active Record Validations and Callbacks guide; it provides comprehensive insight about a lot of the features Active Record provides in terms of validation. You can also check out the documentation here.
If you are using any of the built-in validations (presence, length_of) you can apply a single validation to multiple attributes like this:
validates_presence_of :name, :email
If you have custom logic you can create a validator object to house the code and apply it individually
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || "is not an email") unless
value =~ /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
end
end
def Person
validates :home_email, :email => true
validates :work_email, :email => true
end
see: http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/
In Rails 4 you can apply the same validation to multiple columns by using a loop:
[:beds, :baths].each do |column|
validates column, allow_blank: true, length: { maximum: 25 }
end
Both beds and baths are validated using the same validations.
Edit:
In Rails 4.2 you can do this same thing by putting multiple symbols after the validates function call. Example:
validates :beds, :baths, allow_blank: true
Use Themis for this:
# Describe common validation in module
module CommonValidation
extend Themis::Validation
validates_presence_of :foo
validates_length_of :bar, :maximum => 255
end
class ModelA < ActiveRecord::Base
# import validations
include CommonValidation
end
class ModelB < ActiveRecord::Base
# import validations
include CommonValidation
end
Or you can use "with_options", for example:
with_options presence: true do |video|
REQUIRED_COLUMNS.map do |attr|
video.validates attr
end
end

Validation on missing attributes on update_attributes

The question is very simple, does update_attributes validates every possible validation of the model, even if I don't want to update some of the attributes?
I have a edit view, where the user might change his password, but only if he passes it, i.e., if it's blank the password would not change.
So I do the following in the controller:
def update
params[resource_name].delete(:password) if params[resource_name][:password].blank?
params[resource_name].delete(:password_confirmation) if params[resource_name][:password_confirmation].blank?
params[resource_name].delete(:current_password) if params[resource_name][:current_password].blank?
if resource.update_attributes(params[resource_name])
...
end
end
I defined the following validation on the model:
validates :password,
:length => { :within => 6..40 }
So whenever i use call the update I get an error saying that the password is too short
Ps.: I'm using Devise to deal it this.
EDIT: Do any of you know if Devise already have any validation on the password? Cause, I removed the validation and it worked in the right way, but if I put a short password it still show a validation, saying it's too short.
Yes, Devise does provide validations on password if you've passed :validatable to the devise method in your model. The default configuration for password length is 6..128. You can override this in config/initializers/devise.rb (around line 101).
You can remove :validatable from your model and write your own validations if you prefer. If you do this and don't want the validation to run if the password isn't passed to update_attributes, then do something like this:
validates :password, :presence => true, :if => lambda { new_record? || !password.nil? }

Rails 3 validates rule based on action

This seems like a simple question but I can't seem to find an answer short of writing custom validators. I have this validator
validates :password, :presence => true, :confirmation => true, :length => { :minimum => 5}
there are more rules applied such as some regex for complexity, but this gives the gist.
The issue is that I only want presence applied on create, everything else needs to be on create and update. Because the user may not be changing a password when updating their information.
I tried splitting the rules
validates :password, :presence => true, :on => :create
validates :password, # The rest of the rules
This resulted in all rules being ignored for update. Is there a simple way to apply only one rule to create and the rest to everything?
You can try keeping it in one line, but applying :on => :create to just the :presence check:
validates :password, :presence => {:on => :create}, :confirmation => true, :length => { :minimum => 5}
However, I'm not sure it makes sense to always require a minimum length, but not always require presence -- if you update an existing record with a blank password, it's going to fail validations anyway since the length is 0.
My hunch is that the problem is that the validate :password call is not additive. Can you switch the presence check to:
validates_presence_of :password, :on=>:create
And then keep your other validations using the validate. Does that work?