В этом втором ссылочном тексте есть ... жемчужина, которая, я думаю, доходит до сути ответа: предки синглтон-класса.Применительно к вашему объекту это будет:
fido_instance.singleton_class.ancestors
Это всегда даст вам порядок поиска методов, который использует Ruby.Это довольно просто, когда вы смотрите на это таким образом, и это основной ответ на ваш вопрос.Ruby начнет с singleton_class и продолжит свой путь к предкам, ищущим этот метод.Использование вашей диаграммы:
fido.singleton_class.ancestors
=> [Fetch, WagTail, DogClass, Object, Kernel, BasicObject]
(Примечание 1: Bark
не является частью этого вывода, потому что вы использовали extend
вместо include
. Подробнее об этом в секунду.)
(Примечание 2: Если он не находит его полностью вплоть до BasicObject
, то он вызовет method_missing
по той же цепочке предков.)
При вызове метода в классе нет разницыпотому что в Ruby класс это просто экземпляр класса Class
.Так что DogClass.method1
будет искать method1
на DogClass.singleton_class
, а затем подниматься по цепочке предков, как и раньше.
DogClass.singleton_class.ancestors
=> [Bark, Class, Module, Object, Kernel, BasicObject]
Поскольку вы использовали extend
для Bark
, это то, где мы находимЭто!Так что если Bark
определил метод bark
, то вы можете вызвать DogClass.bark
, потому что этот метод определен в предках DogClass
singleton_class '.
Чтобы понять, что это дерево предков (вместополагаться на его распечатку каждый раз), вам просто нужно знать, как наследование изменяется подклассами, extend
, include
, prepend
и т. д.
- Подклассы дают ребенкуКласс всей цепочки предков своего суперкласса.
include
модуль в классе C
добавляет этот модуль в цепочку предков после C
и перед всем остальным. prepend
модуль в классе C
добавляет этот модуль в цепочку предков раньше всего, включая C
и любые в настоящее время предварительно добавленные модули. def x.method1
добавляет method1
к x.singleton_class
.Точно так же x.extend(M)
добавит M
к происхождению x.singleton_class
(но не к x.class
).Обратите внимание, что последнее именно то, что произошло с Bark
и DogClass.singleton_class
, но может в равной степени применяться к любому объекту.
Исключение extend
из приведенного выше списка, поскольку оно не изменяет объектродословная.Он изменяет происхождение singleton_class
этого объекта - как мы видели, Bark
был включен в DogClass.singleton_class.ancestors
.
Tangent:
Немного о методах класса вышеэто ключ для меня, чтобы понять, насколько важны синглтон-классы для Ruby.Очевидно, вы не можете определить bark
для DogClass.class
, потому что DogClass.class == Class
, и мы не хотим bark
для Class
!Итак, как мы можем позволить DogClass
быть экземпляром Class
, позволяя ему иметь (class) метод bark
, который определен для DogClass
, но не относящиеся к нему классы?Используя синглтон-класс!Таким образом, определение «метода класса», например, def self.x
внутри класса C
, похоже на C.singleton_class.send(:define_method, :x) {...}
.