我在Rails 3.0中创建了一个自定义验证器,用于验证列的组合在表中是否唯一。整个验证代码是:
class UniqueInProjectValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
if object.new_record?
object.errors[attribute] << (options[:message] || "must be unique in each project")
else
orig_rec = object.class.find(object.id)
if value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
object.errors[attribute] << (options[:message] || "must be unique in each project")
end
end
end
end
请注意,识别if语句的功能并不容易,因此我希望能够使用unless
方法和第二个def attribute_and_project_exist?
语句替换if
条件使用def attribute_or_project_changed?
方法。但是,在创建这些方法时,由于封装,validates_each
的参数不会通过。
现在的问题是:有没有办法以某种方式干净地允许我的两个新创建的方法访问这些变量,因为可以使用模型中的列名称,或者我坚持选择再次传递每个参数还是留下难以阅读的条件陈述?
提前致谢!
答案 0 :(得分:1)
我想你可以用一个变量,一个lambda和一个“尽快返回”来清理它:
def validate_each(object, attribute, value)
# If there is no duplication then bail out right away as
# there is nothing to check. This reduces your nesting by
# one level. Using a variable here helps to make your
# intention clear.
attribute_and_project_exists = object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
return unless attribute_and_project_exists
# This lambda wraps up your second chunk of ugly if-ness and saves
# you from computing the result unless you have to.
attribute_or_project_changed = lambda do
orig_rec = object.class.find(object.id)
value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
end
# Note that || short-circuits so the lambda will only be
# called if you have an existing record.
if object.new_record? || attribute_or_project_changed.call
object.errors[attribute] << (options[:message] || "must be unique in each project")
end
end
我不知道这比你原来的好多少,但由于更好的分块,逻辑和控制流程对我来说更加清晰。