Topic: REST with all-singular identifiers

I'm playing around with the RESTful tutorial found in the Pragmatic Programmers Agile Web Development with Rails, second edition, page 410. I'm trying to work with only singular identifiers, and I'm running up against what appears to be a bug in Rails, but I'm very new at this, so I'm easily confused when things don't go smoothly, so maybe it's a problem with my stuff, not Rails.

First, I created two empty rails projects like this:

~ $ rails --version
Rails 2.0.1
~ $ rails restful_plural
...
~ $ rails restful_singular
...
~ $ mysqladmin -u root -p create database restful_plural_development
...
~ $ mysqladmin -u root -p create database restful_singular_development
...

After editing ~/restful_plural/config/database.yml and ~/restful_singular/config/database.yml to connect to my MySQL server, I then edited ~/restful_singular/config/initializers/inflections.rb to this:
Inflector.inflections do |inflect|
  inflect.uncountable %w( fish sheep article )
end

Next, I generated the RESTful scaffolds like this:
~$ cd ~/restful_plural
...
~/restful_plural $ ruby script/generate scaffold article title:string summary:text content:text
...
~/restful_plural $ rake db:migrate
...
~/restful_plural $ cd ~/restful_singular
...
~/restful_singular $ ruby script/generate scaffold article title:string summary:text content:text
...
~/restful_singular $ rake db:migrate
...

At this point I have two very basic Rails apps called restful_singular and restful_plural . They are identical, except that restful_plural uses the identifiers "article" and "articles" , while restful_singular only ever uses "article" in all contexts. Now, when I run script/server from restful_plural, everything works as expected. However, when I run the server from restful_singular, I get errors.

Running the restful_singular server, I point my browser to "http://localhost:3000/articles/new"
And I get this error message:
    No route matches "/articles/new" with {:method=>:get}
That's no surprise, since there's nothing called "articles" anywhere in that application.

Pointing the browser to "http://localhost:3000/article/new" gets me this error:

    ActionController::RoutingError in Article#new
    Showing article/new.html.erb where line #5 raised:
    article_url failed to generate from {:action=>"show", :controller=>"article"} - you
    may have ambiguous routes, or you may need to supply additional parameters for this
    route.  content_url has the following required parameters: ["article", :id] - are
    they all satisfied?
    Extracted source (around line #5):
    3: <%= error_messages_for :article %>
    4:
    5: <% form_for(@article) do |f| %>
    6:   <p>
    7:     <b>Title</b><br />
    8:     <%= f.text_field :title %>

Investigating further I look at the routes for each app.

restful_plural/config/routes.rb contains this:

ActionController::Routing::Routes.draw do |map|
  map.resources :articles
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

And restful_singular/config/routes.rb is this:
ActionController::Routing::Routes.draw do |map|
  map.resources :article
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

They're identical except for line 2, as expected.

Then I look at the routes that actually get generated:

~ $ cd ~/restful_plural
...
~/restful_plural $ rake routes
articles               GET  /articles             {:controller=>"articles", :action=>"index"}
formatted_articles     GET  /articles.:format     {:controller=>"articles", :action=>"index"}
                       POST /articles             {:controller=>"articles", :action=>"create"}
                       POST /articles.:format     {:controller=>"articles", :action=>"create"}
new_article            GET  /articles/new         {:controller=>"articles", :action=>"new"}
formatted_new_article  GET  /articles/new.:format {:controller=>"articles", :action=>"new"}
edit_article           GET  /articles/:id/edit    {:controller=>"articles", :action=>"edit"}
formatted_edit_article GET  /articles/:id/edit.:format {:controller=>"articles", :action=>"edit"}
article                GET  /articles/:id         {:controller=>"articles", :action=>"show"}
formatted_article      GET  /articles/:id.:format {:controller=>"articles", :action=>"show"}
                       PUT  /articles/:id         {:controller=>"articles", :action=>"update"}
                       PUT  /articles/:id.:format {:controller=>"articles", :action=>"update"}
                     DELETE /articles/:id         {:controller=>"articles", :action=>"destroy"}
                     DELETE /articles/:id.:format {:controller=>"articles", :action=>"destroy"}
                            /:controller/:action/:id         
                            /:controller/:action/:id.:format

~/restful_plural $ cd restful_singular
~/restful_singular $ rake routes
article_index           GET  /article              {:controller=>"article", :action=>"index"}
formatted_article_index GET  /article.:format      {:controller=>"article", :action=>"index"}
                        POST /article              {:controller=>"article", :action=>"create"}
                        POST /article.:format      {:controller=>"article", :action=>"create"}
new_article             GET  /article/new          {:controller=>"article", :action=>"new"}
formatted_new_article   GET  /article/new.:format  {:controller=>"article", :action=>"new"}
edit_article            GET  /article/:id/edit     {:controller=>"article", :action=>"edit"}
formatted_edit_article  GET  /article/:id/edit.:format {:controller=>"article", :action=>"edit"}
article                 GET  /article/:id          {:controller=>"article", :action=>"show"}
formatted_article       GET  /article/:id.:format  {:controller=>"article", :action=>"show"}
                        PUT  /article/:id          {:controller=>"article", :action=>"update"}
                        PUT  /article/:id.:format  {:controller=>"article", :action=>"update"}
                      DELETE /article/:id          {:controller=>"article", :action=>"destroy"}
                      DELETE /article/:id.:format  {:controller=>"article", :action=>"destroy"}
                             /:controller/:action/:id         
                             /:controller/:action/:id.:format


Well, there's a surprise. Rails seems to be using a completely different format for some of the route names. Where restful_plural has "articles" and "formatted_articles" , restful_singular has "article_index" and "formatted_article_index" . It seems to be doing this to prevent a name collision between the index and show actions. restful_plural names GET/articles{articles,index} as "articles", and GET/articles/:id{articles,show} as "article" . restful_singular names GET/article{article,index} as "article_index", and GET/article/:id{article,show} as "article" .

Weird.

But it doesn't seem to explain the error. The error message for "http://localhost:3000/article/new" says "article_url failed to generate from {:action=>show, :controller=>article}" . The only difference between these two rake listings seems to be the weird route names. Other than that, and the controller names being all singular or all plural, everything's the same. In both Rails apps, the named route "article" refers to the same action (show).

So now I'm stuck. At this point, I'm pretty sure there's a bug in Rails that relies on singular and plural forms of a word having two distinct spellings. I find pluralization of code to be a blight on the otherwise exceptionally well-crafted productivity tool. Why go to all that trouble, just to cause me all this trouble? It's like Microsoft Word's Clippy ("You seem to be trying to accomplish something") -- a waste of mental resources with no advantage in return -- but now it seems even the Rails team has tripped over it too. If it were up to me, I'd choose a naming convention for REST routes that's intuitive, memorable, and works every time (i.e. sheep_index..sheep_show or index_sheep..show_sheep, not sheep_index..sheep). But I think the problem is somewhere deep inside the Rails code, and I don't have enough knowledge to find it.

Any ideas here. Am I misunderstanding something, or is there a decent workaround for using RESTful Rails with always-singular naming conventions?

Loqi