Report abuse

call_counter.rb

module CallCounter

    def count_calls_to(method_name)
      original_method = instance_method(method_name)

      unless class_variable_defined?(:@@call_counter):
        class_variable_set(:@@call_counter, {})
      end

      call_counter = class_variable_get(:@@call_counter)

      define_method(method_name) do |*args|
        call_counter[method_name] ||= 0
        call_counter[method_name] += 1
        bound_original_method = original_method.bind(self)
        bound_original_method.call(*args)
      end

      metaclass = class << self; self; end
      metaclass.instance_eval do

        define_method(:calls_to) do |m|
          call_counter[m].nil? ? 0 : call_counter[m]
        end

        define_method(:reset_counters) do
          call_counter.each_key do |k|
            call_counter[k] = 0
          end
        end
    end

  end

end

call_foo.rb

require "call_counter"

class CallFoo

  extend CallCounter

  def foo; "foo"; end
  def bar; "bar"; end

  count_calls_to :foo
  count_calls_to :bar

end

call_counter_spec.rb

require "call_foo"
require "spec"

describe CallFoo do

  before(:each) do
    @call_foo = CallFoo.new
    CallFoo.reset_counters
  end

  it "should have a method calls_to" do
    CallFoo.should respond_to(:calls_to)    
  end  

  it "method counter should be zero at start" do
    CallFoo.calls_to(:foo).should == 0
  end

  it "should reset counters" do
    4.times { @call_foo. foo }
    CallFoo.reset_counters
    CallFoo.calls_to(:foo).should == 0
  end

  it "should count the number of times a counted method has been called" do
    4.times { @call_foo.foo }
    CallFoo.calls_to(:foo).should == 4
  end

  it "should be able to count several methods' calls" do
    4.times { @call_foo.foo }
    CallFoo.calls_to(:foo).should == 4
    7.times { @call_foo.bar }
    CallFoo.calls_to(:bar).should == 7
  end

  it "shold count bar calls correctly, too" do
    7.times { @call_foo.bar }
    CallFoo.calls_to(:bar).should == 7    
  end

end