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'

?

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) }

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" 

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.


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.