This post originated from an RSS feed registered with Ruby Buzz
by Florian Frank.
Original Post: Casting timestop and time travel
Feed Title: The Rubylution: Tag Ruby
Feed URL: http://rubylution.ping.de/xml/rss/tag/ruby/feed.xml
Feed Description: Starts… Now.
If you want to test classes, that create Time objects for the current time, you sometimes require several different times to test your methods, time ranges, and so on. The easiest way to achieve this is of course to create time instances and sleep for a few seconds between the consecutive calls to Time.new or Time.now in your methods.
One problem with this approach is, that your tests will require much more time than really is necessary, while Ruby is just idling around and waiting. Another problem is, that you cannot test times with very long intervals between them, say several years, unless you're very, very patient.
A better way is to have accurate control over the created instances without having to wait. It turns out, that this can be done quite easy in Ruby, and the change can be made transparent to the tested classes. (This much different to the situation in Java, for example.)
Reopen the Time class
Using Ruby's open classes feature, a few lines of code are enough:
class ::Time
class << self
attr_accessor :dummy
alias really_new new
def new
if dummy
dummy.dup
else
really_new
end
end
alias now new
end
end
Now it's possbile to create a Time instance like before: t = Time.new.
To freeze the time at t, you can just assign Time.dummy = t.
Now every other call to Time.new or Time.now returns a duped copy of t. This freezes the time for all classes, that rely on
those two methods. It's still possible to create an instance for the current time by calling Time.really_new, though. After testing is done, it's also a good idea to assign Time.dummy = nil, to let the flow of time continue.
Casting timestop now works, what about time travel?
Time travel is easy as well, just use Time.dummy += 5 to travel five seconds into the future. This is trivial even in reality - Time travel into the past isn't as easy or it isn't possible at all in the real world. In Ruby you can just call Time.dummy -= 5 to go back in time for five seconds. We have seen now, that Ruby makes casting a level 9 wizard spell like Timestop a piece of cake. The next time let's tackle Isaac's Greater Missle Storm.