Ruby 一个元编程的代码跟踪, 求大神指教其中的调用顺序, 有点摸不着头绪了

wukong · 2013年04月10日 · 最后由 fsword 回复于 2013年04月10日 · 2845 次阅读
module TraceCalls
    def self.included(klass)
        klass.instance_methods(false).each do |existing_method|
            wrap(klass, existing_method)
        end
        def klass.method_added(method)
            puts "@@@"
            puts method
            puts @trace_calls_internal
            puts "$$$$"
            unless @trace_calls_internal
                @trace_calls_internal = true
                TraceCalls.wrap(self, method)
                @trace_calls_internal = false
            end
        end
    end
    def self.wrap(klass, method)
        puts "###{}"
        puts klass
        puts method
        puts "*****"
        klass.instance_eval do
            method_object = instance_method(method)
            define_method(method) do |*args, &block|
                puts "==> calling #{method} with #{args.inspect}"
                result = method_object.bind(self).call(*args, &block)
                puts "<== #{method} returned #{result.inspect}"
                result
            end
        end
    end
end
class Example
    def one(arg)
        puts "One called with #{arg}"
    end
end
ex1 = Example.new
ex1.one("Hello")
class Example
    include TraceCalls
    def two(arg1, arg2)
        arg1 + arg2
    end
end
ex1.one("GoodBye")
puts ex1.two(4, 5)

上面的代码跟踪有点绕,不是很清楚,希望大神们帮解析一下这其中的调用,还有像这种 ruby 元编程的东西有没有什么好方法去学习?

先看完《Ruby 元编程》这本书吧。

具体哪一行不懂?比较复杂,别人也没那么空闲吧。

这个代码太混乱了 要光凭借看就理解确实不容易 至于学习其实不难 无非就是了解几个方法是干什么用得,本质上和 UNIX 编程的学习是一样的。书么就是《Ruby metaprogramming》,最权威了。

@Rei @iBachue 嗯,好的。我先看这本书吧,

@chenge wrap(klass, existing_method) 与 TraceCalls.wrap(self, method) 这两处调用 为什么会不一样,都在同一个 module 里面啊

#5 楼 @wukong 我的功力不够,看不太懂啊。悟空,找师傅吧。

@chenge 没事儿,谢谢 chenge 同学,那我先找资料看看。

打印结果

1:
One called with Hello 
2:
##
Example
one
*****
3.1:
@@@
two

$$$$
3.2:
##
Example
two
*****
3.3:
@@@
two
true
$$$$
4:
==> calling one with ["GoodBye"]
One called with GoodBye
<== one returned nil
5:
==> calling two with [4, 5]
<== two returned 9
9

分析

1.
ex1.one("Hello")  => # 打印 1
2.
include TraceCalls =>
self.included(klass) => 
wrap(klass, existing_method) => 
def self.wrap(klass, method)
  # 打印 2
  puts "###{}"
  puts klass
  puts method
  puts "*****"
  # 结束打印 2
  klass.instance_eval do
     method_object = instance_method(method)
     define_method(method) do |*args, &block|
        puts "==> calling #{method} with #{args.inspect}"
        result = method_object.bind(self).call(*args, &block)
        puts "<== #{method} returned #{result.inspect}"
        result
     end
  end
end

3.
def two(arg1, arg2)
   arg1 + arg2
end
=>
def klass.method_added(method)
    # 打印 3.1
    puts "@@@"
    puts method
    puts @trace_calls_internal
    puts "$$$$"
    # 结束打印 3.1
    unless @trace_calls_internal
       @trace_calls_internal = true
       TraceCalls.wrap(self, method) =>(
       def self.wrap(klass, method)
          # 打印 3.2
          puts "###{}"
          puts klass
          puts method
          puts "*****"
          # 结束打印 3.2
          klass.instance_eval do
             method_object = instance_method(method)
             define_method(method) do |*args, &block|
                puts "==> calling #{method} with #{args.inspect}"
                result = method_object.bind(self).call(*args, &block) 
                puts "<== #{method} returned #{result.inspect}"
                result
            end =>(
            def klass.method_added(method)
              # 打印 3.3
              puts "@@@"
              puts method
              puts @trace_calls_internal
              puts "$$$$"
              # 结束打印 3.3
              unless @trace_calls_internal
                @trace_calls_internal = true
                TraceCalls.wrap(self, method)
                @trace_calls_internal = false
            end
          end
          )
        end
      end
       ) 
       @trace_calls_internal = false
    end
    # 结束打印 3
end

4. 
ex1.one("GoodBye") => 

define_method(method) do |*args, &block|
    #  打印 4
    puts "==> calling #{method} with #{args.inspect}"
    result = method_object.bind(self).call(*args, &block)
    puts "<== #{method} returned #{result.inspect}"
    # 结束打印4
    result
end

5. puts ex1.two(4, 5) =>

define_method(method) do |*args, &block|
   # 打印 5
   puts "==> calling #{method} with #{args.inspect}"
   result = method_object.bind(self).call(*args, &block)
   puts "<== #{method} returned #{result.inspect}"
   # 结束 打印 5
   esult
end

参考资料

  1. Module.included http://www.ruby-doc.org/core-2.0/Module.html#method-i-included
  2. Module.method_added http://www.ruby-doc.org/core-2.0/Module.html#method-i-method_added
  3. Module.instance_methods http://www.ruby-doc.org/core-2.0/Module.html#method-i-instance_methods
  4. BaseObject.instance_eval http://ruby-doc.org/core-2.0/BasicObject.html#method-i-instance_eval
  5. BaseObject.define_method http://www.ruby-doc.org/core-2.0/BasicObject.html#method-i-instance_eval

好累,感觉不会再爱了。

有点线索,method_added 是 callback,后一个是在 callback 里,要加类名。

简单说一下,具体的自己看书

module TraceCalls
  # 回调的钩子
  # 在下文include这个module后,klass即为Example类

  def self.included(klass)
    klass.instance_methods(false).each do |existing_method|
      wrap(klass, existing_method)  # 对one这个实例方法进行wrap操作
    end

    # 由于include在two方法前,这里对two方法进行了wrap操作
    def klass.method_added(method)
      unless @trace_calls_internal
        @trace_calls_internal = true
        TraceCalls.wrap(self, method)
        @trace_calls_internal = false
      end
    end
  end

  def self.wrap(klass, method)
    klass.instance_eval do
      method_object = instance_method(method)
      # 这里重新定义了实例方法
      define_method(method) do |*args, &block|
        puts "==> calling #{method} with #{args.inspect}"
        result = method_object.bind(self).call(*args, &block)
        puts "<== #{method} returned #{result.inspect}"
        result
      end
    end
  end
end

@zgm 谢谢,呵呵,来个更复杂的你应该又能再爱了!

@chenge @neverlandxy_naix 谢谢两位,知道了

#5 楼 @wukong 因为这句话

def klass.method_added(method)

已经改变了 self 的含义,所以后面就要显示指定 wrap 方法的调用对象

需要 登录 后方可回复, 如果你还没有账号请 注册新账号