Skip to content

Instantly share code, notes, and snippets.

@rcorreia
Forked from ryanlecompte/gist:3631742
Created September 5, 2012 07:28
Show Gist options
  • Save rcorreia/3632616 to your computer and use it in GitHub Desktop.
Save rcorreia/3632616 to your computer and use it in GitHub Desktop.
My solution to New Relic's programming challenge
# I had a lot of fun doing this one! The following code makes
# the tests at https://gist.github.com/6ea0a0ba5702824075ab pass.
#
# NOTE: I normally would DRY some of this code up, but it's just a
# fun challenge and would never be deployed to production. :)
module MethodInstrumenter
def self.instrument_path(path)
@path = path
@instrumenting = false
@counts = 0
@original_plus = Fixnum.instance_method(:+)
attempt_eager_instrumentation(path)
end
def self.instrumented_path
@path
end
def self.path_counts
@counts
end
def self.increment_path_counter
@counts = @original_plus.bind(@counts).call(1)
end
def self.instrumenting?
@instrumenting
end
def self.redefine_instance_method(klass, method_name)
@instrumenting = true
original_method = klass.instance_method(method_name)
klass.send(:define_method, method_name) do |*args, &blk|
MethodInstrumenter.increment_path_counter
original_method.bind(self).call(*args, &blk)
end
@instrumenting = false
end
def self.redefine_class_method(klass, method_name)
@instrumenting = true
original_method = klass.method(method_name)
klass.define_singleton_method(method_name) do |*args, &blk|
MethodInstrumenter.increment_path_counter
original_method.call(*args, &blk)
end
@instrumenting = false
end
def self.attempt_eager_instrumentation(path)
klass_path, method_name = path.split(/[.#]/)
klass = klass_path.split('::').inject(Object) { |acc, o| acc.const_get(o) }
if path.include?('#')
redefine_instance_method(klass, method_name)
elsif path.include?('.')
redefine_class_method(klass, method_name)
else
raise ArgumentError, "Unknown path: #{path}"
end
rescue => ex
# we don't know about the class or method yet
end
end
class Module
def method_added(m)
super if defined?(super)
return if MethodInstrumenter.instrumenting?
path = "#{name}##{m}"
if MethodInstrumenter.instrumented_path == path
MethodInstrumenter.redefine_instance_method(self, m)
end
end
def singleton_method_added(m)
super if defined?(super)
return if MethodInstrumenter.instrumenting?
path = "#{name}.#{m}"
if MethodInstrumenter.instrumented_path == path
MethodInstrumenter.redefine_class_method(self, m)
end
end
def included(base)
super if defined?(super)
return if MethodInstrumenter.instrumenting?
instance_methods(false).each do |m|
if MethodInstrumenter.instrumented_path == "#{base.name}##{m}"
MethodInstrumenter.redefine_instance_method(base, m)
end
end
end
def extended(base)
super if defined?(super)
return if MethodInstrumenter.instrumenting?
base_name = Module === base ? base.name : base.class.name
instance_methods(false).each do |m|
if MethodInstrumenter.instrumented_path == "#{base_name}.#{m}"
MethodInstrumenter.redefine_class_method(base, m)
end
end
end
end
class Class
def inherited(base)
super if defined?(super)
return if MethodInstrumenter.instrumenting?
instance_methods(false).each do |m|
if MethodInstrumenter.instrumented_path == "#{base.name}##{m}"
MethodInstrumenter.redefine_instance_method(base, m)
end
end
singleton_methods(false).each do |m|
if MethodInstrumenter.instrumented_path == "#{base.name}.#{m}"
MethodInstrumenter.redefine_class_method(base, m)
end
end
end
end
MethodInstrumenter.instrument_path(ENV['COUNT_CALLS_TO'])
at_exit do
if path = ENV['COUNT_CALLS_TO']
puts '%s called %d times' % [path, MethodInstrumenter.path_counts]
end
end
__END__
[ryan@:~] $ ruby meta_counter_test.rb
Run options:
# Running tests:
.........................
Finished tests in 1.325786s, 18.8567 tests/s, 18.8567 assertions/s.
25 tests, 25 assertions, 0 failures, 0 errors, 0 skips
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment