Ruby元编程:动态实例变量名称


94

假设我有以下哈希值:

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

我如何动态设置键和值以成为对象中的实例变量...

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

...这样我最终得到了模型中的以下内容...

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

Answers:


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)}
Andrei 2012年

3
喜欢Ruby的另一个原因
jschorr

您能否解释一下中hash.each &method(:instance_variable_set)的方法如何instance_variable_set接收它需要的两个参数?
阿诺德·罗阿

知道如何递归执行此操作吗?(如果输入杂凑中有多个层级)
nemenems'2

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个创建刚创建的类的第一个对象,并将其初始化为Hash的值。参见ruby-doc.org/core-1.8.7/classes/Struct.html
DigitalRoss 2011年

2
实际上,这是一种非常好的方法,因为这几乎就是Struct的用途。
查克(Chuck)

2
或使用OpenStructrequire '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_getObject#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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.