Created
February 10, 2013 04:40
-
-
Save JEG2/4748378 to your computer and use it in GitHub Desktop.
My solution to https://groups.google.com/forum/?fromgroups=#!searchin/pdxruby/Metaprogramming$20Challenge/pdxruby/CJUoR4X5w_w/Ppih4KMLy-MJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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