条件模型验证依赖于控制器

时间:2012-11-24 22:54:58

标签: ruby-on-rails ruby-on-rails-3 model controller

我有一个用户模型,用于存储用户地址等。创建帐户(user_sessions控制器)时,我只希望验证其名称的存在,该地址是可选的。

当他们升级帐户(升级控制器)时,我想确认输入了他们的完整地址。

是否有基于控制器进行条件验证的优雅方法?

2 个答案:

答案 0 :(得分:3)

您是否尝试过创建一个继承旧模型的新模型。所以你可以:

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

class UpgradedUser < User
  validates_precense_of :address
end

由于它继承自User类,因此UpgradedUser类具有地址和名称的验证。而这个解决方案仍然只使用一个数据库表。由于它们仍然是一个表,因此您必须添加:account_status列或其他内容来确定帐户是否已升级。

在升级的用户控制器中,您将拥有标准的更新方法:

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

此方法将像users_controller一样访问同一个User表,但它只是通过UpgradedUser模型执行此操作,因此会在保存时检查验证器的名称和地址是否存在

答案 1 :(得分:2)

装饰器/实例扩展

您可以使用装饰器模式来完成此任务。实例扩展是另一种选择。因为我们使用ruby,所以实例扩展方式更有趣:

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

在你的控制器中:

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

命令模式

另一种方法是使用命令模式:

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

在这种情况下,你的控制器看起来像:

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

这两种解决方案的好处是它们非常容易测试。