I'm all about Ember.js recently

Ruby Metaprogramming: Call Counter - Take 2

So last time I left you with the promise that I’d return with a solution so that the number of times a certain method was called is a class method which makes more sense than if it was an instance method. So here is the “improved version”:

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

What has changed is the introduction of a class variable that counts the calls on all watched methods and that the number of calls on each method is queried by calls_to(<method name>) instead of calls_to_<method_name>. A bit less magic.

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

(a snippet of) 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 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
    
end

I’m still not 100% content with this solution, the programming interface is nice now but it would be cool to get rid of the class variable somehow, possibly replacing it with closures. If you know how to achieve it, please leave a comment.

ps. You can also get the whole code in nice colored format or the raw text version.