Send my love to Ruby

20 September 2007

Over at the Rubinius project, in between hatching plots to take over the world, we fit in some time for recreation. For example, we’ve got this masochistic interest in writing RSpec compatible specs for the Ruby core library. One of the challenges there is the large number of aliased methods that Ruby has. Using RSpec’s shared behaviors as an example, I’ve created a flavor of shared behaviors in our mini_rspec implementation. As the code below shows, this makes it straightforward to spec all these aliases.

 1 hash_store = shared "Hash#store" do |cmd|
 2   describe "Hash##{cmd}" do
 3     it "associates the key with the value and return the value" do
 4       h = { :a => 1 }
 5       (h.send(cmd, :b, 2).should == 2
 6       h.should == {:b=>2, :a=>1}
 7     end
 8 
 9     it "duplicates and freezes string keys" do
10       key = "foo"
11       h = {}
12       h.send(cmd, key, 0)
13       key << "bar"
14 
15       h.should == { "foo" => 0 }
16       h.keys[0].frozen?.should == true
17     end
18 
19     it "duplicates string keys using dup semantics" do
20       # dup doesn't copy singleton methods
21       key = "foo"
22       def key.reverse() "bar" end
23       h = {}
24       h.send(cmd, key, 0)
25 
26       h.keys[0].reverse.should == "oof"
27     end  
28 
29     it "raises TypeError if called on a frozen instance" do
30       should_raise(TypeError) { hash.send(cmd, 1, 2) }
31     end
32   end
33 end
34 
35 describe "Hash#[]=" do
36   it_behaves_like(hash_store, :[]=)
37 end

The very cool thing about this is how useful Ruby’s send method is. And in Rubinius, it gets even cooler, as you’ll see in part II