Получить все методы экземпляра поверх определения класса - PullRequest
0 голосов
/ 22 октября 2018

Я пытаюсь обернуть все методы экземпляра TestClass для выполнения кода до и после . Вызывается метод экземпляра.Пока этот код работает:

module Wrapper
  def wrap(*methods)
    prependable_module = Module.new do
      methods.each do |m|
        define_method(m) do |*args, &block|
          p 1
          super(*args, &block)
          p 3
        end
      end
    end

    prepend prependable_module
  end
end

class TestClass
  extend Wrapper
  wrap :instance_method1

  def instance_method1
    p 2
  end
end

TestClass.new.instance_method1 # => 1, 2, 3

Я могу вызвать wrap со всеми именами методов в качестве аргументов.Если я пытаюсь обернуть все методы, не перечисляя их по отдельности, мне нужно вызвать его, используя instance_methods(false) внизу определения класса.

class TestClass
  extend Wrapper

  def instance_method1
    p 2
  end

  wrap(*instance_methods(false))
end

В Rails все методы обратного вызова, такие как before_action или after_create обычно вызывается поверх определения класса. Моя цель - также вызвать wrap поверх определения класса (без перечисления всех методов по отдельности).В этом случае я не могу вызвать instance_methods(false) поверх определения класса, потому что на данный момент метод не определен.

Спасибо за вашу помощь!

Обновление

Благодаря подходу Киммо Лехто я могу обернуть каждый экземплярный метод с помощью хука method_added.Я не хочу добавлять новый модуль для каждого определенного метода, поэтому я добавляю все переопределенные методы в один и тот же модуль.

module Wrapper
  def method_added(method_name)
    tmp_module = find_or_initialize_module
    return if tmp_module.instance_methods(false).include?(method_name)

    tmp_module.define_method(method_name) do |*args, &block|
      p 1
      super(*args, &block)
      p 3
    end
  end

  def find_or_initialize_module
    module_name  = "#{name}Wrapper"
    module_idx   = ancestors.map(&:to_s).index(module_name)

    unless module_idx
      prepend Object.const_set(module_name, Module.new)
      return find_or_initialize_module
    end

    ancestors[module_idx]
  end
end

class TestClass
  extend Wrapper

  def instance_method1
    p 2
  end
end

tc = TestClass.new
tc.instance_method1 # => 1, 2, 3

1 Ответ

0 голосов
/ 23 октября 2018

Вы можете использовать хук Module # method_added , чтобы автоматически обернуть любые добавляемые методы.

Вам понадобится немного магии, чтобы не переполнить стек из бесконечного цикла.

Другим вариантом является использование TracePoint для запуска переноса после определения класса.Вы можете использовать Module#extended для настройки точки трассировки.Примерно так:

module Finalizer
  def self.extended(obj)
    TracePoint.trace(:end) do |t|
      if obj == t.self
        obj.finalize
        t.disable
      end
    end
  end

  def finalize
    wrap(*instance_methods(false))
  end
end

Классы обычно не совсем "закрыты", если вы явно не .freeze их, так что это немного хакерское решение и не сработает, если методы будут добавлены впоследствии.method_added, вероятно, ваш лучший выбор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...