Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-state Validation in Rails

I'm working on a Rails app that existing users can invite additional members to join. The problem with this is that the User model exists in different states and in those different states, different sets of information is required.

For example, John is a member of the site and invites Mary. John enters Mary's name and email address, a user record is created in the database for Mary, and an invitation email is sent. After she joins, however, the required set of data changes and we require her to enter additional information (e.g. a password).

I'm still learning Ruby on Rails and I don't see any way to handle this using the standard validation techniques of validates_presence_of, validates_format_of, etc. Can anyone point me in the right direction

like image 354
Jason Avatar asked May 27 '09 23:05

Jason


People also ask

What is Active Record in Ruby on Rails?

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.

What is the difference between validate and validates in Rails?

So remember folks, validates is for Rails validators (and custom validator classes ending with Validator if that's what you're into), and validate is for your custom validator methods.

How does validation work in Rails?

Rails validation defines valid states for each of your Active Record model classes. They are used to ensure that only valid details are entered into your database. Rails make it easy to add validations to your model classes and allows you to create your own validation methods as well.


2 Answers

The easiest is to use :if as follows:

class User < ActiveRecord::Base
  validate_presence_of :name
  validate_presence_of :age, :if => Proc.new { |user| user.signup_step >= 2 }
  # ... etc
end

or:

class User < ActiveRecord::Base
  validate_presence_of :name
  validate_presence_of :age, :if => :registering?

  def registering?
    signup_step >= 2
  end
end

You can also use the validate method to define any complex, custom logic. For example:

class User < ActiveRecord::Base
  validate :has_name_and_email_after_invitation
  validate :has_complete_profile_after_registration

  def has_name_and_email_after_invitation
    if ... # determine if we're creating an invitation
      # step 1 validation logic here
    end
  end

  def has_complete_profile_after_registration
    if ... # determine if we're registering a new user
      # step 2 validation logic here
    end
  end 
end

(In the above example, you could actually define the validation rules in has_name_and_email_after_invitation with regular validates_xxx_of calls, because they must apply in step 2 as well, but using two methods for the separate steps gives you maximum flexibility.)

like image 166
molf Avatar answered Sep 28 '22 10:09

molf


And, for DRYin'up your code a bit, you can use with_options, like this:

class Example < ActiveRecord::Base
  [...]
  def registering?
    signup_step >= 2
  end
  with_options(:if => :registering?) do |c|
    c.validates_presence_of :name
  end

  with_options(:unless => :registering?) do |c|
    c.validates_presence_of :contact_details
  end
  [...]
end

Find out more about with_options here:

http://apidock.com/rails/v2.3.2/Object/with_options

There's even a screencast by RailsCasts:

http://railscasts.com/episodes/42-with-options

like image 45
Marco Lazzeri Avatar answered Sep 28 '22 12:09

Marco Lazzeri