Backo
Backo

Reputation: 18871

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 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?!).

Upvotes: 22

Views: 20172

Answers (5)

streetlogics
streetlogics

Reputation: 4730

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

Upvotes: 1

Mladen Jablanović
Mladen Jablanović

Reputation: 44080

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.

Upvotes: 9

Marnen Laibow-Koser
Marnen Laibow-Koser

Reputation: 6337

def set_attributes(*attributes)
  attributes.each do |attr|
    self.send "#{attr}=", true
  end
end

Remember that setter method names end with = in Ruby.

Upvotes: 3

Arun Kumar Arjunan
Arun Kumar Arjunan

Reputation: 6857

attr_accessor :variable1, :variable2, :variable3

def set_variables(*attributes)
  attributes.each {|attribute| self.send("#{attribute}=", true)}
end

Upvotes: 65

skorks
skorks

Reputation: 4366

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

Upvotes: 4

Related Questions