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.