web-dev-qa-db-ja.com

「動的に」変数値を設定する方法は?

Ruby on Rails 3.0.9を使用していて、いくつかの変数値を「動的に」設定しようとしています。つまり...

...私のモデルファイルには次のものがあります。

_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
_

これらの変数値をtrueに設定するにはどうすればよいですか?


self.send(...)メソッドを使用しようとしましたが、成功しませんでした(ただし、おそらく、そのsendメソッドの使用方法がまったくわかりません...可能ですか? sendメソッドを使用して必要ですか?!)。

21
Backo
attr_accessor :variable1, :variable2, :variable3

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

sendinstance_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

そして、タイミングは次のとおりです。

      user     system      total        real
send                   1.000000   0.020000   1.020000 (  1.025247)
instance_variable_set  0.370000   0.000000   0.370000 (  0.377150)

(1.9.2を使用して測定)

特定の状況(このように、アクセサがattr_accessorを使用して定義されている場合)でのみ、sendinstance_variable_setが機能的に同等であることに注意してください。関係するアクセサーに何らかのロジックがある場合は違いがあり、2つのうちどちらのバリアントが必要かを決定する必要があります。 instance_variable_setはivarを設定するだけですが、sendは実際にアクセサメソッドを実行します。

別の注意-2つのメソッドは別の側面で異なる動作をします。まだ存在しないivarをinstance_variable_setすると、作成されます。 sendを使用して存在しないアクセサーを呼び出すと、例外が発生します。

9

あなたが求めている方法はinstance_variable_setそうあなたの場合:

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

Rubyでは、setterメソッド名は=で終わることに注意してください。

3

質問がRails 3であったことは知っていますが、Rails「変数値に動的にアクセスする方法」に関する4つの回答」を検索すると、質問が表示されました。テストしました。これは私のモデルであり、提案されたソリューションの代替としてうまく機能しました。

def set_variables(*attributes)
  attributes.each {|attribute| self["#{attribute}"] = true}
end
1
streetlogics