model validate only for create - ruby-on-rails

There's a way of validate a field only for the create action ?
Supose,
class Client < ActiveRecord::Base
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
in that case the password field will be required to save anything to db #client.save
What I want is that the password must be present only for: create the client and update the password, any other field update doesn't need password.

You can add the following to your validation.
on: :create
For your code it will be
validates :password, presence: true, length: { minimum: 6 } , on: :create
If you want the validation for multiple action then you can provide array of symbols.
On : [:create ,:edit]

Related

Ruby - Validates field presence after create

Is it possible to validate a field for presence after the initial creation?
I want to make phone number mandatory if the user wants to update their account after signing up.
validates :phone, presence: true, if: .....
if I use on: :update I can no longer authenticate until the field is filled
There are many ways to accomplish this task assuming it is a normal Rails model backed by a DB table. Off the top of my head you can do:
validates :phone,
presence: true,
if: Proc.new{ |model| model.id.present? }
Or more to the point and doesn't fail if you assign an ID before saving:
validates :phone,
presence: true,
if: Proc.new{ |model| model.persisted? }

Update action with empty password

In Michael Hartl's tutorial, the following is stated.
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
In case you’re worried that Listing 9.10 might allow new users to sign up with empty passwords, recall from Section 6.3.3 that has_secure_password includes a separate presence validation that specifically catches nil passwords.
My question is, how is the has_secure_password validation working if it allows the test to pass? I do not understand, clearly the has_secure_password validation is not "catching" this rule to bypass an empty password.
Further, how does rails know not to set and save the empty passwords to the user? please help me.
You can check the documentation here , it explains everything in details,
According to the source code:
def has_secure_password
.....
if options.fetch(:validations, true)
include ActiveModel::Validations
# This ensures the model has a password by checking whether the password_digest
# is present, so that this works with both new and existing records. However,
# when there is an error, the message is added to the password attribute instead
# so that the error message will make sense to the end-user.
validate do |record|
record.errors.add(:password, :blank) unless record.password_digest.present?
end
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
validates_confirmation_of :password, allow_blank: true
end
..........
end
if you did not call has_secure_password(validations: false) the three types of validations will be added. i think the reason of the passing test is :
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
The :allow_nil option skips the validation when the value being
validated is nil.
To Add
how does rails know not to set and save the empty passwords to the
user?
I think it's because the params[:user][:password] is blank and not nil

How can I update particular fields of model with validation in Ruby on Rails?

There is an AcviteRecord Model named User like this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :presence => true, :uniqueness => true
validates :plain_password, :presence => true, :confirmation => true
validates :plain_password_confirmation, :presence => true
#...other codes
end
It requires that the update of name and email and the update of password are separated.
When only update name and password, using update or update_attributes will cause password validation which is not needed. But using update_attribute will save name and email without validation.
Are there any ways to update particular fields of model with validation without causing the other fields' validation?
Give it a try, might help
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, :uniqueness => true
validates :plain_password, length: { in: 4..255, allow_nil: true }, confirmation: true
validates :plain_password_confirmation, presence: true, if: -> (user){ user.plain_password.present? }
# ......
# ......
end
Apart from this you should reconsider about saving plain_password ;)
You can adjust your validations to only run on create. Requiring confirmation ensures changes on edit are applied.
validates :plain_password,
confirmation: true,
presence: {
on: :create },
length: {
minimum: 8,
allow_blank: true }
validates :plain_password_confirmation,
presence: {
on: :create }
I am assuming you are hashing your passwords, so this would accompany code similar to:
attr_accessor :plain_password
before_save :prepare_password
def encrypted_password( bcrypt_computational_cost = Rails.env.test? ? 1 : 10)
BCrypt::Password.create plain_password, cost: bcrypt_computational_cost
end
private #===========================================================================================================
# Sets this users password hash to the encrypted password, if the password is not blank.
def prepare_password
self.password_hash = encrypted_password if plain_password.present?
end
A better way to handle this is to not include the fields in the rest of the edit form, but rather provide a link to "Change my password". This link would direct to a new form (perhaps in a modal window) which will require the new password, confirmation of the new password, and the old password, to prevent account hijacking.
In your case you can use has_secure_password The password presence is only validated on creation.

Ruby on rails update client with and without new password

I have a edit form for a client. It is also possible to change the password, but of course you don't want to change(and/or reenter) your password every time you change on of the other settings. To avoid updating the password I tried this:
def client_update_params
if admin? == true
params.require(:client).permit(:name, :email,:company_name,
:address_street,:address_number,:address_city,
:address_zip,:address_country,:billing_informations)
else
if params[:client][:password].blank?
params[:client].delete("password")
params[:client].delete("password_confirmation")
params.require(:client).permit(:name, :email,:company_name,
:address_street,:address_number,:address_city,
:address_zip,:address_country)
else
params.require(:client).permit(:name, :email,:company_name,
:address_street,:address_number,:address_city,
:address_zip,:address_country,
:password,:password_confirmation)
end
end
end
So the idea is to check if the password field is set or not. If it is set, update with new password, else do not update the password. But every time I hit submit(and leave the password field empty), the form validation says the password is to short....
Is there maybe a working/more elegant solution for this problem ?
EDIT:VALIDATIONS ON MODEL:
attr_accessor :is_admin_applying_update
attr_accessor :plain_password
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :company_name,presence:true
validates :address_street,presence:true
validates :address_number,presence:true
validates :address_city,presence:true
validates :address_zip,presence:true
validates :address_country,presence:true
validates :billing_informations,presence:true
has_secure_password
validates :password, length: { minimum: 6 }, :unless => :is_admin_applying_update
def Client.new_remember_token
SecureRandom.urlsafe_base64
end
def Client.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = Client.encrypt(Client.new_remember_token)
end
Remember that you don't really have a password attribute in your model. The password is stored encrypted in a field named password_digest.
You should only be validating the password attribute when a password and a password_confirmation is given. You can do this in your model validation rather than deleting things from the params hash.
Also, you should validate the existence of a password_confirmation if password is present.
Add a virtual attribute skip_password like the devise uses
In model
attr_accessible :skip_password
validates :password, length: { minimum: 6 }, :unless => :skip_password
def skip_password=(value)
#skip = value
end
def skip_password
#skip
end
In controller
#client.skip_password = true

Updating profile without updating password

I want to be able to update my user information without having to have the user set a password each time they edit any other attribute.
My current validations are:
validates :password, presence: true, length: { minimum: 8 }
validates :password_confirm, presence: true
how can I make this conditional? It would be clever to only require these validations if the password and password_confirm attribues were in the params. I could use some idea about how to achieve this. Thanks.
I think this should do it:
validates :password, presence: true, length: { minimum: 8 }
validates :password_confirm, presence: true, :if => Proc.new { |a| a.password_changed? || a.new_record? }
See the docs on ActiveModel::Dirty for more on the _changed? methods. There should be no problem running the presence validation on password every time you change an attribute since this will persist in the DB, so it will always pass (whereas password_confirm will not persist and thus requires a conditional).

Resources