変数がvar = "some_name"
あり、新しいオブジェクトを作成して に割り当てたいと考えていsome_name
ます。どうすればいいですか?例えば
var = "some_name"
some_name = Struct.new(:name) # I need this
a = some_name.new('blah') # so that I can do this.
変数がvar = "some_name"
あり、新しいオブジェクトを作成して に割り当てたいと考えていsome_name
ます。どうすればいいですか?例えば
var = "some_name"
some_name = Struct.new(:name) # I need this
a = some_name.new('blah') # so that I can do this.
Ruby 1.9+ では動的にローカル変数を作成することはできません (Ruby 1.8 では 経由で作成できましたeval
):
eval 'foo = "bar"'
foo # NameError: undefined local variable or method `foo' for main:Object
ただし、これらは eval-ed コード自体の中で使用できます。
eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"
Ruby 2.1 が追加されましlocal_variable_set
たが、新しいローカル変数を作成することもできません:
binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object
この動作は、 Ruby 自体を変更しない限り変更できません。別の方法として、多くのローカル変数ではなく、ハッシュなどの別のデータ構造内にデータを格納することを検討することもできます。
hash = {}
hash[:my_var] = :foo
eval
と の両方で、既存のローカル変数を再割り当てできることに注意してくださいlocal_variable_set
。
foo = nil
eval 'foo = "bar"'
foo #=> "bar"
binding.local_variable_set :foo, 'baz'
foo #=> "baz"
ruby 2.2.x といえば、現在のコンテキスト/バインディングでローカル変数をプログラムで作成できないのは事実ですが、ハンドルを持っている特定のバインディングで変数を設定することはできます。
b = binding
b.local_variable_set :gaga, 5
b.eval "gaga"
=> 5
ここで興味深いのは、呼び出しがbinding
毎回新しいバインディングを提供することです。したがって、関心のあるバインディングのハンドルを取得し、必要な変数が設定されたら、そのコンテキストで評価する必要があります。
これはどのように役立ちますか?<%= myvar %>
たとえば、ERB を評価したいのですが、ERBの代わりに<%= opts[:myvar] %>
またはそのようなものを使用できれば、ERB を書く方がはるかに優れています。
新しいバインディングを作成するために、モジュールクラスメソッドを使用しています (誰かがこれを適切に呼び出す方法を修正してくれると確信しています。Java では静的メソッドと呼びます)。特定の変数が設定されたクリーンなバインディングを取得します。
module M
def self.clean_binding
binding
end
def self.binding_from_hash(**vars)
b = self.clean_binding
vars.each do |k, v|
b.local_variable_set k.to_sym, v
end
return b
end
end
my_nice_binding = M.binding_from_hash(a: 5, **other_opts)
これで、目的の変数のみを含むバインディングが作成されました。ERB やその他の (サード パーティの可能性がある)信頼できるコード (これはいかなる種類のサンドボックスでもありません) のより適切に管理された評価に使用できます。インターフェイスを定義するようなものです。
更新: バインディングに関するいくつかの追加メモ。それらを作成する場所も、メソッドの可用性と定数の解決に影響します。上記の例では、適度にクリーンなバインディングを作成しています。しかし、あるオブジェクトのインスタンス メソッドを利用できるようにしたい場合は、同様のメソッドを使用してバインドを作成できますが、そのオブジェクトのクラス内に作成できます。例えば
module MyRooTModule
class Work
def my_instance_method
...
end
def not_so_clean_binding
binding
end
end
class SomeOtherClass
end
end
これで、コードがオブジェクトmy_object.not_so_clean_binding
を呼び出すことができるようになります。同様に、たとえばコード内で の代わりにこのバインディングを使用して呼び出すことができます。そのため、バインディングを作成する際には、ローカル変数だけでなく、より多くの考慮事項が必要になる場合があります。HTH#my_instance_method
my_object
SomeOtherClass.new
MyRootModule::SomeOtherClass.new
他の人が指摘しているように、Ruby ではローカル変数を動的に作成することはできませんが、メソッドを使用してこの動作をある程度シミュレートできます。
hash_of_variables = {var1: "Value 1", var2: "Value 2"}
hash_of_variables.each do |var, val|
define_method(var) do
instance_variable_get("@__#{var}")
end
instance_variable_set("@__#{var}", val)
end
puts var1
puts var2
var1 = var2.upcase
puts var1
版画:
Value 1
Value 2
VALUE 2
一部のライブラリは、この手法を組み合わせてinstance_exec
、ブロック内のローカル変数のように見えるものを公開します。
def with_vars(vars_hash, &block)
scope = Object.new
vars_hash.each do |var, val|
scope.send(:define_singleton_method, var) do
scope.instance_variable_get("@__#{var}")
end
scope.instance_variable_set("@__#{var}", val)
end
scope.instance_exec(&block)
end
with_vars(a: 1, b:2) do
puts a + b
end
プリント: 3
ただし、抽象化は決して完璧ではないことに注意してください。
with_vars(a: 1, b:2) do
a = a + 1
puts a
end
結果: undefined method `+' for nil:NilClass
。これは、メソッドよりも優先される、a=
に初期化された実際のローカル変数を定義するためです。次に呼び出され、メソッドがないため、エラーがスローされます。nil
a
a.+(1)
nil
+
したがって、この方法は読み取り専用のローカル変数をシミュレートするのに非常に便利ですが、ブロック内で変数を再割り当てしようとすると、常にうまく機能するとは限りません。