Ruby Metaprogramming: імена змінних динамічних екземплярів


94

Скажімо, у мене є такий хеш:

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

Як я міг динамічно встановити ключі та значення, щоб стати змінними екземпляра в об'єкті ...

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

... щоб я закінчив наступне всередині моделі ...

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

?

Відповіді:


168

Ви шукаєте метод 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)}
Андрій

3
ще одна причина любити Рубі
jschorr

Ви можете пояснити, як hash.each &method(:instance_variable_set)метод instance_variable_setотримує два необхідні йому параметри?
Арнольд Роа

будь-яка ідея, як це зробити рекурсивно? (якщо у вхідному хеші є кілька рівнів)
ненеми

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

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

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

1
Це досить цікаво ... що саме робить другий ланцюжок .new()?
Андрій

3
@Andrew: Struct.newстворює новий клас на основі хеш-ключів, а потім другий newробить перший об'єкт щойно створеного класу, ініціалізуючи його до значень Хешу. Дивіться ruby-doc.org/core-1.8.7/classes/Struct.html
DigitalRoss

2
Це насправді дуже чудовий спосіб зробити це, оскільки це майже все, для чого створена Struct.
Чак,

2
Або скористайтеся OpenStruct . require 'ostruct'; h = {:foo => 'foo'}; o = OpenStruct.new(h); o.foo == 'foo'
Джастін Форс

Мені довелося скласти свої ключі до символів:Struct.new(*hash.keys.map { |str| str.to_sym }).new(*hash.values)
erran

7

Ти змушуєш нас плакати :)

У будь-якому випадку, см Object#instance_variable_getі Object#instance_variable_set.

Щасливе кодування.


е, так, я не міг не задатися питанням ... чому? коли б вдало скористатися цим?
Зак Сміт

Наприклад, я, можливо, захочу отримати загальний 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.