kidbrax
kidbrax

Reputation: 2434

How can I implement dynamic validation in activerecord

What is the best way to adjust your validation of a model based on a parameter or action? Say I am entering a lead into a system, so all that is required is basic contact info. But then later I may enter a new contract for that user at which point I need more detailed information. So I may need phone number when just entering a lead but once they are entering into a contract I might need their birthdate and alternate phone number.

I considered using a state machine but the user could actually enter into two contracts at the same time so state doesn't really work for this scenario.

I also considered storing the extra info with the contract but since the user can have more than one contract, it needs to live with the user so it is not redundant.

So basically when saving a contract, it would tell me that the attached user is invalid if said user doesn't have the extra fields.

Upvotes: 2

Views: 4330

Answers (3)

br3nt
br3nt

Reputation: 9586

Check out conditional validations:

class Person
  validates_presence_of :given_name, family_name

  validates_presence_of :phone_number, :email_address, :if => :registered

  with_options :if => :registered do |person|
    # validations in this block are scoped to a registered user
    person.validates_presence_of :dob
  end

end

The :if option can take:

  • a symbol that corresponds to a method on the class that evaluates to true or false
  • a proc or lambda that returns a value that evaluates to true or false
  • a string containing ruby code (god knows why you'd want to do that)

You also have access to an :unless option which works in a similar fashion.

You can also encapsulate the logic to determine the current state of the user and use that to determine what validation steps you can take:

class Person

  validates_presence_of :email_address, :if => ->(p) { p.state == :pending_confirmation }

  # I actually prefer validations in this format
  validate do # stricter validations when user is confirming registration
    if confirming_membership && (given_name.blank? || family_name.blank?
      errors.add(:base, 'A full name is required')
    end
  end

  def state
    # your logic could be anything, this is just an example
    if self.confirmed_email
      :registered
    elsif confirming_membership
      :pending_confirmation
    else
      :new_user
    end
  end

  def confirming_membership
    # some logic
  end
end

Upvotes: 4

wael34218
wael34218

Reputation: 4930

You can use conditional validation for example:

validates_presence_of :phone, :if => Proc.new { |p| p.lead? }

Upvotes: 3

Michael Cruz
Michael Cruz

Reputation: 882

In whatever action the lead posts to, you could just do this:

@object.save(validate: false)

Then, when they need to enter the contract, leave off that validate: false option to ensure that those validations run.

Also, see this post if you want to skip only certain validations.

Upvotes: 0

Related Questions