How to set "dynamically" variable values?
I am using Ruby on Rails 3.0.9 and I am trying to set "dynamically" some variable values. That is...
... in my model file I have:
attr_accessor :variable1, :variable2, :variable3
# The 'attributes' argument contains one or more symbols which name is equal to
# one or more of the 'attr_accessor' symbols.
def set_variables(*attributes)
# Here I should set to 'true' all ":variable<N>" attributes passed as symbol
# in the 'attributes' array, but variable names should be interpolated in a
# string.
#
# For example, I should 开发者_Python百科set something like "prefix_#{':variable1'.to_s}_suffix".
end
How can I set those variable values to true
?
I tried to use the self.send(...)
method, but I did not succeed (but, probably, I don't know how to use at all that send
method... is it possible do to that I need by using the send
method?!).
attr_accessor :variable1, :variable2, :variable3
def set_variables(*attributes)
attributes.each {|attribute| self.send("#{attribute}=", true)}
end
Here's the benchmark comparison of send
vs instance_variable_set
:
require 'benchmark'
class Test
VAR_NAME = '@foo'
ATTR_NAME = :foo
attr_accessor ATTR_NAME
def set_by_send i
send("#{ATTR_NAME}=", i)
end
def set_by_instance_variable_set i
instance_variable_set(VAR_NAME, i)
end
end
test = Test.new
Benchmark.bm do |x|
x.report('send ') do
1_000_000.times do |i|
test.set_by_send i
end
end
x.report('instance_variable_set') do
1_000_000.times do |i|
test.set_by_instance_variable_set i
end
end
end
And the timings are:
user system total real
send 1.000000 0.020000 1.020000 ( 1.025247)
instance_variable_set 0.370000 0.000000 0.370000 ( 0.377150)
(measured using 1.9.2)
It should be noted that only in certain situations (like this one, with accessor defined using attr_accessor
) are send
and instance_variable_set
functionally equivalent. If there is some logic in the accessor involved, there would be a difference, and you would have to decide which variant you would need of the two. instance_variable_set
just sets the ivar, while send
actually executes the accessor method, whatever it does.
Another remark - the two methods behave differently in another aspect: if you instance_variable_set
an ivar which doesn't exist yet, it will be created. If you call an accessor which doesn't exist using send
, an exception would be raised.
The method you're after is instance_variable_set
so in your case:
def set_variables(*attributes)
attributes.each {|attribute| self.instance_variable_set(attribute, true)}
end
def set_attributes(*attributes)
attributes.each do |attr|
self.send "#{attr}=", true
end
end
Remember that setter method names end with =
in Ruby.
I know the question was for Rails 3, but the question came up when searching for Rails 4 answers about "how to dynamically access variable values". I tested this on my model and it worked great as an alternative to the proposed solutions:
def set_variables(*attributes)
attributes.each {|attribute| self["#{attribute}"] = true}
end
精彩评论