对象的Ruby方法查找路径

Pow*_*ers 5 ruby

是否有内置的Ruby方法或知名的库来返回对象的整个方法查找链?Ruby查看一个令人困惑的类序列(如本问题中所讨论的),例如对应于消息的方法,method_missing如果没有类响应消息,则调用接收器.

我把以下代码放在一起,但我确定它缺少某些情况,或者它是100%正确的.请指出任何缺陷,并指导我一些更好的代码,如果它存在.

def method_lookup_chain(obj, result = [obj.singleton_class])
  if obj.instance_of? Class
    return add_modules(result) if result.last == BasicObject.singleton_class
    r = result.last.superclass
    method_lookup_chain(obj, result << r)
  else
    return result + obj.class.ancestors
  end
end

def add_modules(klasses)
  r = []
  klasses.each_with_index do |k, i|
    r << k
    next if k == klasses.last
    r << (k.included_modules - klasses[i+1].included_modules)
  end
  r.flatten
end

# EXAMPLES

module ClassMethods; end
module MoreClassMethods; end
class A
  extend ClassMethods
  extend MoreClassMethods
end
p method_lookup_chain(A) # => [#<Class:A>, MoreClassMethods, ClassMethods, #<Class:Object>, #<Class:BasicObject>]

module InstanceMethods; end
class Object
  include InstanceMethods
end
module X; end
module Y; end
class Dog
  include X
  include Y
end
d = Dog.new
p method_lookup_chain(d) # => [#<Class:#<Dog:0x007fcf7d80dd20>>, Dog, Y, X, Object, InstanceMethods, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

7st*_*tud 16

那篇文章让它看起来令人困惑,但事实并非如此.如果你对这些事情感兴趣,你应该阅读"Metaprogramming Ruby".在此之前,基本规则是one step to the right and up:

          Object (superclass)
              ^
              |
          Parent class A(superclass)
              ^
              |
          Parent class B(superclass)
              ^
              |
obj  ->   object's class
Run Code Online (Sandbox Code Playgroud)

2)在obj和对象的类之间插入Singleton类:

          Object
              ^
              |
          Parent class A(superclass)
              ^
              |
          Parent class B(superclass)
              ^
              |
          object's class(superclass)
              ^
              |
obj  ->   obj's singleton_class
Run Code Online (Sandbox Code Playgroud)

3)包含的模块直接插入到包含以下内容的类的上方:

          Object
              ^
              |
          Parent class A
              ^
              |
              Module included by Parent Class B
              ^
              |
          Parent class B
              ^
              |
          object's class
              ^
              |
obj  ->   obj's singleton_class
Run Code Online (Sandbox Code Playgroud)

编辑:

请指出任何缺陷

p method_lookup_chain(Class)

--output:--
[#<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>]
Run Code Online (Sandbox Code Playgroud)

但...

class Object
  def greet
    puts "Hi from an Object instance method"
  end
end

Class.greet

--output:--
Hi from an Object instance method
Run Code Online (Sandbox Code Playgroud)

和..

class Class
  def greet
    puts "Hi from a Class instance method"
  end
end

Class.greet

--output:--
Hi from a Class instance method
Run Code Online (Sandbox Code Playgroud)

在类上调用的方法的查找路径实际上继续超过BasicObject的单例类(#<Class:BasicObject>):

class BasicObject
  class <<self
    puts superclass
  end
end

--output:--
Class
Run Code Online (Sandbox Code Playgroud)

类上调用的方法的完整查找路径如下所示:

                  Basic Object                 
                      ^
                      |
                    Object
                      ^
                      |
                    Module
                      ^
                      |
                    Class
                      ^
                      |
BasicObject    BasicObject's singleton class                
  |                   ^
  |                   |
Object         Object's singleton class
  |                   ^
  |                   |
Module         Module's singleton class
  |                   ^
  |                   |
Class  --->    Class's singleton class
Run Code Online (Sandbox Code Playgroud)

查找从Class的单​​例类开始,然后在右侧的层次结构中向上."Metaprogramming Ruby"声称所有对象都有一个统一的查找理论,但是对类调用的方法的查找不符合3)中的图.

你有同样的问题:

class A 
end

class B < A
end

p method_lookup_chain(B)

--output:--
[#<Class:B>, #<Class:A>, #<Class:Object>, #<Class:BasicObject>]
Run Code Online (Sandbox Code Playgroud)

它应该是这样的:

                  Basic Object                 
                      ^
                      |
                    Object
                      ^
                      |
                    Module
                      ^
                      |
                    Class
                      ^
                      |
BasicObject    BasicObject's singleton class
  |                   ^
  |                   |
Object         Object's singleton class
  |                   ^
  |                   |
  A            A's singleton class
  |                   ^
  |                   |
  B.greet -->  B's singleton class
Run Code Online (Sandbox Code Playgroud)

您需要记住的一件事是:在类上调用的任何方法的查找路径都必须包含Class,因为所有类都继承自Class.