在运行时包装Ruby方法以拦截方法调用

时间:2014-08-15 20:52:56

标签: ruby-on-rails ruby

我有一个类似于ActiveRecord的父类,我正试图在我们的测试套件中打开这个类。我想包装initialize方法,以便它维护一个初始化的所有子类的列表,这样我就可以确保在测试之间清理这些类的所有数据。在测试之间运行完全擦除会导致效率太低(而且我只是对代码想要执行此操作感兴趣)。

我的目标是在继承树中插入新的initialize方法,然后调用super。一直在维护父类中所有实例化类的一些列表。

到目前为止我的尝试:

class NewActiveRecord
  @@__instantiated_classes = Set.new

  def initialize(*args)
    @@__instantiated_classes.add(self.class)
    super *args
  end

  def self.reset_tracking
    @@__instantiated_classes = Set.new
  end

  def self.get_instantiated_classes
    @@__instantiated_classes.to_a
  end
end

class ActiveSupport::TestCase
   teardown do
      NewActiveRecord.get_instantiated_classes.each {|c| c.destroy_all}
      NewActiveRecord.reset_tracking
   end
end

基本上我想包装所有在某些父类的子类上调用的方法,将它们的类发送到某个预定义对象的类

1 个答案:

答案 0 :(得分:0)

今天做了一些探索并提出了以下解决方案。我仍然不确定如何解开一些东西但是:

module Spy
  def self.on_instance_method(mod, method, &block)
    mod.class_eval do
      # Stash the old method
      old_method = instance_method(method)

      # Create a new proc that will call both our block and the old method
      proc = Proc.new do
        block.call if block
        old_method.bind(self).call
      end

      # Bind that proc to the original module
      define_method(method, proc)
    end
  end

  def self.on_class_method(mod, method, &block)
    mod.class_eval do
      # Stash the old method
      old_method = singleton_method(method)

      # Create a new proc that will call both our block and the old method
      proc = Proc.new do
        block.call if block
        old_method.call
      end

      # Bind that proc to the original module
      define_singleton_method(method, proc)
    end
  end
end

我的测试用法如下:

count = 0
Spy.on_instance_method(FakeClass, :value) { count += 1 }
fake = FakeClass.new(6)
fake.value.must_equal 6
count.must_equal 1