Reputation: 8827
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
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
Reputation: 3012
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
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