views:

425

answers:

2

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

+3  A: 

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.)

molf
Thank you, that was an excellent explanation.
Jason
A: 

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

Marco Lazzeri