如何拦截ruby中的方法调用?

时间:2011-08-21 08:34:35

标签: ruby

我目前有一个超类,它有一个函数,我希望所有子类都在它的每个函数中调用。该函数应该像rails中的before_filter函数一样,但我不确定如何实现before_filter。这是一个例子

class Superclass
  def before_each_method
    puts "Before Method" #this is supposed to be invoked by each extending class' method
  end
end

class Subclass < Superclass
  def my_method
    #when this method is called, before_each_method method is supposed to get invoked
  end
end

3 个答案:

答案 0 :(得分:11)

这是一种方法:

class Superclass
  def before_each_method name
    p [:before_method, name]
  end

  def self.method_added name
    return if @__last_methods_added && @__last_methods_added.include?(name)
    with = :"#{name}_with_before_each_method"
    without = :"#{name}_without_before_each_method"
    @__last_methods_added = [name, with, without]
    define_method with do |*args, &block|
      before_each_method name
      send without, *args, &block
    end
    alias_method without, name
    alias_method name, with
    @__last_methods_added = nil
  end
end

class SubclassA < Superclass
  def my_new_method
    p :my_new_method
  end

  def my_new_other_method
    p :my_new_other_method
  end
end

SubclassA.new.my_new_method
SubclassA.new.my_new_other_method

只要在子类中定义了要包装的方法,就会使用alias_method_chaining方法创建一个包装器方法。

答案 1 :(得分:6)

这是我的解决方案:

require 'active_support/all'

module BeforeEach
  extend ActiveSupport::Concern

  module InstanceMethods
    def before_each
      raise NotImplementedError('Please define before_each method')
    end
  end

  module ClassMethods
    def method_added(method)
      method = method.to_s.gsub(/_with(out)?_before$/, '')
      with_method, without_method = "#{method}_with_before", "#{method}_without_before"

      return if method == 'before_each' or method_defined?(with_method)

      define_method(with_method) do |*args, &block|
        before_each
        send(without_method, *args, &block)
      end
      alias_method_chain(method, :before)
    end
  end
end

要使用它,只需将BeforeEach包含在您的课程中,如下所示:

class Superclass
  include BeforeEach

  def before_each
    puts "Before Method" #this is supposed to be invoked by each extending class' method
  end
end

class Subclass < Superclass
  def my_method
    #when this method is called, before_each_method method is supposed to get invoked
  end
end

Subclass.new.my_method

# => Before Method

希望这对你有用!

答案 2 :(得分:0)

class BalanceChart < BalanceFind
include ExecutionHooks

attr_reader :options

def initialize(options = {})
 @options = options
 @begin_at = @options[:begin_at]
end

def months_used
 range.map{|date| I18n.l date, format: :month_year}.uniq!
end

before_hook :months_data, :months_used, :debits_amount
end

module ExecutionHooks

def self.included(base)
 base.send :extend, ClassMethods
end

module ClassMethods

def before
  @hooks.each do |name|
    m = instance_method(name)
    define_method(name) do |*args, &block|  

      return if @begin_at.blank? ## the code you can execute before methods

      m.bind(self).(*args, &block) ## your old code in the method of the class
    end
  end
end

def before_hook(*method_name)
  @hooks = method_name
  before
end

def hooks
  @hooks ||= []
end
end
end