2

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.

kidbrax
  • 2,364
  • 3
  • 30
  • 38

3 Answers3

4

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
br3nt
  • 9,017
  • 3
  • 42
  • 63
  • I have looked at this but I am trying how to figure out how to check before the state of the user changes. So they are about to register so i want stricter validation. They are not registered yet. Hope that makes sense. – kidbrax Dec 15 '15 at 23:01
  • You might want to use `new_record?` (http://apidock.com/rails/ActiveRecord/Base/new_record%3F) – wael34218 Dec 15 '15 at 23:09
  • Ultimately, it depends on your workflow. You still have plenty of options available to you. I'll update the question with a few other suggestions. – br3nt Dec 15 '15 at 23:11
3

You can use conditional validation for example:

validates_presence_of :phone, :if => Proc.new { |p| p.lead? }
wael34218
  • 4,860
  • 8
  • 44
  • 62
0

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.

Community
  • 1
  • 1
Michael Cruz
  • 864
  • 6
  • 13