Created
February 16, 2016 22:20
-
-
Save shiroyasha/64a3605008c3203516cf to your computer and use it in GitHub Desktop.
Method tracer for Ruby classes
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 Dog | |
attr_writer :name | |
def initialize(name) | |
@name = name | |
end | |
def bark | |
puts "patrick" | |
end | |
end |
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
require "tracer" | |
# turn on method tracing for every Dog instance | |
Tracer.measure(Dog) | |
dog = Dog.new("sparky") | |
dog.name = "patrick" | |
dog.bark |
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
Dog#name= called with arguments ["patrick"] | |
Dog#name= took 1.7329000002064276e-05 and returned "patrick" | |
Dog#bark called with arguments [] | |
Dog#bark took 3.9409999999406864e-05 and returned nil |
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
require "benchmark" | |
module Tracer | |
extend self | |
def measure(klass) | |
methods = klass.instance_methods(false) | |
klass.class_eval do | |
methods.each do |method| | |
alias_method :"original_#{method}", method | |
define_method(method) do |*args| | |
Tracer.log_method_start(self.class.name, method, args) | |
result = nil | |
duration = Benchmark.realtime do | |
result = self.send(:"original_#{method}", *args) | |
end | |
Tracer.log_method_end(self.class.name, method, result, duration) | |
result | |
end | |
end | |
end | |
end | |
def log_method_start(klass_name, method_name, arguments) | |
puts "#{klass_name}##{method_name} called with arguments #{arguments.inspect}" | |
end | |
def log_method_end(klass_name, method_name, result, duration) | |
puts "#{klass_name}##{method_name} took #{duration} and returned #{result.inspect}" | |
puts "\n" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is really nice.
Small nitpick but as a general rule you shouldn’t assume anything about the class you’re getting; it might already define methods that start with
original_
, e.g.:Output:
You can also get an infinite loop by calling
Tracer.measure(C)
twice beforeC.new.a
(this works with any class).Here is a possible solution (not extensively tested):
Output: