pingu
pingu

Reputation: 8827

Conditional model validations dependent on controller

I have a user model, that stores, amongst other things, the users address. When creating an account (user_sessions controller) I only wish to validate the presence of their name, the address is optional.

When they upgrade their account (upgrade controller) i want to validate that that their full address is entered.

Is there an elegant way of having a conditional validation based on the controller?

Upvotes: 0

Views: 448

Answers (2)

joofsh
joofsh

Reputation: 1655

Have you tried creating a new model that inherits from the old one. So you could have:

class User < ActiveRecord::Base
  attr_accessible :name, :address
  validates_presence_of :name
end

class UpgradedUser < User
  validates_precense_of :address
end

Since it inherits from the User class, the UpgradedUser class has validations for both address and name. And this solution would still be only using one database table. Because they are still one table, you'd have to add a :account_status column or something to determine whether an account has been upgraded or not.

In your upgraded_users controller you would have a standard update method:

class upgraded_users_controller   

  def update
    @upgraded_user = UpgradedUser.find params[:id]
    respond_to do |format|
      if @upgraded_user.update_attributes params[:user]
        format.html { redirect_to @upgraded_user }
      end
    end

This method will access the same User table just like the users_controller would, but its simply doing so through the UpgradedUser model instead, and therefore would check the validators on save for both name and address presence.

Upvotes: 3

mnelson
mnelson

Reputation: 3012

Decorator / Instance Extension

You could use a decorator pattern to accomplish this. Instance extension is another option. Because we're using ruby and it's more fun here's the instance extension way:

class User < ActiveRecord::Base
  validate :decorator_validations

  protected

  def decorator_validations
    # noop at this point
  end
end


module User::Upgrader

  def upgrade!
    # do whatever upgrade operation is needed
    save
  end

  protected

  def decorator_validations
    super
    self.errors.add(:address, 'needs to be provided') unless self.address.present?
  end
end

And in your controller:

def upgrade
  @user = User.find(params[:id])
  @user.extend User::Upgrader
  if @user.upgrade!
    # success case
  else
    # failure case
  end
end

Command Pattern

An alternative way is to use a command pattern:

class UserUpgrader

 def initialize(user)
   @user = user
 end

 def call
   validate_upgrade_state
   return false if @user.errors.present?
   @user.update_attribute(:upgraded, true)
 end

 protected

 def validate_upgrade_state
   @user.errors.add(:address, 'must be present') unless @user.address.present?
 end
end

And in this case your controller would look like:

@user = User.find(params[:id])
if UserUpgrader.new(@user).call
 # success case
else
 # failure case
end

The nice thing about both of these solutions is they are very very testable.

Upvotes: 2

Related Questions