Ruby:调用动态定义方法时的上下文切换

时间:2010-02-13 16:19:52

标签: ruby metaprogramming

这是一些解释我遇到的问题的测试代码。 Child类调用Parent类的方法。 Parent的一个方法在Parent上定义了一个名为foo的新方法。在定义foo之后,尝试从Child类调用它,但上下文完全不同(我只能访问Child的实例变量,而不是Parent的)。

我在这里猜测这与Ruby的闭包有关。当我拨打define_method时,我应该使用除了块之外的东西吗? 编辑:我尝试使用lambda和proc,但它没有改变任何内容。

class Parent
  @foo = 'foo'

  def self.existing_method
    puts "Calling existing_method, @foo is #{@foo}"
  end

  def self.define_new_method
    self.class.send :define_method, :foo do
      context = methods.include?('bar') ? 'child' : 'parent'

      puts "Context is #{context}, @foo is #{@foo.inspect}"
    end
  end
end

class Child
  @foo = 'childfoo'

  def self.method_missing(*args, &block)
    Parent.send args.shift, *args, &block
  end

  def self.bar
  end
end

Child.existing_method    # Calling existing_method, @foo is foo
Child.define_new_method
Child.foo                # Context is child, @foo is "childfoo"
                         # (in Ruby 1.9, the context is parent, but
                         # @foo is still "childfoo")

Parent.foo               # Context is parent, @foo is "foo"

这个结果不是我想要的。 Child.foo的回复应与Parent.foo的回复相同。

提前致谢!

3 个答案:

答案 0 :(得分:0)

经过大量的挖掘,这就是我想到的。

  1. 调用self.class.define_method实际上定义了Object实例方法。这意味着所有收到了新方法,这是明显的上下文转换的原因(Child.foo实际上是在调用Child.foo而不是Parent.foo)。糟糕。
  2. 为了定义一个类方法,你必须抓住实际的类对象,由于某种原因,它不是self,它是class << self; self; end(是的,我没有得到它其一)。这里简要提到了代码:http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html
  3. Ruby有时让我的大脑受伤。此代码应返回预期结果。

    class Parent
      @foo = 'foo'
    
      def self.existing_method
        puts "Calling existing_method, @foo is #{@foo}"
      end
    
      def self.define_new_method
        inst = class << self; self; end # Do not understand this...
        inst.send :define_method, :foo do
          context = methods.include?(:bar) ? 'child' : 'parent'
          puts "Context is #{context}, @foo is #{@foo.inspect}"
        end
      end
    end
    
    class Child
      @foo = 'childfoo'
    
      def self.method_missing(*args, &block)
        return unless args.length > 0
        Parent.send args.shift, *args, &block
      end
    
      def self.bar
      end
    end
    
    Child.existing_method     # Calling existing_method, @foo is foo
    Child.define_new_method
    Child.foo                 # Context is parent, @foo is "foo"
    Parent.foo                # Context is parent, @foo is "foo"
    

答案 1 :(得分:0)

这很有趣;我认为Yehuda Katz今天的帖子解释了完全你想要什么:http://yehudakatz.com/2010/02/15/abstractqueryfactoryfactories-and-alias_method_chain-the-ruby-way/


编辑:好的 - 这不是“你要求的东西”,第二个想法,因为它不是元编程,但它最有可能更接近你想做的事情。至于你的具体答案,我会看一下代码。

答案 2 :(得分:0)

在Ruby 1.9x中,

&#39;方法&#39;方法返回Symbol(method name) Array

因此使用respond_to方法:

context = respond_to?(:bar) ? 'child' : 'parent'