开发者

How can I intercept method call in ruby?

开发者 https://www.devze.com 2023-03-30 03:02 出处:网络
I currently have a superclass which has a function that I want all the subclass to call within each of its function. The function is supposed to behave like a before_filter function in rails but I am

I currently have a superclass which has a function that I want all the subclass to call within each of its function. The function is supposed to behave like a before_filter function in rails but I am not sure on how to go about implementing before_filter. Here is an example

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


This is one way to do it:

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

This will create a wrapper method using the alias_method_chaining method as soon as the method you'd like to wrap is defined in the subclass.


This is my solution:

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

To use it, just include BeforeEach into your class like so:

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

Hopefully this will work for you!


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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号