Как динамически добавить attr_reader
Я ожидаю, что следующий код будет работать так, как ожидалось, но он дает мне NoMethodError (частный метод `foo ', вызываемый для # < MyClass...)
class MyClass
end
my_object = MyClass.new
my_object.instance_variable_set(:@foo, "bar")
MyClass.send("attr_reader", :foo)
puts my_object.foo
Проблема заключается в том, что я использую буквально идентичный код в более крупном приложении, и он работает точно так, как я ожидаю, но когда я упрощу его на этот самый простой пример, он терпит неудачу.
(Я понимаю, что есть много других способов сделать то, что я делаю в Ruby)
Ответы
Ответ 1
Используйте 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"
Ответ 2
Интересная проблема, я нашел, что это решение работает нормально:
MyClass.class_eval("attr_reader :foo")
Ответ 3
Ответ от @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
Ответ 4
Предположим на мгновение, что вы хотите создать accessor для этого конкретного экземпляра класса (который, как я предполагаю, имеет место, поскольку вы работаете с экземпляром.
Вы можете просто открыть экземпляр singleton class и использовать instance_eval для добавления accessor
class MyClass
end
my_instance = MyClass.new
my_instance.singleton_class.instance_eval { attr_accessor :foo }
my_instance.foo = :bar
my_instance.foo # => :bar
Ответ 5
Просто запустите метод, чтобы он стал общедоступным:
MyClass.send("public", :foo)
Я не знаю, почему это частное в некоторых случаях.