Ruby Metaprogramming: ชื่อตัวแปรอินสแตนซ์แบบไดนามิก


97

สมมติว่าฉันมีแฮชต่อไปนี้:

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

ฉันจะตั้งค่าคีย์และค่าแบบไดนามิกให้กลายเป็นตัวแปรอินสแตนซ์ในออบเจ็กต์ได้อย่างไร ...

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

... ดังนั้นฉันจึงจบลงด้วยสิ่งต่อไปนี้ในโมเดล ...

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

เหรอ?

คำตอบ:


172

instance_variable_setวิธีการที่คุณกำลังมองหาอยู่ ดังนั้น:

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

หรือสั้นกว่านั้น

hash.each &method(:instance_variable_set)

หากชื่อตัวแปรอินสแตนซ์ของคุณไม่มี "@" (ตามที่อยู่ในตัวอย่างของ OP) คุณจะต้องเพิ่มเข้าไปจึงจะเป็นดังนี้:

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

18
ไม่ได้ผลสำหรับฉันสำหรับ 1.9.3 ฉันใช้สิ่งนี้แทนhash.each {|k,v| instance_variable_set("@#{k}",v)}
Andrei

3
อีกเหตุผลหนึ่งที่ทำให้รักทับทิม
jschorr

คุณช่วยอธิบายได้hash.each &method(:instance_variable_set)ไหมว่าวิธีการinstance_variable_setรับสองพารามิเตอร์ที่ต้องการได้อย่างไร
Arnold Roa

มีความคิดอย่างไรที่จะทำสิ่งนี้ซ้ำ ๆ (หากมีหลายระดับในแฮชอินพุต)
nemenems

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

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

o.baz
 => "qux" 
o.foo
 => "bar" 

1
มันน่าสนใจทีเดียว ... โซ่ที่สอง.new()กำลังทำอะไรอยู่?
Andrew

3
@ แอนดรูว์: Struct.newสร้างคลาสใหม่โดยใช้แฮชคีย์จากนั้นคลาสที่สองnewจะสร้างอ็อบเจ็กต์แรกของคลาสที่เพิ่งสร้างขึ้นโดยเริ่มต้นด้วยค่าของแฮ ดูruby-doc.org/core-1.8.7/classes/Struct.html
DigitalRoss

2
นี่เป็นวิธีที่ยอดเยี่ยมจริงๆเนื่องจากนี่เป็นสิ่งที่ Struct สร้างขึ้นมาเพื่อ
Chuck

2
หรือการใช้OpenStruct require 'ostruct'; h = {:foo => 'foo'}; o = OpenStruct.new(h); o.foo == 'foo'
Justin Force

ฉันต้องจับคู่กุญแจกับสัญลักษณ์:Struct.new(*hash.keys.map { |str| str.to_sym }).new(*hash.values)
erran

8

คุณทำให้เราอยากร้องไห้ :)

ไม่ว่าในกรณีใดโปรดดูObject#instance_variable_getและObject#instance_variable_setและ

มีความสุขในการเขียนโค้ด


เอ่อใช่ฉันอดสงสัยไม่ได้ว่า ... ทำไม? เมื่อไหร่ที่จะใช้สิ่งนี้?
Zach Smith

ตัวอย่างเช่นฉันอาจต้องการset_entityเรียกกลับทั่วไปสำหรับคอนโทรลเลอร์ทั้งหมดและฉันไม่ต้องการยุ่งเกี่ยวกับตัวแปรอินสแตนซ์ที่มีอยู่def set_entity(name, model); instance_variable_set(name, model.find_by(params[:id])); end;
user1201917

5

คุณยังสามารถใช้sendซึ่งป้องกันไม่ให้ผู้ใช้ตั้งค่าตัวแปรอินสแตนซ์ที่ไม่มีอยู่จริง:

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

ใช้sendเมื่อในชั้นเรียนของคุณมีตัวตั้งattr_accessorค่าเช่นตัวแปรอินสแตนซ์ของคุณ:

class Example
  attr_accessor :foo, :baz
  def initialize(hash)
    hash.each { |key, value| send("#{key}=", value) }
  end
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.