跨表单对象和模型的重复验证

时间:2014-10-24 14:09:21

标签: ruby-on-rails forms validation oop activerecord

在处理Form对象和常规Rails模型时,基本验证器在哪里?

遵循从Rails中的持久层解耦表单的概念。我已经设置了一个表单对象Cage,它可以同时创建两个对象...说AnimalPlant

来自http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/https://github.com/solnic/virtushttps://github.com/makandra/active_type的表单对象示例,其中每个示例都表明Form对象本身已经过验证......没问题......部分好处包括成为能够以更加环境感知的方式验证对象。

问题:

class Animal < ActiveRecord::Base

  validates :color, presence: true
  validate :only_one_brown

  private

  def only_one_brown
    if some_complex_thing
      errors.add(:color, 'can not have more than one brown animal.')
    end
  end
end

class Plant < ActiveRecord::Base
  validates :color, presence: true
end

class Cage
  include Virtus.model # or ActiveType or whatever
  include ActiveModel::Validations

  attribute :bird_color, String
  attribute :plant_color, String

  validates :bird_color, presence: true
  validates :plant_color, presence: true

  def save
    if valid?
      animal.save!
      plant.save!
      true
    else
      false
    end
  end

  def animal
    @animal ||= Animal.new(color: bird_color)
  end

  def plant
    @plant ||= Plant.new(color: plant_color)
  end
end

如果没有:

,我如何验证动物的“只有一条棕色”规则
  1. 重复太多。
  2. 许多使Cage仍然像AR模型一样的代码
  3. 如果我们不复制验证码,当“只有一个棕色”为假时,Cage没有错误...我们会提高,这需要控制器捕获和处理,这是坏。

    如果我们复制代码,并且如果有多个自定义验证,我们将复制大量代码,并且每个处理Animal的对象都需要重复的验证。

    如果我们将验证码完全从Animal转移到Cage,类似的问题:与Animal交互的所有对象都需要知道“只有一个棕色”规则,这只是复制验证器并开辟一种简单的方法来忘记在某个地方执行它。

    如果我们将Animal的错误数组移动到Cage,则Animal的错误在:color上,这对Cage来说是不明确的,并且在客户端从未发送过的属性名称上显示错误。如果您想映射Animal的错误凯奇的钥匙,现在你需要为每个表格对象保留一张地图,感觉很臭。

    有没有好的模式或方法来处理这种情况?我觉得当你开始使用表单对象时很常见,但所有的例子都非常简单。

    提前致谢!

1 个答案:

答案 0 :(得分:0)

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/的第3点结束时,作者说:“作为奖励,由于验证逻辑通常是上下文的,因此可以在确切的位置定义,而不是需要保护验证。 ActiveRecord本身。“我同意Bryan Helmkamp,将验证放在重要的地方,你不需要复制它。

编辑:

如果我是你,我会将验证仅放在ActiveRecord模型上。我将更新Cage类:

def save
  if valid?
    ActiveRecord::Base.transaction do
      animal.save!
      plant.save!
    end
    true
  else
    false
  end
rescue Exception => exception
  raise if valid?
  false
end

我将添加一个错误方法,返回Cage,Plant和Animal实例的错误。

编辑:

我认为你可以重新定义有效的?方法,然后错误工作正常:

class Cage
  include ActiveModel::Model

  def valid_with_mymodels?
    valid_without_mymodels? && animal.valid? && plant.valid?
    animal.errors.each do |attribute, error|
      self.errors.add :"bird_#{attribute.to_s}", error
    end
    plant.errors.each do |attribute, error|
      self.errors.add :"plant_#{attribute.to_s}", error
    end
    errors.empty?
  end
  alias_method_chain :valid?, :mymodels

  ...

end

只是,小心你的名字。

我不确定Virtus是如何工作的,使用Rails 4你可以使用ActiveModel :: Model,如果使用rails 3我需要研究。

编辑:

如果您使用的是Rails 3.2,则无法使用ActiveModel :: Model,但您也可以使用它:

class Cage
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  ... 

end