Ruby Metaprogramming: nama variabel instance dinamis

94

Katakanlah saya memiliki hash berikut:

{ :foo => 'bar', :baz => 'qux' }

Bagaimana saya bisa secara dinamis mengatur kunci dan nilai untuk menjadi variabel instan dalam suatu objek ...

class Example
  def initialize( hash )
    ... magic happens here...
  end
end

... sehingga saya berakhir dengan yang berikut di dalam model ...

@foo = 'bar'
@baz = 'qux'

?

Andrew
sumber

Jawaban:

168

Metode yang Anda cari adalah instance_variable_set. Begitu:

hash.each { |name, value| instance_variable_set(name, value) }

Atau, lebih singkatnya,

hash.each &method(:instance_variable_set)

Jika nama variabel instance Anda tidak memiliki "@" (seperti di contoh OP), Anda harus menambahkannya, jadi akan menjadi seperti ini:

hash.each { |name, value| instance_variable_set("@#{name}", value) }
Membuang
sumber
18
Tidak berhasil untuk saya selama 1.9.3. Saya menggunakan ini sebagai gantinyahash.each {|k,v| instance_variable_set("@#{k}",v)}
Andrei
3
alasan lain untuk mencintai Ruby
jschorr
dapatkah Anda menjelaskan caranya hash.each &method(:instance_variable_set), metode instance_variable_setmenerima dua parameter yang dibutuhkan?
Arnold Roa
tahu bagaimana melakukan ini secara rekursif? (jika ada beberapa level dalam hash input)
nemenems
13
h = { :foo => 'bar', :baz => 'qux' }

o = Struct.new(*h.keys).new(*h.values)

o.baz
 => "qux" 
o.foo
 => "bar" 
DigitalRoss
sumber
1
Itu cukup menarik ... apa sebenarnya yang .new()dilakukan rantai kedua ?
Andrew
3
@Andrew: Struct.newmembuat kelas baru berdasarkan kunci hash, lalu yang kedua newmembuat objek pertama dari kelas yang baru saja dibuat, menginisialisasinya ke nilai-nilai Hash. Lihat ruby-doc.org/core-1.8.7/classes/Struct.html
DigitalRoss
2
Ini sebenarnya adalah cara yang sangat bagus untuk melakukannya karena untuk itulah Struct dibuat.
Chuck
2
Atau gunakan OpenStruct . require 'ostruct'; h = {:foo => 'foo'}; o = OpenStruct.new(h); o.foo == 'foo'
Justin Force
Saya harus memetakan kunci saya ke simbol:Struct.new(*hash.keys.map { |str| str.to_sym }).new(*hash.values)
erran
7

Anda membuat kami ingin menangis :)

Dalam kasus apapun, lihat Object#instance_variable_getdanObject#instance_variable_set .

Selamat membuat kode.


sumber
eh ya, saya tidak bisa tidak bertanya-tanya ... kenapa? kapan waktu yang tepat untuk menggunakan ini?
Zach Smith
misalnya, saya mungkin ingin memiliki set_entitypanggilan balik umum untuk semua pengontrol, dan saya tidak ingin mengganggu variabel instan yang adadef set_entity(name, model); instance_variable_set(name, model.find_by(params[:id])); end;
user1201917
5

Anda juga dapat menggunakan sendyang mencegah pengguna menyetel variabel contoh yang tidak ada:

def initialize(hash)
  hash.each { |key, value| send("#{key}=", value) }
end

Gunakan sendjika di kelas Anda ada penyetel seperti attr_accessoruntuk variabel instan Anda:

class Example
  attr_accessor :foo, :baz
  def initialize(hash)
    hash.each { |key, value| send("#{key}=", value) }
  end
end
Asarluhi
sumber