Ответ 1
Используйте TracePoint
, чтобы отслеживать, когда ваш класс отправляет событие :end
.
Общее решение
Этот модуль позволит вам создать обратный вызов self.finalize
в любом классе.
module Finalize
def self.extended(obj)
TracePoint.trace(:end) do |t|
if obj == t.self
obj.finalize
t.disable
end
end
end
end
Теперь вы можете расширить свой класс и определить self.finalize
, который запустится, как только закончится определение класса:
class Foo
puts "Top of class"
extend Finalize
def self.finalize
puts "Finalizing #{self}"
end
puts "Bottom of class"
end
puts "Outside class"
# output:
# Top of class
# Bottom of class
# Finalizing Foo
# Outside class
Конкретное решение проблемы ОП
Вот как вы можете вставить TracePoint
непосредственно в ранее существовавший модуль.
require 'active_support/all'
module MyPlugin
extend ActiveSupport::Concern
module ClassMethods
def consumes_my_plugin(**options)
m = options[:specific_method_to_use]
TracePoint.trace(:end) do |t|
break unless self == t.self
raise ArgumentError.new("#{m} is not defined") unless instance_methods.include?(m)
t.disable
end
end
end
end
Приведенные ниже примеры демонстрируют, что он работает как указано:
# 'def' before 'consumes': evaluates without errors
class MethodBeforePlugin
include MyPlugin
def your_method; end
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end
# 'consumes' before 'def': evaluates without errors
class PluginBeforeMethod
include MyPlugin
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
def your_method; end
end
# 'consumes' with no 'def': throws ArgumentError at load time
class PluginWithoutMethod
include MyPlugin
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end