在我的表单中,我有一个虚拟属性,允许我接受混合数字(例如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
但是,我想添加另一个列,翼展,它将具有虚拟属性: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
。
答案 0 :(得分: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
语句。
现在你只需要进行一些元编程来完成所有工作,就像我在你的另一个问题上提出in my answer一样。你基本上想要像
这样的东西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