Skip to content

Instantly share code, notes, and snippets.

@JEG2
Created February 10, 2013 04:40
Show Gist options
  • Save JEG2/4748378 to your computer and use it in GitHub Desktop.
Save JEG2/4748378 to your computer and use it in GitHub Desktop.
class MethodCounter
def initialize
@signature = ENV.fetch("COUNT_CALLS_TO")
@path = @signature.split(/(?:::|#)/)
@scope = @signature.include?("#") ? :instance : :class_or_module
@count = 0
@wrapping = false
@wrapped = false
end
attr_reader :signature, :path, :scope
private :signature, :path, :scope
attr_accessor :count, :wrapping, :wrapped
private :wrapping=, :wrapped=
def add_counter_to_method
method = method_to_count or return
counter = self
self.wrapping = true
class_or_module.send( instance? ? :define_method
: :define_singleton_method,
method_to_count_name ) { |*args, &block|
counter.count += 1
counter.method_to_count.bind(self).call(*args, &block)
}
self.wrapping = false
self.wrapped = true
end
def add_method_hooks
counter = self
Module.send(:define_method, :method_added) do |*args|
return if counter.wrapping?
counter.add_counter_to_method unless counter.wrapped?
end
Module.send(:define_method, :singleton_method_added) do |*args|
return if counter.wrapping?
counter.add_counter_to_method unless counter.wrapped?
end
Module.send(:define_method, :included) do |*args|
counter.add_counter_to_method unless counter.wrapped?
end
Module.send(:define_method, :extended) do |*args|
counter.add_counter_to_method unless counter.wrapped?
end
Module.send(:define_method, :inherited) do |*args|
counter.add_counter_to_method unless counter.wrapped?
end
end
def method_to_count
@method_to_count ||= method_target.instance_method(method_to_count_name)
rescue NameError
# method doesn't yet exist
end
def report
"#{signature} called #{@count} time#{'s' unless @count == 1}"
end
def wrapping?
@wrapping
end
def wrapped?
@wrapped
end
private
def class_or_module_path
@class_or_module_path ||= path[0..-2]
end
def class_or_module
@class_or_module ||=
class_or_module_path.inject(Object) { |object, constant|
object.const_get(constant)
}
rescue NameError
# class or module doesn't exist yet
end
def method_to_count_name
@method_to_count_name ||= path[-1].to_sym
end
def instance?
scope == :instance
end
def method_target
@method_target = instance? ? class_or_module
: class_or_module.singleton_class
end
end
counter = MethodCounter.new
counter.add_counter_to_method
counter.add_method_hooks unless counter.wrapped?
at_exit do
warn counter.report
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment