skip to content

In config/routes.rb, how does resource :profile differ from resources :profiles in routes, helpers and the controller it calls?

level: middleimportance: should knowfreq 55%

answer

  1. no index, no :id
  2. six actions, not seven
  3. plural controller name
  4. profile_path takes no argument
  5. record comes from the signed-in user

basics

~20 s

resource :profile draws six routes on /profile with no :id and no index, names helpers profile_path, new_profile_path and edit_profile_path, and still routes to the plural ProfilesController; the action finds the record from context, not params.

solid answer

~30 s

`resource :profile` declares a **singular resource**: there is one profile per visitor, so the URL never carries an id. It draws `GET /profile` (`show`), `GET /profile/new`, `POST /profile` (`create`), `GET /profile/edit`, `PATCH`/`PUT /profile` (`update`) and `DELETE /profile` (`destroy`), with no `index`. The helpers are `profile_path`, `new_profile_path` and `edit_profile_path`, none of which needs an argument. The trap is the controller: it is still the plural `ProfilesController`, so the same controller can serve both a singular and a plural resource. Because there is no `params[:id]`, the controller loads the record from the request's context, typically the signed-in user's profile.

code

ruby · 8 lines
ruby
# config/routes.rb
Rails.application.routes.draw do
  resource :profile, only: [:show, :edit, :update]
end

# GET   /profile       -> ProfilesController#show    profile_path
# GET   /profile/edit  -> ProfilesController#edit    edit_profile_path
# PATCH /profile       -> ProfilesController#update  profile_path

go deeper

for a junior

Remember the shape: six routes on /profile, no index and no id, helpers without arguments, and the plural ProfilesController.

for a middle

Explain why the controller stays plural, why the record comes from the signed-in user rather than params, and when to choose the singular form.

for a senior

Use singular resources to remove ids from URLs that mean the current user, and pair them with plural admin routes where staff manage everyone's records.

for a principal

Decide where the app's 'mine' surfaces live and keep them singular by convention, so ownership is enforced by the route shape across teams.

## The idea behind a singular resource Most resources in an application are **collections**: many photos, each addressed by an id. Some things exist **once per visitor** from that visitor's point of view: the signed-in photographer's own profile, their account settings, their current session. Putting an id in those URLs would be redundant at best and, at worst, an invitation to try someone else's id. Rails expresses this with `resource` (singular) instead of `resources` (plural) in `config/routes.rb`. ```ruby Rails.application.routes.draw do resources :photos resource :profile end ``` ## The routes it draws | Verb | Path | Action | Helper | |---|---|---|---| | GET | `/profile/new` | `new` | `new_profile_path` | | POST | `/profile` | `create` | `profile_path` | | GET | `/profile` | `show` | `profile_path` | | GET | `/profile/edit` | `edit` | `edit_profile_path` | | PATCH / PUT | `/profile` | `update` | `profile_path` | | DELETE | `/profile` | `destroy` | `profile_path` | Compared with `resources :profiles`: - **No `index`**: there is no list to show, so six actions instead of seven. - **No `:id` segment**: every member route sits on `/profile` itself. - **No plural helper**: `profiles_path` is not defined; `profile_path` covers show, create, update and destroy and takes **no argument**. - `only:` and `except:` work as usual, validated against these six names. ## The controller name trap A singular resource still maps to a **plural controller**: `resource :profile` sends requests to `ProfilesController`, not `ProfileController`. Rails keeps controller names plural so that one controller can sit behind both forms (an admin area might use `resources :profiles` while photographers use `resource :profile`). When the controller lives elsewhere, the `controller:` option names it, exactly as on `resources`. ## Where the record comes from Because the URL carries no id, `params[:id]` is absent. The controller has to find the record from the **request's context** instead: 1. Authenticate the visitor, which an authentication layer or a `before_action` does. 2. Load the record through that identity, for example the current user's `profile` association. 3. Build it in `new` and `create` the same way, so a visitor can only ever create their own. That is the security win of the singular form: there is no id to tamper with, because the router never offers one. ## Mistakes interviewers listen for | Belief | What `resource :profile` actually does | |---|---| | It routes to `ProfileController` | It routes to the plural `ProfilesController` | | `profile_path(@profile)` is needed | `profile_path` takes no record; there is no id segment | | `/profile` lists profiles | `GET /profile` is `show`; there is no `index` | | The controller reads `params[:id]` | No id arrives; the record comes from the signed-in user | | `profiles_path` exists too | No collection route is drawn, so no plural helper either | ## When to choose it - **Use `resource`** when the URL means "mine" or "the current one": profile, settings, session, the current cart, a dashboard. - **Use `resources`** when the same visitor may address many records, or when an administrator addresses other people's records by id. - **Use both** when two audiences need different shapes, often with the plural version inside an admin namespace. ## Edges worth knowing - `only:` and `except:` are checked against the six singular actions, so `resource :profile, only: [:index]` raises `ArgumentError` when the routes load. - The singular form also takes the other options of `resources`: `path:` for a different URL word (`resource :profile, path: "about-me"`), `as:` for different helper names and `controller:` for a different class. - In an app generated with `--api`, the singular form drops `new` and `edit` as well, leaving `show`, `create`, `update` and `destroy`. - Record-based URL generation, such as passing a `Profile` instance to a form or a redirect, does not find a singular route on its own and needs an extra routing declaration; that belongs to the path-helpers topic rather than to the `resource` line. - Nesting under a singular resource is possible, but its children's paths contain no parent id either.

  • Can ProfilesController serve both resource :profile and resources :profiles?
    Yes. Both forms map to `ProfilesController` by default, so one controller can answer `/profile` and `/profiles/:id`. In practice the two audiences differ, so the plural form usually goes in an admin namespace with its own controller, and each loads records differently: by `params[:id]` there, by the signed-in user here.
  • Why is resource :profile safer than resources :profiles for a user's own profile?
    The singular routes carry no id, so there is nothing for a visitor to change in the URL. The controller loads the profile through the signed-in user, which makes another person's profile unreachable by construction instead of depending on an authorization check that someone could forget.

A hotel's "my room" button at reception works without a room number: the desk knows who is asking. A singular resource is that button; the plural resource is the full list of numbered rooms the manager can open.

saying these in an interview costs you the question

  • resource :profile routes to a ProfileController class
  • A singular resource still has an index action at /profile
  • profile_path needs the profile record or its id as an argument
  • The controller reads params[:id] to find the profile
  • resource :profile also defines profiles_path for the collection