单一(重要)故障后如何放弃验证?

时间:2012-02-06 23:47:59

标签: ruby-on-rails ruby validation

我有一个有一些验证的课程:

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,但这样会感觉很麻烦,并且会使代码的可读性降低,而且当验证次数变得更大时,它也不会很干。

如果第一个验证失败,有没有办法取消剩余的验证?

3 个答案:

答案 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)

我做了这样的事。我只是在这里使用验证模块,但它以相同的方式应用于活动记录模型。可以对此进行许多不同的修改,但这几乎就是我使用的确切实现。

  • 必须按顺序列出验证
  • 可以修改为暂停任何错误,包括instance.errors [:base]
  • 可以使用instance.errors.any停止任何错误吗?
  • with_options方法实际上会传递&#34;除非:Proc&#34;每次验证,实际上
  • 为每次验证运行
  • with_options可以通过在每个验证中添加if或unless条件来替换

类定义。非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 >