是否有内置的Ruby方法或知名库返回对象的整个方法查找链? Ruby查看一个令人困惑的类序列(如this question中所讨论的),用于与消息对应的实例方法,如果没有类响应消息,则在接收器上调用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]
答案 0 :(得分:15)
其他帖子让它看起来令人困惑,但事实并非如此。如果你对这些事情感兴趣,你应该阅读“Metaprogramming Ruby”。在此之前,基本规则是one step to the right and up
:
Object (superclass)
^
|
Parent class A(superclass)
^
|
Parent class B(superclass)
^
|
obj -> object's class
2)在obj和对象的类之间插入单例类:
Object
^
|
Parent class A(superclass)
^
|
Parent class B(superclass)
^
|
object's class(superclass)
^
|
obj -> obj's singleton_class
3)包含的模块直接插入到包含以下内容的类的上方:
Object
^
|
Parent class A
^
|
Module included by Parent Class B
^
|
Parent class B
^
|
object's class
^
|
obj -> obj's singleton_class
编辑:
请指出任何缺陷
p method_lookup_chain(Class)
--output:--
[#<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:BasicObject>]
但是...
class Object
def greet
puts "Hi from an Object instance method"
end
end
Class.greet
--output:--
Hi from an Object instance method
和..
class Class
def greet
puts "Hi from a Class instance method"
end
end
Class.greet
--output:--
Hi from a Class instance method
在类上调用的方法的查找路径实际上继续超过BasicObject的单例类(#<Class:BasicObject>
):
class BasicObject
class <<self
puts superclass
end
end
--output:--
Class
在Class上调用的方法的完整查找路径如下所示:
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
查找从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>]
应该是这样的:
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
您需要记住的一件事是:在类上调用的任何方法的查找路径都必须包含Class,因为所有类都继承自Class。