Как отказаться от проверок после одного (важного) сбоя? - PullRequest
0 голосов
/ 07 февраля 2012

У меня есть класс с некоторыми проверками:

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 ]

2 голосов
/ 07 февраля 2012

Поскольку они зависят друг от друга, это не должно быть отдельной проверкой.Сделайте это:

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 голос
/ 07 февраля 2012

Поскольку вы не хотите использовать операторы 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
0 голосов
/ 05 ноября 2014

Я сделал что-то подобное. Я просто использую модуль валидации здесь, но он применяется к активным моделям записей таким же образом. Существует множество различных модификаций, которые можно сделать, но это почти та же самая реализация, которую я использовал.

  • Необходимо перечислить проверки в порядке
  • Может быть изменено, чтобы остановить любую ошибку, включая instance.errors [: base]
  • Можно ли остановить любую ошибку, используя instance.errors.any?
  • Метод with_options будет фактически передавать «yes: Proc» каждой проверке, поэтому он на самом деле
  • выполняется для каждой проверки
  • with_options можно заменить, просто указав условия if или else для каждой проверки

Определение класса. Версия без AR. То же самое можно сделать с ActiveRecord :: Базовые классы

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 > 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...