Rubyでローカル変数を動的に設定することに興味があります。メソッド、定数、またはインスタンス変数を作成していません。
だから次のようなもの:
args[:a] = 1
args.each_pair do |k,v|
Object.make_instance_var k,v
end
puts a
> 1
問題のメソッドがモデル内に存在し、グローバル空間またはオブジェクト空間を汚染したくないため、特にローカル変数が必要です。
ここでの問題は、each_pair内のブロックのスコープが異なることです。そこに割り当てられたローカル変数は、そこでのみアクセスできます。たとえば、これは次のとおりです。
args = {}
args[:a] = 1
args[:b] = 2
args.each_pair do |k,v|
key = k.to_s
eval('key = v')
eval('puts key')
end
puts a
これを生成します:
1
2
undefined local variable or method `a' for main:Object (NameError)
これを回避するには、ローカルハッシュを作成し、このハッシュにキーを割り当てて、次のようにアクセスします。
args = {}
args[:a] = 1
args[:b] = 2
localHash = {}
args.each_pair do |k,v|
key = k.to_s
localHash[key] = v
end
puts localHash['a']
puts localHash['b']
もちろん、この例では、キーの文字列を含む元のハッシュをコピーしているだけです。ただし、実際のユースケースはもっと複雑だと思います。
将来の読者のための追加情報として、Ruby 2.1.0から、binding.local_variable_get
およびbinding.local_variable_set
を使用できます。
def foo
a = 1
b = binding
b.local_variable_set(:a, 2) # set existing local variable `a'
b.local_variable_set(:c, 3) # create new local variable `c'
# `c' exists only in binding.
b.local_variable_get(:a) #=> 2
b.local_variable_get(:c) #=> 3
p a #=> 2
p c #=> NameError
end
doc で述べられているように、これは
binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
興味深いことに、変更ローカル変数はできますが、設定できません:
def test
x=3
eval('x=7;')
puts x
end
テスト=> 7
def test
eval('x=7;')
puts x
end
test => NameError:main:Objectの未定義のローカル変数またはメソッド `x '
これが、DorkusPrimeのコードが機能する唯一の理由です。
ハッシュを使用することをお勧めします(ただし、他の選択肢については読み続けてください)。
なぜ?
任意の名前付き引数を許可すると、コードが非常に不安定になります。
これらの理論上の名前付き引数を受け入れたいメソッドfoo
があるとします。
シナリオ:
呼び出されたメソッド(foo
)は、引数をとらないプライベートメソッド(bar
と呼びましょう)を呼び出す必要があります。ローカル変数foo
に格納したい引数をbar
に渡すと、bar
メソッドがマスクされます。回避策は、bar
を呼び出すときに明示的な括弧を付けることです。
foo
のコードがローカル変数を割り当てているとしましょう。ただし、呼び出し元は、そのローカル変数と同じ名前の引数を渡すことにします。割り当ては引数を覆します。
基本的に、メソッドの呼び出し元は、メソッドのロジックを変更できないようにする必要があります。
代替
別の中間点にはOpenStruct
が含まれます。ハッシュを使用するよりもタイピングが少なくて済みます。
require 'ostruct'
os = OpenStruct.new(:a => 1, :b => 2)
os.a # => 1
os.a = 2 # settable
os.foo # => nil
OpenStruct
を使用すると、存在しないメンバーにアクセスできることに注意してください。nil
が返されます。より厳密なバージョンが必要な場合は、代わりにStruct
を使用してください。
これにより、匿名クラスが作成され、クラスがインスタンス化されます。
h = {:a=>1, :b=>2}
obj = Struct.new(* h.keys).new(* h.values)
obj.a # => 1
obj.a = 2 # settable
obj.foo # NoMethodError
定数が必要ないので
args = {}
args[:a] = 1
args[:b] = 2
args.each_pair{|k,v|eval "@#{k}=#{v};"}
puts @b
2
このアプローチは興味深いと思うかもしれません(適切なコンテキストで変数を評価してください)
fn="b*b"
vars=""
args.each_pair{|k,v|vars+="#{k}=#{v};"}
eval vars + fn
4