Проверка дочернего объекта с помощью проверки ActiveModel

У меня есть два простых класса Ruby: Account и Contact. Я использую simple_form_for и simple_fields_for Simple Form для создания вложенных атрибутов. Я хочу выполнить следующие требования проверки:

  1. Для новой учетной записи должен существовать связанный контакт.
  2. Связанный контакт должен быть действительным (т. е. account.contact.valid?)

Похоже, что ActiveModel больше не включает метод validates_associated, так как использование этого метода приводит к ошибке неопределенного метода. Я рассматривал необходимость ActiveRecord::Validations, но это привело к множеству различных ошибок (например, неопределенный метод `marked_for_destruction?')

Я также подумал об определении проверки в классе Account и вызове valid? для связанного объекта, но это предотвратило отправку формы только в том случае, если в родительском объекте также была ошибка.

validate do |account|
  account.contact.valid?

  # required for form to fail
  errors.add(:base, "some error")
end

Есть ли что-то, о чем я не знаю, чтобы решить эту проблему? Спасибо.


person Eric M.    schedule 23.05.2013    source источник


Ответы (2)


Недавно (через 7 лет после того, как был задан этот вопрос!) Я столкнулся с той же проблемой и решил ее, внедрив AssociatedValidator на основе ActiveRecord. Я просто включил его в папку config/initializers:

module ActiveModel
  module Validations
    class AssociatedValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        if Array(value).reject { |r| valid_object?(r) }.any?
          record.errors.add(attribute, :invalid, **options.merge(value: value))
        end
      end

      private

      def valid_object?(record)
        record.valid?
      end
    end

    module ClassMethods
      def validates_associated(*attr_names)
        validates_with AssociatedValidator, _merge_attributes(attr_names)
      end
    end
  end
end

теперь вы можете использовать validates_associated и в ActiveModel.

person coorasse    schedule 01.09.2020

class Person
  include Virtus
  include ActiveModel::Model

  attribute :address, Address, :default => Address.new

  validate :address_valid

  private

  def address_valid
    errors.add(:base, 'address is not valid') unless address.valid?
  end
end

class Address
  include Virtus::ValueObject
  include ActiveModel::Validations

  attribute :line_1, String
  attribute :line_2, String

  validates :line_1, :presence => true
  validates :line_2, :presence => true
end

Ошибки отображаются в форме, если вы передаете объект simple_fields_for:

 = form.simple_fields_for person.address do |af|      
   = af.input :line_1

Другой вариант переопределяет valid?:

def valid?
  super & address.valid?
end

Обратите внимание, что это &, а не &&, поэтому условия не замыкаются, если первое возвращает false.

person Kris    schedule 05.07.2013