Многое из этого объяснения базируется на How Ruby Method Dispatch Works Джеймсом Коглану, немного из Ruby Hacking Guide и просто smidge из source.
Начнет с кратким, родословная выглядит следующим образом:
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>
---> Parent
~~~> Singleton class
Давайте начнем с самого начала и пристроить. BasicObject
- это корень всего - если вы проверите BasicObject.superclass
, вы получите nil
. BasicObject
также является экземпляром Class
. Да, это циркуляр, и есть special case in the code, чтобы справиться с этим.Когда A
является экземпляром B
, A.singleton_class
является потомком B
, таким образом мы получаем это:
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>
Object
наследует от BasicObject
. Когда A
наследуется от B
, A
является дочерним по отношению к B
и A.singleton_class
является дочерним по отношению к B.singleton_class
. Object
также включает Kernel
. Когда A
включает B
, B
вставлен в качестве первого предка A
(после A
сам, но до A.superclass
).
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>
Kernel
является экземпляром Module
. Это единственный пример Module
, который мы увидим, и его одноэлементный класс не появляется в каких-либо цепочках родословной, поэтому я не буду выходить за его пределы.
Теперь мы переходим к Foo
, который наследуется от Object
(хотя вам не нужно писать < Object
). Мы уже можем выяснить, что такое Foo
, а его одноэлементный класс - дети.
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>
Теперь Class
наследует от Module
и Module
унаследован от Object
, поэтому добавьте Module
и соответствующие классы одноплодной. Потому что Module < Object
и Object < BasicObject
и BasicObject.instance_of?(Class)
, вот где рисунок немного напуган. Помните, что вы просто прекращаете движение вверх, когда вы нажимаете BasicObject
.
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ |
| | |
| BasicObject ~~~~~> #<Class:BasicObject> |
| ^ ^ |
| | Kernel | |
| | ^ | |
| | | | +----------------------------------------+
| +-----+----+ | |
| | | v
+-------> Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>
Последний шаг. Каждый экземпляр Class
имеет singleton_class
(хотя он не будет создан до тех пор, пока он не понадобится, или вам понадобится больше ОЗУ). Все наши одноэлементные классы являются экземплярами Class
, поэтому у них есть классы одноэлементного типа. Следите за этим предложением: родительский класс одного класса класса - это однопользовательский класс родительского класса. Я не знаю, есть ли краткий способ заявить, что в отношении систем типа, и Ruby source в значительной степени говорит, что он просто делает это для обеспечения согласованности в любом случае. Так что, когда вы просите Foo.singleton_class.singleton_class
язык счастливо обязывает вас и распространяет необходимые родителям вверх, что приводит в конце концов к:
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>
Если исходить из любого узла в этой графе и траверсы в глубину, справа налево (и остановитесь на BasicObject
, вы получите цепочку предков узла, как мы и хотели. И мы построили его из основных аксиом, чтобы мы могли просто доверять ему. Недостаток доверия, есть пара интересных способов далее проверьте структуру.
Попробуйте найти node.singleton_class.ancestors - node.ancestors
для любого узла на графике. Это дает нам предки одноэлементного класса, которые не являются предками самого узла, что устраняет некоторую запутанную избыточность в списке.
> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors
=> [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>,
#<Class:Class>, #<Class:Module>]
Вы также можете проверить любого родителя с помощью node.superclass
.
> Foo.singleton_class.singleton_class.superclass
=> #<Class:#<Class:Object>>
И вы можете даже проверить, что идентификатор объекта все соответствует, так что не анонимные классы выскакивают повсюду без особых отношений друг с другом.
> def ancestor_ids(ancestors)
> ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")}
> end
> puts ancestor_ids(Foo.ancestors)
70165241815140 Foo
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject
> puts ancestor_ids(Foo.singleton_class.ancestors)
70165241815120 #<Class:Foo>
70165216039400 #<Class:Object>
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object # Same as Foo from here down
70165216040340 Kernel
70165216040540 BasicObject
> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors)
70165241980080 #<Class:#<Class:Foo>>
70165215986060 #<Class:#<Class:Object>>
70165215986040 #<Class:#<Class:BasicObject>>
70165216039440 #<Class:Class>
70165216039420 #<Class:Module>
70165216039400 #<Class:Object> # Same as Foo.singleton_class from here down
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject
И что, в двух словах, как вы snipe a nerd.
Вы предполагаете, что это организовано вообще. Вы уверены, что это/должно быть организовано? –
Возможно, вы можете уточнить свой вопрос? Вы спрашиваете, где определен метод бара во втором примере? – saghaulor
Определяется метод бара. Я спрашиваю, как организована иерархия наследования во втором примере. –