Sometimes you want to call methods on an object, but you want to delay the actual execution of those methods till a later time.
For example, in expectations you create a mock at parse time, but you actually want the mock to be available at execution time.
class SystemProcess
def start
puts "started"
new StartedProcess
end
end
Expectations do
expect SystemProcess.new.to.receive(:start) do |process|
process.start
end
end
In the above code you define what you expect when the file is parsed, but you actually want the process expectation to be set when the do block is executed. This can be (and is) achieved by using a recorder to record all the method calls on the process object. At execution time the method calls are played back and the initialized process object is yielded to the block.
The code for a recorder is actually quite trivial in Ruby.
class Recorder
attr_reader :subject
def initialize(subject)
@subject = subject
end
def replay
method_stack.inject(subject) { |result, element| result.send element.first, *element.last }
end
def method_stack
@method_stack ||= []
end
def method_missing(sym, *args)
method_stack << [sym, args]
self
end
end
Here's an example usage of a recorder.
class SystemProcess
def start(in_seconds)
puts "starting in #{in_seconds}"
sleep in_seconds
StartedProcess.new
end
end
class StartedProcess
def pause(in_seconds)
puts "pausing in #{in_seconds}"
sleep in_seconds
PausedProcess.new
end
end
class PausedProcess
def stop(in_seconds)
puts "stopping in #{in_seconds}"
sleep in_seconds
self
end
end
Recorder.new(SystemProcess.new).start(1).pause(2).stop(3).replay
The only thing worth noting is that by using inject you can use a method chain that returns different objects. Traditional versions of a recorder that I've seen often assume that all the methods should be called on the subject. I prefer the version that allows for object creation within the fluent interface. In practice, that's exactly what was needed for recording and playing back Mocha's expectation setting methods.

Read: Ruby: Recording Method Calls and Playback With Inject