我有一个有一些验证的课程:
class HeyThere < ActiveRecord::Base
validate :check_something
validate :check_something_property_1
validate :check_something_property_2
validate :check_something_property_3
def check_something
errors.add(:something, "not there") if something.nil?
end
def check_something_property_1
errors.add(:something, "bad property 1") if something.property_1 > 10
end
def check_something_property_2
errors.add(:something, "bad property 2") if something.property_2 == "ha!"
end
def check_something_property_3
errors.add(:something, "bad property 3") if something.property_3
end
end
问题是,如果某些东西不存在,第一个验证会触发,但第二个验证会抛出异常:
undefined method `property_1' for nil:NilClass
对于该示例,我给出了验证的一般示例,但实际上,它们相当复杂。我可以将每一个更改为if something && something.property_N whatever
,但这样会感觉很麻烦,并且会使代码的可读性降低,而且当验证次数变得更大时,它也不会很干。
如果第一个验证失败,有没有办法取消剩余的验证?
答案 0 :(得分:2)
由于它们彼此依赖,因此这些不应是单独的验证。这样做:
class PastaRecipe < ActiveRecord::Base
validate :have_ingredients
private
def have_ingredients
# Don't run the remaining validations if any one validation fails.
have_pasta &&
have_sauce &&
have_water
end
def have_pasta
errors.add(:pasta, "need to buy pasta!") unless pasta.purchased?
end
def have_sauce
errors.add(:sauce, "need delicious sauce!") unless sauce.delicious?
end
def have_water
errors.add(:water, "need to boil water!") unless water.boiled?
end
end
答案 1 :(得分:1)
由于您不想使用if-else语句,我建议使用raise
来暂停整个验证链:
class HeyThere < ActiveRecord::Base
validate :check_something
validate :check_something_property_1
validate :check_something_property_2
validate :check_something_property_3
def check_something
if something.nil?
errors.add(:something, "not there")
raise ActiveRecord::RecordInvalid, self
end
end
def check_something_property_1
errors.add(:something, "bad property 1") if something.property_1 > 10
end
def check_something_property_2
errors.add(:something, "bad property 2") if something.property_2 == "ha!"
end
def check_something_property_3
errors.add(:something, "bad property 3") if something.property_3
end
end
答案 2 :(得分:0)
我做了这样的事。我只是在这里使用验证模块,但它以相同的方式应用于活动记录模型。可以对此进行许多不同的修改,但这几乎就是我使用的确切实现。
类定义。非AR版本。使用ActiveRecord :: Base类可以完成相同的操作
class Skippy
# wouldnt need to do this initialize on the ActiveRecord::Base model version
include ActiveModel::Validations
validate :first
validate :second
validate :halt_on_third
validates_presence_of :or_halt_on_fourth
with_options unless: Proc.new{ |instance| [:halt_on_thirds_error_key, :or_halt_on_fourth].any?{ |key| instance.errors[key].any? } } do |instance|
instance.validate :wont_run_fifth
instance.validates_presence_of :and_wont_run_sixth
end
# wouldn't need to do these on the ActiveRecord::Base model version
attr_accessor :attributes
def initialize
@attributes = { or_halt_on_fourth: "I'm here" }
end
def read_attribute_for_validation(key)
@attributes[key]
end
def first
errors.add :base, 'Base error from first'
end
def second
errors.add :second, 'Just an error'
end
def halt_on_third
errors.add :halt_on_thirds_error_key, 'Halting error' unless @donthalt
end
def wont_run_fifth
errors.add :wont_run_fifth, 'ran because there were no halting errors'
end
end
演示
2.0.0 :040 > skippy = Skippy.new
=> #<Skippy:0x0000000d98c1c0 @attributes={:or_halt_on_fourth=>"I'm here"}>
2.0.0 :041 > skippy.errors.any?
=> false
2.0.0 :042 > skippy.valid?
=> false
2.0.0 :043 > skippy.errors.full_messages
=> ["Base error from first", "Second Just an error", "Halt on thirds error key Halting error"]
2.0.0 :044 > skippy.errors.clear
=> {}
2.0.0 :045 > skippy.instance_variable_set(:@donthalt, true)
=> true
2.0.0 :046 > skippy.errors.any?
=> false
2.0.0 :047 > skippy.valid?
=> false
2.0.0 :048 > skippy.errors.full_messages
=> ["Base error from first", "Second Just an error", "Wont run fifth ran because there were no halting errors", "And wont run sixth can't be blank"]
2.0.0 :049 > skippy.errors.clear; skippy.attributes = {}; skippy.errors.any?
=> false
2.0.0 :050 > skippy.valid?; skippy.errors.full_messages
=> ["Base error from first", "Second Just an error", "Or halt on fourth can't be blank"]
2.0.0 :051 >