73 lines
3.1 KiB
Markdown
73 lines
3.1 KiB
Markdown
|
---
|
|||
|
layout: post
|
|||
|
title: Singular and Plural Rails Routes for the Same Resource
|
|||
|
tag:
|
|||
|
- api
|
|||
|
- ruby-on-rails
|
|||
|
- restful
|
|||
|
---
|
|||
|
|
|||
|
Sometimes when building your API with Rails,
|
|||
|
[following best practices](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api)
|
|||
|
may seem difficult. I recently came across one of these cases and was tempted to
|
|||
|
take a shortcut. However, I held strong and eventually came to a quality RESTful
|
|||
|
solution — but not without issue.
|
|||
|
|
|||
|
I wanted to allow users of my API to quickly access their own user profile. Our
|
|||
|
application had already implemented a route to allow this via
|
|||
|
`GET /profiles/:id` and simply implemented with
|
|||
|
`resources :profiles, only: [:show]` in our routes configuration.
|
|||
|
|
|||
|
Unfortunately, our API users wanted to be able to access their profile without
|
|||
|
providing their ID. My first pass at resolving this was passed in a "fake
|
|||
|
resource" to accomplish this.
|
|||
|
|
|||
|
{% gist 1a056f8dca8931d8872c6cfefebb2d1a %}
|
|||
|
|
|||
|
But I had broken one of the RESTful best practices. /profiles/me is not an
|
|||
|
actual resource but we are pretending it is. So I looked to the
|
|||
|
[Rails routes documentation](http://guides.rubyonrails.org/routing.html) for
|
|||
|
guidance and came across
|
|||
|
[singular resources](http://guides.rubyonrails.org/routing.html#singular-resources).
|
|||
|
|
|||
|
> Sometimes, you have a resource that clients always look up without referencing
|
|||
|
> an ID. For example, you would like /profile to always show the profile of the
|
|||
|
> currently logged in user.
|
|||
|
|
|||
|
I should not have been surprised that my exact use case was cited!
|
|||
|
|
|||
|
Now we are back on track! I get to go back to my simple route declaration with
|
|||
|
`resource :profile, only: :show` and without changing my controller code at all.
|
|||
|
|
|||
|
But now I needed users to be able to access each other’s profiles. Again, the
|
|||
|
Rails documentation had me covered.
|
|||
|
|
|||
|
> Because you might want to use the same controller for a singular route
|
|||
|
> (/account) and a plural route (/accounts/45), singular resources map to plural
|
|||
|
> controllers. So that, for example, resource :photo and resources :photos
|
|||
|
> creates both singular and plural routes that map to the same controller
|
|||
|
> (PhotosController).
|
|||
|
|
|||
|
And our implementation stays clean.
|
|||
|
|
|||
|
{% gist e8d6641349e4d0ea7e68d22dd3755e9d %}
|
|||
|
|
|||
|
This was awesome until I needed to use path helpers. With this implementation,
|
|||
|
`profile_path(:id)` works as expected but `profile_path` does not. If the order
|
|||
|
is reversed in the routes configuration, `profile_path` will work and
|
|||
|
`profile_path(:id)` will not. This is the result of a bug in the Rails core that
|
|||
|
touches some pretty intricate code that is linked to other issues.
|
|||
|
[One has even been open for five years](https://github.com/rails/rails/issues/1769)!
|
|||
|
|
|||
|
And we can work around that one as well by
|
|||
|
[overriding the named helpers](http://guides.rubyonrails.org/routing.html#overriding-the-named-helpers).
|
|||
|
Passing as: to our resource definition creates the helpers with a new name.
|
|||
|
|
|||
|
Our final code is ready!
|
|||
|
|
|||
|
{% gist e9dcc4cd4bad89554fb01be6627c7b63 %}
|
|||
|
|
|||
|
In our application, we can reference a generic profile with `profile_path(:id)`
|
|||
|
while still having `current_profile_path` to direct the user to their
|
|||
|
own profile.
|