Модуль, чтобы обернуть метод класса? - PullRequest
5 голосов
/ 25 августа 2011

Можно ли сделать эту работу, не включая модуль в конце класса и просто включив его в начало?

module VerboseJob
  def self.included(job_class)
    class << job_class
      alias_method :original_perform, :perform
      def perform(*args)
        JobLogger.verbose { original_perform(*args) }
      end
    end
  end
end

class HelloJob
  include VerboseJob

  def self.perform(arg1, arg2)
    puts "Job invoked with #{arg1} and #{arg2}"
  end
end

То, что я хочу, это для HelloJob.perform для фактического вызова VerboseJob.perform (который затем вызывает оригинальный метод внутри блока).Поскольку этот модуль включен в начало класса, это не работает, поскольку perform еще не определено.Перемещение include в конец работает, но есть ли способ более щадящий?Мне нравится держать все включенные модули в начале моих определений классов.

Я как бы ищу какой-то метод, который вызывается на Module или Class, когда он полностью загружен, вместо этогоо том, как это интерпретируется средой выполнения.

Ответы [ 2 ]

2 голосов
/ 25 августа 2011

Вот довольно окольный / хакерский способ сделать это, я придумал, отложив определение метода-оболочки до тех пор, пока не будет определен исходный метод:

module A
  def self.included(base)
    base.class_eval do
      def self.singleton_method_added(name)
        @@ran||=false
        if name==:perform && !@@ran
          @@ran=true
          class<<self
            alias_method :original_perform, :perform
            def perform(*args)
              puts "Hello"
              original_perform(*args)
            end
          end
        end
      end
    end
  end
end

class B
  include A

  def self.perform
    puts "Foobar"
  end
end

B.perform

Edit:

d11wtq упростила это до намного чище:

module VerboseJob
  module ClassMethods
    def wrap_perform!
      class << self
        def perform_with_verbose(*args)
          JobLogger.verbose { perform_without_verbose(*args) }
        end

        alias_method_chain :perform, :verbose \
          unless instance_method(:perform) == instance_method(:perform_with_verbose)
      end
    end

    def singleton_method_added(name)
      wrap_perform! if name == :perform
    end
  end

  def self.included(job_class)
    job_class.extend ClassMethods
    job_class.wrap_perform! if job_class.respond_to?(:perform)
  end
end
0 голосов
/ 26 августа 2011

Предполагая, что вы хотите, чтобы все ваши классы были определены до запуска perform, вы можете использовать Kernel # at_exit :

Преобразует блок в объект Proc(и, следовательно, связывает его в точке вызова) и регистрирует его для выполнения при выходе из программы.Если зарегистрированы несколько обработчиков, они выполняются в обратном порядке регистрации.

   def do_at_exit(str1)
     at_exit { print str1 }
   end
   at_exit { puts "cruel world" }
   do_at_exit("goodbye ")
   exit

производит:

прощай жестокий мир

Вы также можете посмотреть, как платформы модульного тестирования, такие как Test :: Unit или MiniTest, справляются с задержкой выполнения задач.

...