Ruby - метод определения происхождения?
При отправке сообщения объект Ruby ищет, чтобы узнать, имеет ли он метод с этим именем для ответа. Поиск метода ищет в следующем порядке и использует первый найденный метод.
- Методы Singleton, определенные сами по себе (так называемые методы на его "eigenclass" )
- Методы, определенные в его классе
- Любые модули, смешанные с его классом, в обратном порядке включения (только самое раннее включение данного модуля имеет какой-либо эффект - если суперкласс включает в себя модуль A, а подкласс включает его снова, его игнорируют в подклассе; подкласс включает A, затем B, затем A, второй A игнорируется) ( update: обратите внимание, что это было написано до
Module.prepend
)
- Свой родительский класс
- Любые методы, смешанные с родительским классом, родительским родителем и т.д.
Или, проще говоря, он смотрит на себя, затем все в self.class.ancestors
в том порядке, в котором они перечислены.
Этот путь поиска выполняется в момент вызова метода; если вы делаете экземпляр класса, затем снова открываете класс и добавляете метод или смешиваете его через модуль, существующий экземпляр получит доступ к этому методу.
Если все это не удается, он видит, имеет ли он метод method_missing
или его класс, его родительский класс и т.д.
Мой вопрос: помимо изучения кода вручную или использования примерных методов, таких как puts "I'm on module A!"
, можете ли вы указать, откуда пришел данный метод? Можете ли вы, например, перечислить object method и увидеть, что "этот находится в родительском классе, этот находится в модуле A, этот находится в классе и переопределяет родительский класс" и т.д.
Ответы
Ответ 1
Object#method
возвращает объект Method
давая метаданные о данном методе. Например:
> [].method(:length).inspect
=> "#<Method: Array#length>"
> [].method(:max).inspect
=> "#<Method: Array(Enumerable)#max>"
В Ruby 1.8.7 и более поздних версиях вы можете использовать Method#owner
, чтобы определить класс или модуль, которые определили метод.
Чтобы получить список всех методов с именем класса или модуля, где они определены, вы можете сделать что-то вроде следующего:
obj.methods.collect {|m| "#{m} defined by #{obj.method(m).owner}"}
Ответ 2
Получить соответствующий объект Method (или UnboundMethod) и запросить его owner
. Таким образом, вы можете сделать method(:puts).owner
и получить Kernel
.
Ответ 3
Чтобы узнать, какие методы экземпляра определены на A
(но не на суперклассах):
A.methods(false)
Чтобы узнать, какие методы экземпляра определены на A
и его суперклассах:
A.methods
Чтобы узнать, какой класс (или модуль) задан для данного метода:
method(:my_method).owner
Чтобы найти, какой объект является приемником для данного метода:
method(:my_method).receiver
Ответ 4
Вы можете использовать Object#method
. Например,
[1, 2, 3].method(:each_cons) # => #<Method: Array(Enumerable)#each_cons>
указывает, что метод each_cons массива поступает из модуля Enumerable.