2011-03-31 47 views
11

我希望如期下面的代码工作,但它给了我(呼吁#< MyClass的...私有方法'富”)如何动态地添加attr_reader

class MyClass 
end 

my_object = MyClass.new 

my_object.instance_variable_set(:@foo, "bar") 
MyClass.send("attr_reader", :foo) 

puts my_object.foo 

的问题是NoMethodError我在一个更大的应用程序中使用完全相同的代码,它的工作原理与我所期望的完全相同,但是当我将其简化为这个最基本的示例时,它就会失败。

(我的理解有许多其他方法可以做到什么,我在Ruby中正在做)

+1

的例子在IRB的作品,但不是红宝石... – 2011-03-31 22:44:12

+0

@jleedev - 我发现同样的事情(无论是在红宝石1.8.6和Ruby 1.8.7企业)。 irb很棒,但是如果你从一个文件运行完全相同的命令......你认为这是Ruby实现中的一个错误(REE主要是redid垃圾收集器)? – 2011-03-31 22:59:34

回答

0

就强制法成为公众:

MyClass.send("public", :foo) 

我不知道为什么它是私人的一些案例。

+0

这假设'foo'方法已经被定义,对于一些实例变量可能不是这种情况。 – Phrogz 2011-03-31 23:03:13

+0

这是迄今为止最好的答案,但我仍然好奇为什么它在某些情况下是私人的,而不是其他情况。 – Leroy22 2011-03-31 23:48:06

3

有趣的问题,我发现这个解决方案正常工作:

MyClass.class_eval("attr_reader :foo") 
+0

我建议使用块形式(per @MikeLewis'answer)而不是字符串形式,这样就不需要运行时解析/解析。 – Phrogz 2011-03-31 23:02:22

+0

是的,你是对的,我刚发现这个工作解决方案很快就没有寻找更好的方法。 – 2011-04-01 09:44:00

5

使用Module#class_eval

通过这样做,块把你的MyClass上下文中,从而使您可以拨打attr_reader

ruby-1.9.2-p136 :028 > my_object.instance_variable_set(:@foo, "bar") 
=> "bar" 
ruby-1.9.2-p136 :029 > MyClass.class_eval{attr_reader :foo} 
=> nil 
ruby-1.9.2-p136 :030 > my_object.foo 
=> "bar" 
+0

我想知道是否能够为一个特定的类实例做到这一点。 – 2014-08-14 13:59:53

1

@MikeLewis的答案很好,但为什么不重新开放课程呢?

irb(main):001:0> class MyClass; end 
#=> nil 
irb(main):002:0> m = MyClass.new 
#=> #<MyClass:0x2d43ae0> 
irb(main):003:0> class MyClass; attr_accessor :foo; end 
#=> nil 
irb(main):004:0> m.foo = 42 
#=> 42 
+0

如果这是一个宝石,你正在调整:'Module ModuleName; end' 原因: 类型错误:模块名是不是一类 \t从(IRB):15 \t从/ usr/bin中/ IRB:12:'

“' – Crisfole 2016-09-16 19:39:27

+0

@Crisfole也许是因为它是一个'module'而不是一个班级。 – Phrogz 2016-09-17 01:51:33

+0

我必须有宝石混合。我以为我试图重新打开这个 - > https://github.com/dcparker/ruby-gmail/blob/master/lib/gmail.rb#L3-L6。也许我忘了运行捆绑软件.... – Crisfole 2016-09-17 13:17:42

1

假设为您要创建的类(我假设的该特定实例的访问的瞬间是如此,因为你是在实例运行。

你可以简单地打开实例的单例类,并使用instance_eval的添加访问

class MyClass 
end 
my_instance = MyClass.new 
my_instance.singleton_class.instance_eval { attr_accessor :foo } 
my_instance.foo = :bar 
my_instance.foo # => :bar