【发布时间】:2012-11-24 22:54:58
【问题描述】:
我有一个用户模型,其中存储用户地址等。创建帐户(user_sessions 控制器)时,我只想验证他们的姓名是否存在,地址是可选的。
当他们升级他们的帐户(升级控制器)时,我想验证是否输入了他们的完整地址。
有没有一种优雅的方法可以基于控制器进行条件验证?
【问题讨论】:
标签: ruby-on-rails ruby-on-rails-3 model controller
我有一个用户模型,其中存储用户地址等。创建帐户(user_sessions 控制器)时,我只想验证他们的姓名是否存在,地址是可选的。
当他们升级他们的帐户(升级控制器)时,我想验证是否输入了他们的完整地址。
有没有一种优雅的方法可以基于控制器进行条件验证?
【问题讨论】:
标签: ruby-on-rails ruby-on-rails-3 model controller
您是否尝试过创建一个继承旧模型的新模型。所以你可以:
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
此方法将访问同一个 User 表,就像 users_controller 一样,但它只是通过 UpgradedUser 模型这样做,因此将检查保存时的验证器是否存在名称和地址。
【讨论】:
users_controller 更新完全一样,只是这次你通过UpgradedUser 模型访问表。
您可以使用装饰器模式来完成此操作。实例扩展是另一种选择。因为我们使用的是 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
这两种解决方案的好处是它们非常可测试。
【讨论】: