Ruby on Struts

September 6, 2008

Once upon a time, there was a web MVC framework called Struts. Struts was one of the original catalysts of web frameworks based on the MVC pattern, but it was written in Java and required copious amounts of XML to configure your application. One of the many things defined in the XML were the "Action Mappings". An Action Mapping essentially mapped a specific URL pattern to a specific Java class that would be responsible for handling that request.

Then came Ruby on Rails, which eliminated the need for these XML configuration files by using Convention Over Configuration. The way this works in Rails is that if a request is sent to the url /users/new, Rails will call the new method of the UsersController class to handle the request. Rails has a feature called routing that is used to map unconventional url patterns to specific controller actions. Then came RESTful Rails and the convention over configuration was gone.

With RESTful Rails, the request path simply represents what you want to perform an operation on, the Resource, and the HTTP method specifies what you want to do. This all makes sense, but one problem is that RESTful urls do not conform to the url convention. This means every action must be defined in the routing. A shortcut was added to the routing to allow one expression to define the 7 typical methods the controller for a resource will have, but any additional actions must be explicitly defined.

I've been recently working with an experienced Java developer with some familiarity Rails. He was confused by the way RESTful routing works and what paths the named route methods would generate. When he asked what benefits all this provides over the original /controller/action/id pattern, where only non-standard routes had to be mapped, I struggled to find any. I realized that I was doing this just because it was now the "Rails Way". He said this reminds him of Struts, and after some arguing and thinking about it, I realized he was right.

So it seemed unbelievably coincidental that we both found ourselves together today in a talk titled Unconvental Wisdom by Bruce Tate. In today's talk, Bruce pointed out that RESTful Rails adds complexity to Rails, which makes it harder to explain how Rails works to newcomers to Rails. I'm very interested to see how this talk is received by the Rails community, so if you weren't at the talk today, look for it on Confreaks in the next few weeks.

So looking back on RESTful Rails applications that I've developed, if they reach even a level of medium complexity, you end up with 50 lines or so in the routes.rb, with at least a handful of nested and custom routes, in addition to all of the resources. Imagine this as the admin interface for a simple blog:

map.namespace(:admin) do |admin| 
  admin.resources :articles, :has_many => :comments, :member => { :publish => :post }
  admin.resources :categories, :has_many => :articles
  admin.resources :comments, :belongs_to => :article
  admin.resources :tags, :has_many => :articles
end

If you are an experienced Rails developer, you can decipher this right away. But is this really easy understand? Or wouldn't convention over configuration be easier:

map.connect "/admin/:controller/:action/:id"

And what about named routes like new_admin_article_comment_path(@article)? Is that really more clear than admin_path("/comments/new", :article_id => @article)"? Do you really care if the urls are /comments/new?article_id=1 or /articles/1/comments? Maybe I missing something, but I'm starting to like the sound of idea of having almost nothing in my routes and not calling dynamically generated methods everywhere to just to build simple paths. After all, no code is faster than no code.

Posted in Technology | Tags Ruby, Rails, REST

Comments

1.

Hi,

for me admin_path("/comments/new", :article_id => @article) isn't DRY, if someday you want modify the url 'comments/new' you're screwed (it's not the case with named routes, where you can just redefine map.new_admin_article_comment). And I really care if the urls are /comments/new?article_id=1 or /articles/1/comments, it's very very important :-)

# Posted By Thibaud Guillaume-Gentil on Saturday, September 6 2008 at 3:12 AM

2.

+1, it does matter what URL do you have. I don't like /comments/new?article_id=1, it's ugly

In your blog you have URLs like http://paulbarry.com/articles/{year}/{month}/{day}/{permalink}
Why? Why not http://paulbarry.com/articles?id=111 ?

URLs looks better without question marks, believe me. Also I agree with Thibaud, when you decide to change URLs, you will have to change them in a single place.

# Posted By Dmytro Shteflyuk on Saturday, September 6 2008 at 3:53 AM

3.

In February I was busy trying to understand and convert a coworker of mine to RESTful Rails, because I wanted to stay at the top of the curve and keep track of these things. And besides, RESTful Rails seemed to provide a number of important benefits.

I became familiar with make_resourceful and started using that in my projects. I was really pleased, because I could replace ~100 line controllers with ~20 line controllers, nevermind that I always wound up overriding current_objects and certain other methods to get it to do what I want.

This past week I found myself working on an old Rails 1.2 project, maintaining some things and I needed to add an RSS feed. I started putting all that RESTful stuff into the controller and the routing and then I thought, what the hell, I'll just make a little method and a little template. Twenty minutes later, it's completely done and linked to from the main template. How cool was that! And then I sat there thinking, wait a second, have I ever really *used* the fact that my RESTful methods could handle different "types" of responses? Or have I only ever used it to handle HTML this whole time?

So I think the one case where it would really have been handy to have been using REST came and left. I used respond_to inside a regular method and there still are no resources in this application. Reading this blog entry certainly makes me wonder. I had been thinking about how I need to spend some time modernizing this application, how I should go through and stick make_resourceful inside all of the controllers and really fatten up that routing. And yeah, it's probably a savings to remove 80 lines of code from 10 or 12 controllers and add 50 to the routing. But what about all those links? My app is going to have deeply nested resources. Already in another app, I had things like edit_admin_user_role_permission_path. Is that really cleaner than :controller => '/permissions'?

So your article really resonates with me. REST is great if you're building an API, but aren't we mostly still building websites? How much more work should we be willing to do? And why do we do things that are "correct" when they make other things so much worse?

I had been investigating the other Ruby frameworks and discounting some of them because of a general lack of REST, but maybe REST isn't what I need. And I had encountered an error relating to REST (I had an ID with a period in the name and it wanted to break that into an :id and a :format parameter). Maybe I'll just scrap REST for the time being and forget about the pretty URLs. Or make the pretty URLs without using map.resource.

# Posted By Daniel Lyons on Saturday, September 6 2008 at 4:02 AM

4.

I spent most of my time working on an Rails app originally developed for 1.2, and hence theres no RESTfulness. At various times on other projects I've tried to use the REST style but always come back to it seeming more complicated than my companies 'old' rails app. I came to rails originally because I liked the simplicity and neatness - REST definately seems to take some of this away.

As for pretty URLs, this easily is solvable for the important user facing URLs in the routes.rb file - and you get the URLs you want then as well.

# Posted By Jebw on Saturday, September 6 2008 at 5:39 AM

5.

The nice thing about the RESTful routing is once you start using polymorphic URL helpers, and liberal application of to_s and to_param, along with nested resources. You can end up with some wickedly beautiful URLs, along with nice and clean template code generating your links.

Suddenly, your links are only

That'll use to_s for the text of the link, and it'll find the route for a the article, and then its nested comment.

You don't have to know or care about the names of your routes.

Same goes for the RESTful actions. To delete a comment, you do something similar to

:delete %>

Nice and smooth.

# Posted By Bob McWhirter on Saturday, September 6 2008 at 9:23 AM

6.

@Jebw

I agree, use routing to define pretty URLs for public facing things, but depending on what your app does, there may be lots of URLs that have no benefit to being pretty, because they are only seen by administrative users.

# Posted By Paul Barry on Saturday, September 6 2008 at 9:51 AM

7.

I'll admit that using RESTful controllers does fatten up your routing a bit, but I think that ultimately this makes the entire app come closer to "convention over configuration", not farther away from, because in all your controllers you have (mostly) the same set of methods and you can depend on them all to do the same thing to the resources they are in charge of. Maybe there is a bit more of a learning curve (in fact, I know there is), and I'm not saying you want to use it for everything (I have several ajax-y actions in my app that I don't think really relate to resources, and so I use a traditional rails route for them), but ultimately I think the extra layer is a net gain.

# Posted By Ethan Vizitei on Saturday, September 6 2008 at 11:30 AM

8.

Ironic. I had the same reaction to RESTful Rails but only as a found the routing annoying when I first saw it. Glad that you captured this

BTW, your blog hates iPhone

# Posted By Evan Light on Saturday, September 6 2008 at 9:59 PM

9.

NM. It hates NetNewsWire on iPhone.

# Posted By Evan Light on Saturday, September 6 2008 at 10:01 PM

10.

Hey Paul, your article really resonated with me. As previous commenters have mentioned, RESTful routes are great when you need a standard resource-based API (which we commonly do as we do a lot of Rails CMS's with Flex front-ends, and this works wonderfully). However, I find myself often needing to leave the confines of RESTful controllers whenever I need to do something.....well, interesting.

To illustrate (with a very basic example), let's look at reporting interfaces. A basic report takes some user inputs and returns an output. We're really talking about two actions. I suppose you could squeeze this into the 'new' and 'create' actions, but really, this is bastardizing the tenets of REST. Furthermore, I have no need whatsoever for edit/update/destroy/index/show. I suppose this is handled by a combination of RESTful and default routes.

But I really was happy to see someone else put this out there, as it has bothered me a bit as well. I guess I just feel that there's a lot of hoopla around the idea of "everything should be RESTful" that seems to be a little misguided. Then again, maybe I'm just misinterpreting the community consensus....

# Posted By John Trupiano on Sunday, September 7 2008 at 10:58 AM

11.

I don't think Rails prevents you from doing unRESTful routings. You can still fall back to what you use to do.

# Posted By Fadhli on Sunday, September 7 2008 at 10:19 PM

12.

RESTful routing in Ruby on Rails breaks:
a)Convention over Configuration principle and
b)Don't Repeat Yourself principle.

RESTful URLs are good but someone needs to come up with a smarter routing implementation that uses route mapping conventions.

Current implementation of RESTful routing is particularly severe on beginner Rails programmers.

# Posted By tabrez on Tuesday, September 9 2008 at 8:05 AM

13.

I totally agree with your thoughts on this.

REST is overhyped. It's annoying that most of the Rails community blindly follows the newest convention without even asking why.

My two major apps are non-REST and doing just fine.

# Posted By Seth on Tuesday, September 9 2008 at 9:27 PM

14.

I like REST and RESTful routing. You can't shoe horn every problem on top of if it, so you can choose where you want to me.

As for admin_path("/comments/new", :article_id => @article)... this isn't very clear. And worse you now have a hard coded path in your application.

# Posted By bryanl on Friday, September 12 2008 at 7:09 AM

Comments Disabled