Skip to content

Instantly share code, notes, and snippets.

@trans
Created April 24, 2011 11:45
Show Gist options
  • Save trans/939506 to your computer and use it in GitHub Desktop.
Save trans/939506 to your computer and use it in GitHub Desktop.
Simple method probe.
# Recorder, Copyright (c) 2006 Thomas Sawyer
require 'facets/kernel/object_class'
# = Recorder
#
# The Recorder class is a <i>method probe</i>. It records everything
# that happens to it, building an internal action tree. You can then
# pass a substitute object and apply the recording to it. Or you can
# analyze the action tree.
#
# The only limitation of Recorder is with special operators, like
# <code>if</code>, <code>&&</code>, <code>||</code>, and so on.
# Since they are not true methods they can't be recorded (too bad for Ruby).
#
# class Z
# def name ; 'George' ; end
# def age ; 12 ; end
# end
#
# z = Z.new
#
# r = Recorder.new
# q = proc{ |x| (x.name == 'George') & (x.age > 10) }
# x = q[r]
# x.__call__(z)
#
# produces
#
# true
#
class Recorder
#
alias_method :__class__, :class
# Privatize all kernel methods.
private(*instance_methods)
#
def initialize(track=nil, message=nil)
@track = (track || []) << self
@message = message
@stack = []
end
def inspect
"#{@message.inspect}"
end
def __message__
@message
end
#def __call__(subject)
#p @track.last
# if @message
# o, s, a, b = *@message
# subject = subject.__send__(s, *a, &b)
# end
#
# subject
#end
def __call__(orig)
@stack.each do |s|
s.__replay__(orig)
end
end
def __replay__(orig)
return orig unless @message #.empty?
rec, sym, args, blk = *@message
rec = rec.__replay__(orig)
args = args.map do |a|
Recorder === a ? a.__replay__(orig) : a
end
#obj = args.unshift
rec.__send__(sym, *args, &blk)
end
#
def method_missing(sym, *args, &blk)
r = __class__.new(@track, [self, sym, args, blk])
@stack << r
r
end
end
# related article I found
# http://blog.jayfields.com/2008/09/ruby-recording-method-calls-and.html
require 'recorder'
require 'test/unit'
#class Object
# def &(o)
# self && o
# end
#end
class TCRecorder < Test::Unit::TestCase
class Dummy
attr_accessor :name
attr_accessor :age
def initialize(name, age)
@name = name
@age = age
end
end
def test_simple
r = Recorder.new
q = proc { |x| x.name.upcase!; x.age += 1 }
q[r]
z = Dummy.new('George', 11)
r.__call__(z)
assert_equal('GEORGE', z.name)
z = Dummy.new('Tony', 12)
r.__call__(z)
assert_equal(13, z.age)
end
def test_condition
r = Recorder.new
q = proc { |x| (x.name == 'George') & (x.age > 10) }
q[r]
z = Dummy.new('George', 11)
assert_equal(true, r.__call__(z))
z = Dummy.new('George', 9)
assert_equal(false, r.__call__(z))
z = Dummy.new('Tony', 12)
assert_equal(false, r.__call__(z))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment