In my form I have a virtual attributes that allows me to accept mixed numbers (e.g. 38 1/2) and convert them to decimals. I also have some validations (I’m not sure I’m handling this right) that throws an error if something explodes.
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
However, I’d like to add another column, wingspan, which would have the virtual attribute :mixed_wingspan, but I’m not sure how to abstract this to reuse it—I will be using the same conversion/validation for several dozen inputs.
Ideally I’d like to use something like accept_mixed :chest, :wingspan ... and it would take care of the custom getters, setters, validations, etc.
EDIT:
I’m attempting to recreate the functionality with metaprogramming, but I’m struggling in a few places:
def self.mixed_number(*attributes)
attributes.each do |attribute|
define_method("mixed_#{attribute}") do
"@mixed_#{attribute}" || attribute
end
end
end
mixed_number :chest
This sets chest to “@mixed_chest”! I’m trying to get the instance variable @mixed_chest like I have above.
You’re going to want a custom validator
Something like
Then you can do
Note that I’d extract the
mixed_to_decimalstuff into a separate classand that this definition lets you drop the
ifstatement in thesave_chestmethod.Now you just need to do some metaprogramming to get everything going, as I suggested in my answer to your other question. You’ll basically want something like