在纯Ruby类中使用方法回调

时间:2018-03-12 16:39:07

标签: ruby activesupport

我有一个普通的红宝石班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回调beforeafter的{​​{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"

1 个答案:

答案 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方法。在这个方法的内部,我正在做的只是名称修改和覆盖原始方法的新方法,首先运行回调然后调用原始方法。

请注意,此实现既不支持块回调,也不支持超类方法的回调。它们都可以很容易地实现。