0

在我的表单中,我有一个虚拟属性,允许我接受混合数字(例如 38 1/2)并将它们转换为小数。我也有一些验证(我不确定我是否正确处理了这个),如果发生爆炸会引发错误。

class Client < ActiveRecord::Base
  attr_accessible :mixed_chest

  attr_writer :mixed_chest

  before_save :save_mixed_chest

  validate :check_mixed_chest

  def mixed_chest
    @mixed_chest || chest
  end

  def save_mixed_chest
    if @mixed_chest.present?
      self.chest = mixed_to_decimal(@mixed_chest)
    else
       self.chest = ""
    end
  end

  def check_mixed_chest
    if @mixed_chest.present? && mixed_to_decimal(@mixed_chest).nil?
      errors.add :mixed_chest, "Invalid format. Try 38.5 or 38 1/2"
    end
  rescue ArgumentError
    errors.add :mixed_chest, "Invalid format. Try 38.5 or 38 1/2"
  end

  private

  def mixed_to_decimal(value)
    value.split.map{|r| Rational(r)}.inject(:+).to_d
  end

end 

但是,我想添加另一列wingspan,它具有 virtual 属性:mixed_wingspan,但我不知道如何抽象它以重用它——我将对几十个输入使用相同的转换/验证。

理想情况下,我想使用类似的东西accept_mixed :chest, :wingspan ...,它会处理自定义的 getter、setter、验证等。

编辑:

我正在尝试使用元编程重新创建功能,但我在几个地方遇到了困难:

def self.mixed_number(*attributes)
  attributes.each do |attribute|
    define_method("mixed_#{attribute}") do
      "@mixed_#{attribute}" || attribute
    end
  end
end

mixed_number :chest

这会将胸部设置为“@mixed_chest”!我正在尝试@mixed_chest像上面那样获取实例变量。

4

1 回答 1

1

您将需要一个自定义验证器

就像是

class MixedNumberValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value.present? && MixedNumber.new(value).to_d.nil?
      record.errors[attribute] << (options[:message] || "Invalid format. Try 38.5 or 38 1/2")
    end
  end
end

然后你可以做

validates :chest, mixed_number: true

请注意,我会将这些mixed_to_decimal东西提取到一个单独的类中

class MixedNumber
  def initialize(value)
    @value = value
  end

  def to_s
    @value
  end

  def to_d
    return nil if @value.blank?
    @value.split.map{|r| Rational(r)}.inject(:+).to_d
  rescue ArgumentError
    nil
  end
end

并且此定义允许您删除方法if中的语句save_chest

现在您只需要进行一些元编程即可让一切顺利进行,正如我在回答您的另一个问题时所建议的那样。你基本上会想要类似的东西

def self.mixed_number(*attributes)
  attributes.each do |attribute|
    define_method("mixed_#{attribute}") do
      instance_variable_get("@mixed_#{attribute}") || send(attribute)
    end

    attr_writer "mixed_#{attribute}"

    define_method("save_mixed_#{attribute}") do
      # exercise for the reader ;)
    end

    before_save "save_#{attribute}"
    validates "mixed_#{attribute}", mixed_number: true
  end
end

mixed_number :chest, :waist, :etc
于 2012-12-12T23:16:55.380 回答