Рубиновые доработки получили

В метапрограммировании Ruby 2 в главе 2 раздела "Уточнения" я обнаружил следующий фрагмент кода Ruby:

class MyClass 
  def my_method
    "original my_method()"
  end

  def another_method 
    my_method
  end

end

module MyClassRefinement 
  refine MyClass do
    def my_method
      "refined my_method()"
    end 
  end
end

using MyClassRefinement
MyClass.new.my_method # => "refined my_method()"
MyClass.new.another_method # => "original my_method()" - How is this possible?

В соответствии с автором:

Тем не менее, призыв к another_method может застать вас врасплох: даже если вы позвоните another_method после usingПризыв к my_method само по себе происходит раньше using - так он вызывает оригинальную, не определенную версию метода.

Это полностью сбивает меня с толку.

Зачем MyClass.new.another_method печатает "оригинальный my_method()", так как он используется после using MyClassRefinement и что автор пытается здесь сказать?

Может ли кто-нибудь дать более интуитивное / лучшее объяснение?

Благодарю.

1 ответ

Решение

Лучшее объяснение, которое я могу найти, из документов:

Уточнения носят лексический характер. Уточнения активны только в области действия после вызова using, Любой код до using У заявления не будет активированного уточнения.

Это означает, что ваш метод уточнения должен быть вызван где-то после вызова using, Фактическое местоположение вызова метода имеет значение, а не как метод был вызван или откуда метод был вызван.


Вот что происходит.

  1. using т.е. using MyClassRefinement активирует my_method уточнение.
  2. MyClass.new.my_method выполнен.
  3. Поиск метода следует из точной точки вызова:

При поиске метода для экземпляра class C Рубиновые чеки:

  • Если уточнения активны для Cв обратном порядке они были активированы
    • Готовые модули из уточнения для C
    • Уточнение для C
    • Включенные модули из уточнения для C
  • Предустановленные модули C
  • C
  • Включенные модули C
  1. Уточнения активны, и my_method возвращает код из уточнения "refined my_method()"
  2. MyClass.new.another_method выполнен.
  3. Поиск метода вытекает из точной точки вызова.
  4. Уточнения активны в этот момент вызова, но another_method это не уточнение, поэтому Ruby ищет another_method в классе MyClass и находит это.
  5. Внутри метода класса another_method, метод my_method найден и вызван.
  6. Поиск метода вытекает из точной точки вызова.
  7. В момент вызова нет активных уточнений, потому что не было вызовов using выше линии (т.е. физически до) где my_method вызывается. Руби продолжает искать my_method в классе MyClass и находит это.
  8. my_method возвращает код из метода класса "original my_method()",

Мы можем сделать простое сравнение. Допустим, у меня есть один изолированный file.rb со следующим кодом:

puts puppy
puppy = 'waggle'

puppy не может быть использован, пока он не определен. Переменная имеет лексическую область видимости, и ее использование зависит от местоположения ее определения в изолированной file.rb,

Точно так же уточнение не может быть вызвано, пока оно не было активировано через using на предыдущей строке (или где-то физически предыдущий в файле исходного кода). Уточнение лексически ограничено.

Из Википедии

В языках с лексической областью (также называемой статической областью) разрешение имен зависит от местоположения в исходном коде и лексического контекста, который определяется тем, где определена именованная переменная или функция...

Лексическое разрешение может быть определено во время компиляции и также известно как раннее связывание, в то время как динамическое разрешение обычно может быть определено только во время выполнения, и, таким образом, известно как позднее связывание.


Ваша конкретная проблема с уточнениями обсуждается в последнем разделе этой статьи. Автор также объясняет, как using Физическое местоположение оператора в файле определяет, является ли уточнение активным.

Другие вопросы по тегам