Как определить макрос уровня класса в модуле в Ruby? - PullRequest
0 голосов
/ 26 апреля 2020

На языке программирования Ruby я создаю класс с макросом уровня класса следующим образом:

class Timer 
  def self.add_time
    def time
      STDERR.puts Time.now.strftime("%H:%M:%S")      
    end 
  end 
end 

Метод класса add_time при выполнении генерирует time метод. Теперь я могу выполнить этот макрос уровня класса в другом классе Example следующим образом:

class Example < Timer 
  add_time 
end 

Когда я сейчас вызываю time для экземпляра класса Example, метод time присутствует там, как я и предполагал:

ex = Example.new 
ex.time

и печатает текущее время: 23:18:38.

Но теперь я хотел бы поместить макрос add_time в модуль и по-прежнему иметь тот же общий эффект. Я пытался с include следующим образом:

module Timer 
  def self.add_time
    def time
      STDERR.puts Time.now.strftime("%H:%M:%S")      
    end 
  end 
end 

class Example 
  include Timer
  add_time
end 

ex = Example.new 
ex.time

, но затем я получаю сообщение об ошибке, что метод add_time не определен в классе Example: NameError: undefined local variable or method ‘add_time’ for Example:Class. Затем я попытался использовать extend вместо этого, как это:

class Example 
  extend Timer
  add_time
end

, но это дает мне похожую ошибку.

Итак, вопрос: Как я могу получить то же самое эффект, как в моем первоначальном примере, где Timer был определен как класс, но вместо него используется модуль?

1 Ответ

0 голосов
/ 26 апреля 2020

Как указал @CarySwoveland, метод def self.add_time в module Timer игнорируется при включении или расширении в классе. Только методы экземпляра модуля добавляются в класс как метод экземпляра класса (в случае включения) или как методы класса (в случае расширений).

module Timer 
  def add_time # INSTANCE METHOD !
    def time
      STDERR.puts Time.now.strftime("%H:%M:%S")      
    end 
  end 
end 

Итак, первый шаг решение состоит в том, чтобы объявить метод def add_time как метод экземпляра модуля. Затем мы расширяем класс Example этим модулем, чтобы метод экземпляра модуля был добавлен как метод класса в класс Example, и мы вызываем метод add_time:

class Example 
  extend Timer # EXTEND INSTEAD OF INCLUDE
  add_time
end

Тем не менее, это не совсем так, как хотелось бы, так как метод time теперь генерируется как метод класса: Example.time печатает текущее время 01:30:37, но экземпляр ex класса Example не делает понять метод time.

Таким образом, решение состоит в том, чтобы сгенерировать метод def time как метод экземпляра, а не как метод класса. Это можно сделать с помощью class_eval, что приводит нас к следующему рабочему решению:

module Timer 
  def add_time # INSTANCE METHOD !
    self.class_eval do # USE class_eval TO DEFINE AN INSTANCE METHOD !
      def time
        STDERR.puts Time.now.strftime("%H:%M:%S")      
      end 
    end 
  end
end 

class Example 
  extend Timer # USE EXTEND TO ADD add_time AS A CLASS METHOD
  add_time
end 

ex = Example.new 
ex.time
...