简体   繁体   中英

Rails, validations within a with_options block don't work

I have an old Rails 2 app where I have a User model that handles profiles of users coming from web and mobile applications. I would like to have 2 separate groups of validations for web and mobile users. I use DEVISE for authentication.

In order to recognize if a user relates to web or mobile, I use an attr_accessor called validation_for_mobile that I set on the controller side, before saving/updating the record.

It appears that some validations, especially the " validates_length_of :password ", get executed even if they are inside a block with_options that should not be evaluated.

Inside the User model I have this code:

# WEB VALIDATIONS
with_options :if => "self.validation_for_mobile == false && !self.remote_sync" do |vm1|
    vm1.validates_date :birthday, :allow_blank => true
    vm1.validates_presence_of :company, :name, :surname, :address, :city, :province, :postal_code, :position, :company_type, :phone, :unless => :is_imported
    vm1.validates_format_of :phone, :with => /\A([0-9]+)\Z/i, :message => 'deve contenere solo numeri', :unless => :is_imported

    vm1.validates_presence_of :vat, :if => "!imported && !self.script_imported && company_type != 'individuale' && !company_type.blank?"
    vm1.validates_presence_of :social_number, :if => "!imported && !self.script_imported && company_type == 'individuale' && !company_type.blank? "
    vm1.validates_presence_of  :remote_id, :if => "!came_from_user && !imported && !script_imported"
    vm1.validates_acceptance_of :privacy_accepted, :privacy_third_part, :accept => true, :allow_nil => false, :unless => :is_imported
    vm1.validates_presence_of :login, :email
    vm1.validates_email_format_of :email, :message => "email non valida"
    vm1.validates_presence_of :remote_sap_code, :remote_as_code, :if => "!came_from_user"
    vm1.validates_uniqueness_of :login, :scope => :branch_id, :case_sensitive => false
    vm1.with_options :if => :password_required? do |v|
      v.validates_presence_of :password
      v.validates_confirmation_of :password
      v.validates_format_of :password, :with => /^(?=(.*\d){1})(?=.*[a-zA-Z])[0-9a-zA-Z]+$/i, :message => 'solo lettere e almeno 1 numero!'
      v.validates_length_of :password, :within => 6..10, :allow_blank => true
    end
  end


# MOBILE VALIDATIONS
with_options :if => "self.validation_for_mobile == true && !self.remote_sync" do |vm2|
  with_options :if => :mobile_validations do |vm2|
    vm2.validates_presence_of :company, :vat, :name, :surname, :phone
    vm2.validates_format_of :phone, :with => /\A([0-9]+)\Z/i, :message => 'deve contenere solo numeri'
    vm2.validates_length_of :vat, :within => 11..15
    vm2.validates_presence_of :login, :email, :branch_id
    vm2.validates_email_format_of :email, :message => "email non valida"
    vm2.validates_uniqueness_of :login, :scope => :branch_id, :case_sensitive => false
    vm2.with_options :if => :password_required? do |v|
      v.validates_presence_of :password
      v.validates_confirmation_of :password
      v.validates_format_of :password, :with => /^(?=(.*\d){1})(?=.*[a-zA-Z])[0-9a-zA-Z]+$/i, :message => 'solo lettere e almeno 1 numero!'
      v.validates_length_of :password, :within => 1..10, :allow_blank => true
    end
  end

Every time I try to create a new user from mobile, I expect only the second block of validations to be evaluated, where " validates_length_of :password " is " within => 1..10 "

But I receive the error that password is too short (at least 6 chars). And that's the first block!!!

Can you help me?

I also tried to move the conditions of with_options inside a method, like this:

with_options :if => :web_validations
with_options :if => :mobile_validations

but nothing changed.

This is the code I use to test it:

@user = User.new
@user.company = "Testuser"
@user.vat = "123456789012"
@user.name = "Fafag"
@user.surname = "Fafag"
@user.phone = "9182624"
@user.email = "utentest9876@test.it"
@user.login = "Utentest63"
@user.branch_id = 2

@user.password = "TEST1"
@user.password_confirmation = "TEST1"
@user.plain_password = "TEST1"

@user.mobile_signup = true
@user.mobile = true
@user.validation_for_mobile = true    # model custom validation
@user.renew_mobile_token!

@user.came_from_user = true
@user.remote_id = 0
@user.confirmed = nil
@user.active = false

if @user.save
  return true
else
  puts @user.errors.full_messages.inspect
end

Ok, I solved this puzzle!

It seems nested with_options blocks don't inherit the outer condition. So I had to specify the first condition inside the nested with_option block, like this:

with_options :if => :mobile_validations do |vm2|
    vm2.validates_presence_of :company, :vat, :name, :surname, :phone
    vm2.validates_format_of :phone, :with => /\A([0-9]+)\Z/i, :message => 'deve contenere solo numeri'
    vm2.validates_length_of :vat, :within => 11..15
    vm2.validates_presence_of :login, :email, :branch_id
    vm2.validates_email_format_of :email, :message => "email non valida"
    vm2.validates_uniqueness_of :login, :scope => :branch_id, :case_sensitive => false
    vm2.with_options :if => [:password_required?, :mobile_validations] do |v2|
      v2.validates_presence_of :password
      v2.validates_confirmation_of :password
      v2.validates_format_of :password, :with => /^(?=(.*\d){1})(?=.*[a-zA-Z])[0-9a-zA-Z]+$/i, :message => 'solo lettere e almeno 1 numero!'
      v2.validates_length_of :password, :within => 1..10, :allow_blank => true
    end
  end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM