Rubinius for the Layman, Part 2: How Rubinius is Friendly

This is part two of an ongoing series about Rubinius:

In this shorter second installment, I’ll present the ways in which Rubinius will be friendly to your multiple personalities:

  • you, the programmer;
  • you, the (potential) contributor.

Rubinius is programmer-friendly

Backtraces

The first reason why I consider Rubinius more programmer-friendly is the better backtraces. I’ll show some examples run from each interpreter’s interactive console.

Take the following piece of buggy code. Notice the nil at the end of the array.

ary=['bob', 'mom', nil].inject([]) {|result, element| result << element.to_sym}

With MRI you get the following:

NoMethodError: undefined method `to_sym' for nil:NilClass
        from (irb):5
        from (irb):5:in `inject'
        from (irb):5:in `each'
        from (irb):5:in `inject'
        from (irb):5

Ok, we have basic information on all the method calls that led to the NoMethodError.

Rubinius goes a step further, however:

NoMethodError: No method 'to_sym' on an instance of NilClass.
   from Kernel(NilClass)#to_sym (method_missing_cv) at kernel/core/kernel.rb:606
   from Object#irb_binding {} at (irb):4
   from Enumerable(Array)#inject {} at kernel/core/enumerable.rb:375
   from Array#each at kernel/core/array.rb:573
   from Enumerable(Array)#inject at kernel/core/enumerable.rb:371
   from Object#irb_binding {} at (irb):4

The backtrace tells me in which module each method on the stack was defined!

Q: How awesome is that?

A: Very!

I’m almost looking forward to debugging buggy mixins or method chains. Aren’t you?

These backtraces will definitely come in handy when debugging in situations where a lot of magic is happening behind the scenes.

Bring back the horse!

There’s another interesting detail about the backtraces. Remember what language Rubinius is written in? Heh. The backtraces Rubinius generates go deeper.

Say we have another piece of buggy code:

s = 'ahh'
s[1] = nil

MRI:

TypeError: can't convert nil into String
 from (irb):5:in `[]='
 from (irb):5

Rubinius:

TypeError: Coercion error: nil.to_str => String failed:
(undefined local variable or method `to_str' for nil)
   from Type.coerce_to at kernel/core/kernel.rb:19
   from Kernel(String)#StringValue at kernel/core/kernel.rb:80
   from String#splice! at kernel/core/string.rb:2192
   from String#[]= at kernel/core/string.rb:360
   from Object#irb_binding {} at (irb):8

Not only is the message more descriptive, but as you can see, we can see deeper into the interpreter’s inner workings.

The argument could be made that this could lead to noisier backtraces. On one hand it’s true that adding 5 lines to a 30+ lines backtrace generated in a Rails unit test will rarely help. On the other hand, all this additional information may sometimes help finding subtle bugs (in user code or in Rubinius) or figure out that our use of a feature is not quite correct.

I say the additional information is very welcome. And to help keep the information overload in check, let’s get off our asses and use the quiet_backtrace gem by James Golick and Dan Croak.

The real Rubinius backtraces

It gets even better. The previous examples were only run in irb. So the additionnal information was getting squeezed in IRB’s exception formatting. Say I save both pieces of buggy code to 1.rb and 2.rb. You’ll notice too that I split program 2 in 2.rb and /lib/2.rb (I plan on making it a gem).

Here’s in full colored glory the insults Rubinius will throw back at me:

mat@laptop rubinius $ rbx 1.rb
An exception has occurred:
    No method 'to_sym' on an instance of NilClass. (NoMethodError)Backtrace:
    Kernel(NilClass)#to_sym (method_missing_cv) at kernel/core/kernel.rb:612
                        Object#__script__ {} at 1.rb:1
                 Enumerable(Array)#inject {} at kernel/core/enumerable.rb:375
                                  Array#each at kernel/core/array.rb:573
                    Enumerable(Array)#inject at kernel/core/enumerable.rb:371
                           Object#__script__ at 1.rb:1
                    CompiledMethod#as_script at kernel/core/compiled_method.rb:326
                         Compile.single_load at kernel/core/compile.rb:238
                 Compile.load_from_extension at kernel/core/compile.rb:310
                           Object#__script__ at kernel/loader.rb:190

