Fighting Rails RESTful Routes Conventions

Rails Fans,

Back in the 2.x days I converted my personal site to Rails. I eventually got tired of fighting Rails conventions and tried other options. I'm now back and giving Rails another look. I've been tinkering with another personal site and have it partially implemented in both Rails 3 and Django. I'm sure some of my preferences will make most Rails developers cringe. I tend to try to CamelCase everything. Also, Rails RESTful routes don't quite make sense to me. Logic would dictate that collection routes should be plural and member routes should be singular, instead of them both being plural. Using recipes as an example, my personal preference would be for:

/Recipes

to be the index with a list of recipes. For viewing a single recipe, using a slug, it would make more sense for the route to be something like:

Recipe/Beef-Brisket

instead of:

Recipes/Beef-Brisket

Is there a way to configure RESTful routes to use plural collection routes and singular member routes? For the moment I have all of my routes mapped separately via match statements but I'd like to find an easier way, especially if/when I get to a point where nested resources would come in handy.

I don’t think you’ll find much information about that. REST is one of the core principles of Rails.

It can probably be done by hacking parts of ActionDispatch/Routing/Mapper, or by writing a gem to do so.

I also think the default routes make a lot of sense…

The way I see it - if you were to create a folder on your computer to store recipies, what would you call it? Recipe or recipes? I’d call it recipes since it holds more than one recipe. Then under there, you’d have each of your recipes as a file/folder. RESTful routes are designed to emulate that.

And is the case sensitivity that big of a deal? You could probably also do this by hacking ActionDispatch.

Might want to start your search by looking at the Resouces module in action_dispatch\routing\mapper.rb

The way I see it - if you were to create a folder on your computer to store recipies, what would you call it? Recipe or recipes?

I'd create a directory, and call it Recipes.

I'd call it recipes since it holds more than one recipe. Then under there, you'd have each of your recipes as a file/folder. RESTful routes are designed to emulate that.

If that's how I was storing my recipes, that would make some sense. But, I'm storing them in a database, so I have a little more flexibility in how they're presented. If you were talking about a beef brisket recipe, would you call it a beef brisket recipe, or a beef brisket recipes? In that context, which of:

/Recipes/Beef-Brisket

/Recipe/Beef-Brisket

seems more natural?

And is the case sensitivity that big of a deal?

If I give in to the all plural routes convention, the case sensitivity issue isn't a big deal at all. Using my CamelCase preferences is as simple as:

  scope :path_names => { :new => "New", :edit => "Edit" } do

      resources :movies, :path => "Movies"

  end

After sending my last reply, I thought of another way of looking at the above URLs. Using slugs and Rails plural default, and my CamelCase preferences, a single recipe for beef brisket would be displayed at:

/Recipes/Beef-Brisket

But, looking at the above URL, folks not knowing about Rails defaults would expect to find two or more recipes for beef brisket at the above URL. With the singular:

/Recipe/Beef-Brisket

it's a little clearer that it's for a single recipe.

I don't think it matters if the conventions make sense or not. If possible, you should try to use them for the following reasons:

1) If another programmer who is familiar with Rails starts maintaining your site, they will be annoyed that they have to figure out what you did instead of already knowing how it works.

2) If you have a subtle problem and ask this list, no one will have any idea how to fix it because no one else will have done it your way.

3) If you start maintaining another Rails site, you will have to learn the convention anyway, so why learn two conventions?

4) The next version of Rails might change something that breaks your site.

Phil

In that context, which of:

/Recipes/Beef-Brisket

/Recipe/Beef-Brisket

After sending my last reply, I thought of another way of looking at

the above URLs. Using slugs and Rails plural default, and my

CamelCase preferences, a single recipe for beef brisket would be

displayed at:

/Recipes/Beef-Brisket

But, looking at the above URL, folks not knowing about Rails defaults

would expect to find two or more recipes for beef brisket at the above

URL. With the singular:

/Recipe/Beef-Brisket

it’s a little clearer that it’s for a single recipe.

I guess I’ve never looked at it that way. To me, I look at the url “recipes/beef-brisket”, and I assume I am in the part of the website which lists recipes, and that I am looking at one particular recipe, the one for beef-brisket. That seems perfectly natural, even without familiarity with rails conventions.

I can understand having preferences, but I’m not certain I see the worth in fighting all the Rails conventions; if I were to switch to django or to cake-php or (something else), I’d think it would make more sense for me to adopt the conventions of those frameworks than to make my life more difficult by spending half my time working around the default assumptions… But that’s just me. :slight_smile:

I’ll chime in on this thread a little –

As someone learning Rails, the most frustrating thing is not that these conventions exist - it’s that it’s very hard to figure out what those conventions are through anything but experimentation and banging one’s head against it.

This is where I think the documentation could help more. The guides, railscasts, and whatnot are good for figuring out how one piece works, or how to do one particular thing - but it’s hard to get a notion of the big picture, exactly how all the pieces hook together, and what customization points are available to change things.

Something like “The default ActionController::TestCase class assumes that it’s testing a class with the same name as it, but with the word “Test” lopped off the end. To change this, do x.” But all in one place, not littered through API docs.

Good point. Another take on the above might be that one should try a few frameworks and find a framework with conventions that mesh well with one's preferences. That's the reason I moved on from Rails the last time I tried it.

I didn't see anyone in this thread answer your actual, original question on here so I figured I'd chime in. I'm not familiar with the Rails 3 routes syntax yet, but I think in Rails 2.x this should be as simple as:

  map.resources :recipes   map.connect 'Recipe/:id', :controller => 'recipe', :action => 'show'

That should tie the route you want into the conventional controller method, which should allow both to exist in harmony, which means you can use all the normal conventions for restful items from the controller down; all this should do in effect is point your ~custom~ routing URL to the RESTful show action. Note that a few things I haven't shown which you'll likely want are:

1) An override on def self.find for the Recipe model to search by ID or by permalink 2) An override of to_param to output the name instead of ID for permalinking. 3) Possibly a helper to generate your desired URLs in your views, such as def permalink_recipe_path(name) ...; I haven't tried it but you might be able to name the route above to accomplish this, something like

  map.permalink_recipe 'Recipe/:id', :controller => 'recipe', :action => 'show'

But I'm not sure how exactly that works with passing in a parameter. I'd start with the URL mapping, and see when the normal redirects and what not get you to a URL you don't like.

\Peter

\Peter,

I didn't see anyone in this thread answer your actual, original question on here so I figured I'd chime in. I'm not familiar with the Rails 3 routes syntax yet, but I think in Rails 2.x this should be as simple as:

  map.resources :recipes   map.connect 'Recipe/:id', :controller => 'recipe', :action => 'show'

Actually, I think I just found the trick for Rails 3 in section 2.5 of the routing guide - Singular Resources. It looks like RESTful routes tailored to my preferences are as simple as:

      resource :recipe, :path => "Recipe"

      match 'Recipes' => 'recipes#index', :as => :recipes, :via => :get

I had tried using singular resources before but mistakenly typed resources instead of resource.

Hmm, mapping a singular resource should expose routes that don't accept an ID, which isn't really what you want; in other words there should be /recipe which points to the singular recipe in the system, but in reality you have multiple recipes and want to take an ID. I'm not sure what that match line is doing so perhaps that's patching things up, but I'd be worried you'll hit issues like recipe_path won't expect an ID (since its a singular route). Although experimentation is the king, so if its working go with it; if you hit ~unexpected~ issues then this might be the cause.

2c, \Peter