问题
I'm wondering if there is a way to set a virtual attribute so that it can handle multiple fields/variables.
Basically, I have several different integer columns in my model (income, taxes) and for each of them I need to check when the form is submitted, and remove illegal characters, which I'm doing by setting a virtual attribute and using tr
to strip the characters.
def flexible_income
income
end
def flexible_income=(income)
self.income = income.tr('$ ,', '') unless income.blank?
end
And then I'm setting strong parameters in the controller as should be done:
params.required(:tax).permit(:flexible_income, :flexible_taxes)
The problem is I have many fields (more than just the two I listed above), and so for each of those fields, (many of which just need to check for the exact same illegal characters) I have to use a new virtual attribute and basically just repeat the same code:
def flexible_taxes
taxes
end
def flexible_taxes=(taxes)
self.taxes = taxes.tr('$ ,', '') unless taxes.blank?
end
Is there anyway to just set a common shared attribute for many different fields, while still being able to set the strong parameters?
回答1:
You can always use meta programming to dynamically generate all this repeated code.
module Flexible
FLEXIBLE_ATTRIBUTES.each do |attr|
define_method("flexible_#{attr}") do
self.send(attr)
end
define_method("flexible_#{attr}=") do |val|
self.send("#{attr}=", val.tr('$ ,', '')) unless val.blank?
end
end
end
Now include this module in you class and define the FLEXIBLE_ATTRIBUTES
:
class MyModel
FLEXIBLE_ATTRIBUTES = [:income, :taxes] #......
include Flexible
end
Now I haven't tested this code but it should do what you're looking for with minimum repetitiveness. It also separates the Flexible method creation logic outside of the model itself. It will also allow you to reuse this module in other classes if needed.
回答2:
You can take advantage of ruby's meta programming define_method
Define virtual attributes for those list of column's like
['income','taxes'...].each do |attr|
define_method("flexible_#{attr}") do |arg|
eval "#{attr}"
end
define_method("flexible_#{attr}=") do |arg|
eval "self.#{attr} = #{arg}.tr('$ ,', '') unless #{arg}.blank?"
end
end
Refer:
https://rubymonk.com/learning/books/2-metaprogramming-ruby/chapters/25-dynamic-methods/lessons/72-define-method
http://apidock.com/ruby/Module/define_method
http://apidock.com/ruby/Kernel/eval
来源:https://stackoverflow.com/questions/21820599/single-virtual-attribute-definition-for-multiple-fields