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