mat@laptop rubinius $ rbx 2.rb
An exception has occurred:
    Coercion error: nil.to_str => String failed:
(undefined local variable or method `to_str' for nil) (TypeError)
Backtrace:
                 Type.coerce_to at kernel/core/kernel.rb:19
     Kernel(String)#StringValue at kernel/core/kernel.rb:80
                 String#splice! at kernel/core/string.rb:2192
                     String#[]= at kernel/core/string.rb:360
              Object#__script__ at ./lib/2.rb:2
       CompiledMethod#as_script at kernel/core/compiled_method.rb:326
            Compile.single_load at kernel/core/compile.rb:238
        Compile.unified_load {} at kernel/core/compile.rb:149
                     Array#each at kernel/core/array.rb:573
           Compile.unified_load at kernel/core/compile.rb:120
         Kernel(Object)#require at kernel/core/compile.rb:450
              Object#__script__ at 2.rb:1
       CompiledMethod#as_script at kernel/core/compiled_method.rb:326
            Compile.single_load at kernel/core/compile.rb:233
    Compile.load_from_extension at kernel/core/compile.rb:310
              Object#__script__ at kernel/loader.rb:190

Holy f**** sh Batman! How centering the lines makes a difference!

To the left is the logical location: the class/module and the method name. To the right is just the necessary information about the physical location. Notice the paths. They only contain what you need i.e. the relative path to execution – relative to the VM for Rubinius code, and relative to the execution root of my glorious code.

Don’t tell anyone, but I think I’ll intentionally put in bugs at work. Just to see these in the logs! Too bad our logs don’t support coloring.

Rubinius is contributor-friendly

Commit access

The Rubinius project is friendly for the programmer who uses it. But the project itself is also friendly to potential contributors. They currently have a policy called the free-flowing commit bit. This policy says the following:

Anyone who submits a patch which is accepted is granted commit access.

In case you missed it, here it is again:

Anyone who submits a patch which is accepted is granted commit access.

This is not crazy talk. The patch can be as simple as you want. A correction to documentation or a spec for the behavior of a Ruby class not behaving properly under Rubinius. Any form of submission is admissible: a pastie, a ticket prepended with [PATCH] on lighthouse, or any other traditional way (emails, ransom notes etc.) Of course these methods are only temporary, since you’re going to get commit access afterwards.

The idea in being so liberal with commit access is to put the barrier to contribution as low as possible. This way, when your commit bit is set, you’re going to be tempted to help more. And your next contribution is going to be so much easier to submit.

A masterful adaptation of the timeless ‘gateway drug’ style of recruiting, really. Get them hooked with as little friction as possible. Then let them, uhhh, flourish in the wonderful world they just discovered.

How’s that for being contributor-friendly? This may not last forever, I guess. So hurry up and submit a patch! ;-)

The naysayers might once again intervene to remind us that this isn’t going to scale. Well it’s every open source project’s dream to have too many contributors. The community and the team will adapt in time. The fact that Rubinius is hosted with Git also gives the team much more flexibility to try different committing strategies, or simply gives more power to recover from mistakes from newcomers.

To really have a better understanding of Evan’s vision of the Rubinius community, I highly recommend watching the video of his presentation at MountainWest RubyConf. The presentation’s content is really not the same as the other presentation mentioned in my first article. Both are really worth it.

Conclusion

That’s it for now, folks. This is how much Rubinius will be friendly to you. Delicious stacktraces and a very welcoming project. A project ready to give you commit access if you submit anything good: a spec, some doc, or just a plain ol’ bug fix.

Stay tuned for the next installment, in which I’ll start covering a few mildly technical bits of Rubinius. Unless I change my mind and decide to tackle another aspect of Rubinius.

A few references:

  • The links for the videos are 3 paragraphs up, you lazy reader. No wait, I’m the one being lazy here.
  • How to write a ticket for the Rubinius project.
  • Let’s bug James Golick and Dan Croak so they make sure quiet_backtrace is ready for these deep Rubinius stack traces. Ha! Sorry James :-)

Comments