When Rails Fails

rorFor the past years there has been a lot of hype around Ruby on Rails. It’s a modern web framework built on the dynamically typed Ruby language. It makes heavy use of Ruby’s meta-programming abilities to essentially create an internal domain-specific language for building web applications.

People love Rails, and rightfully so, but it’s not perfect. An often overlooked problem is debugging; what if programmers make mistakes? Sure, real programmers don’t make mistakes, especially ruby on rails programmers, but let’s say they do, for argument’s sake. What happens then? Is the bug easy to track down, or do we get buried in stack traces that only DHH understands?

To test this out, I followed Rails’ “Getting Started” guide and made “accidental” mistakes along the way, to see how the system would react.

Mistake #1: Route to a non-existing controller

In my routes.rb I made a typing mistake, referring to a non-existing controller:

map.root :controller => "homme"# should be "home"

Ideally I would get an error message saying “no such controller named ‘homme’”. Instead, this is the result when loading the root page of the application:

rails-fail-1

The error here is “Unintialized constant HommeController”. There are a few problems with this error message. The first is the name mangling: I typed “homme”, which is apparently turned into “HommeController”. I have no clue where I made my mistake, there’s a stack trace, but none of these files are mine, they’re all Rails framework files. Where did I make my mistake?

Mistake #2: mispell the symbol :controller in the mapping file

I misspell the symbol :controller as :controler 

map.root :controler => "home"

The result, when starting the server: 

rails-fail-2

This is actually not bad, it says :controller must be specified, so apparently it is missing. It would be better if it would have noted that the :controler symbol we used makes no sense, but still. Not bad.

Mistake #3: Linking to non-existent pages

Let’s link to a page that does not exist, from our erb code:

<h1>Hello, Rails!</h1> <%= link_to "My Blog", post_path %>

Where post_path, should have been posts_path, results in:

rails-fail-3

The full error got truncated there, this is the full message:

post_url failed to generate from {:controller=>”posts”, :action=>”show”} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: [“posts”, :id] - are they all satisfied?

As a newbie Rails user I’m thoroughly confused by this. The code that I editted is 100% generated by scaffolding commands. Why does it talk about “posts” when I said “post”, and what is this :id thing? Looking through my controller code, it turns out that there is in fact a post controller to be invoked here, namely the show action of the posts controller. Still, to a newbie, this message is thoroughly confusing. It is nice that Rails at least attempts to give a specific error message with suggestions on how to fix it, but to be honest, I don’t get it.

Mistake #4: misspelling:confirm

Another piece of view code: 

<%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %>

Note the misspelling of :confirm, there. If you spell it right, you’ll see the following window when pushing the “Destroy” link:

[![rails-fail-4](http://zef.me/wp-content/uploads/2009/11/rails-fail-4.png “rails-fail-4”)](http://zef.me/wp-content/uploads/2009/11/rails-fail-4.png)

However, with :confirm misspelled as :confrm, it is simply ignored, resulting in immediate deletion of this record, without any confirmation. Ouch.

Mistake #5: Mistyped property names in data model validation rules

I accidentally misspell the name of a property :name in my data model code:

class Post < ActiveRecord::Base   validates_presence_of :nam # should be :name end

When I now add or edit a post object through a form, I receive a validation error:

rails-fail-5

Nam? I don’t see a nam field.

Sure, the bug is tracked down fairly easily, but still it looks odd.

Mistake #6: Misspelled formats

In controllers you can specify different representations of your output, e.g. a HTML, JSON or XML representation. But what if you specify a format that does not exist, such as htm?

respond_to do |format|   format.htm # should be html   format.xml  { render :xml => @posts } end

This is what happens when this piece of code is invoked:

rails-fail-6

That message reads “uninitialized constant Mime::HTM”, this is slightly confusing because we’re talking about formats, not constants. Plus, apparently format is replaced with Mime:: or something and the “htm” is capitalized to “HTM”. In the long stacktrace (which I truncated for the reader’s sake) there is one mention of my code (the line that starts with /Domain) and indeed, this is the line where the mistake was made, still it’s pretty hard to find.

Mistake #7: mistyping of a symbol in the context of data binding

Consider this piece of code:

def create     @post = Post.new(params[:poost])     respond_to do |format|       if @post.save         flash[:notice] = 'Post was successfully created.'         format.html { redirect_to(@post) }         format.xml  { render :xml => @post, :status => :created, :location => @post }       else         format.html { render :action => "new" }         format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }       end     endend

Spotted the mistake? Let’s see what happens if we add a new post, which invokes this create action. First we fill in the form:

rails-fail-7a

We then push the “Create” button:

rails-fail-7b

Wha? Where did all my data go? Oh right, I misspelled :post as :poost in the second line of that controller code.

Conclusion

If I would poke around a bit more, I’m sure I would find a lot of similar examples. I’m not looking for explanations of this behavior, I get how it works. The thing I’m trying to demonstrate here is that poor error messages and very limited checking of your programs is a drawback of internal DSLs like Rails. It would be much nicer for the programmer if programs were analyzed statically giving domain-specific error messages, rather than obscure runtime exception stack traces that are complicated to decipher.

Update: My response to common criticisms.\ Update 2: When JBoss Seam Fails.\ Update 3: When Scala DSLs Fail.