我有一个普通的红宝石班Espresso::MyExampleClass
。
module Espresso
class MyExampleClass
def my_first_function(value)
puts "my_first_function"
end
def my_function_to_run_before
puts "Running before"
end
end
end
使用本课程中的某些方法,我想执行类似于ActiveSupport回调before
或after
的{{1}}或before_action
回调。我想在我的班级中添加类似的内容,该版本会在before_filter
之前运行my_function_to_run_before
:
my_first_function
结果应该是这样的:
before_method :my_function_to_run_before, only: :my_first_function
如何在Rails之类的普通ruby类中使用回调来在每个指定方法之前运行方法?
Edit2:
感谢@tadman推荐XY problem。我们遇到的真正问题是具有令牌过期的API客户端。在每次调用API之前,我们需要检查令牌是否已过期。如果我们对API有很多功能,那么每次检查令牌是否过期都很麻烦。
以下是示例类:
klass = Espresso::MyExampleClass.new
klass.my_first_function("yes")
> "Running before"
> "my_first_function"
答案 0 :(得分:2)
天真的实施将是;
module Callbacks
def self.extended(base)
base.send(:include, InstanceMethods)
end
def overridden_methods
@overridden_methods ||= []
end
def callbacks
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
end
def method_added(method_name)
return if should_override?(method_name)
overridden_methods << method_name
original_method_name = "original_#{method_name}"
alias_method(original_method_name, method_name)
define_method(method_name) do |*args|
run_callbacks_for(method_name)
send(original_method_name, *args)
end
end
def should_override?(method_name)
overridden_methods.include?(method_name) || method_name =~ /original_/
end
def before_run(method_name, callback)
callbacks[method_name] << callback
end
module InstanceMethods
def run_callbacks_for(method_name)
self.class.callbacks[method_name].to_a.each do |callback|
send(callback)
end
end
end
end
class Foo
extend Callbacks
before_run :bar, :zoo
def bar
puts 'bar'
end
def zoo
puts 'This runs everytime you call `bar`'
end
end
Foo.new.bar #=> This runs everytime you call `bar`
#=> bar
此实现中的棘手问题是method_added
。每当方法获得绑定时,ruby会使用方法名称调用method_added
方法。在这个方法的内部,我正在做的只是名称修改和覆盖原始方法的新方法,首先运行回调然后调用原始方法。
请注意,此实现既不支持块回调,也不支持超类方法的回调。它们都可以很容易地实现。