Mel
Mel

Reputation: 2693

Rails 5 - nested routes - uninitialised constants for controllers

I'm trying to figure out how to nest routes in Rails 5 (so that related controllers are kept together.

I have my controllers file tree set up as:

 app/controllers/users

In that folder, I have controllers for:

identities_controller.rb
app_roles_controller.rb

Each of those controllers is saved as:

class Users::IdentitiesController < ApplicationController
class Users::AppRolesController < ApplicationController

My routes file has:

resources :app_roles,
    :controllers => {
      :app_roles => 'users/app_roles'
    }

  devise_for :users,
             :controllers => {
                :sessions => 'users/sessions',
                :registrations => "users/registrations",
                :omniauth_callbacks => 'users/omniauth_callbacks'
              }

  resources :identities, 
    :controllers => {
        :identities => 'users/identities'
    }

  resources :users

In my views folder, all of the files are top-level. Im unclear as to whether I need to group them in the same way that I do my controllers.

When I save all of this and try to navigate to http://localhost:3000/app_roles#index, I expect to go to my app/views/app_roles/index.

Instead, I get an error that says:

app_roles
uninitialized constant AppRolesController

When I rake routes, I get:

rake routes | grep app_roles
                       app_roles GET      /app_roles(.:format)                    app_roles#index {:controllers=>{:app_roles=>"users/app_roles"}}
                                 POST     /app_roles(.:format)                    app_roles#create {:controllers=>{:app_roles=>"users/app_roles"}}
                    new_app_role GET      /app_roles/new(.:format)                app_roles#new {:controllers=>{:app_roles=>"users/app_roles"}}
                   edit_app_role GET      /app_roles/:id/edit(.:format)           app_roles#edit {:controllers=>{:app_roles=>"users/app_roles"}}
                        app_role GET      /app_roles/:id(.:format)                app_roles#show {:controllers=>{:app_roles=>"users/app_roles"}}
                                 PATCH    /app_roles/:id(.:format)                app_roles#update {:controllers=>{:app_roles=>"users/app_roles"}}
                                 PUT      /app_roles/:id(.:format)                app_roles#update {:controllers=>{:app_roles=>"users/app_roles"}}
                                 DELETE   /app_roles/:id(.:format)                app_roles#destroy {:controllers=>{:app_roles=>"users/app_roles"}}

To me, I think these routes show that app_roles#index should go to the app/views/app_roles/index.html.erb via the controller in app/controllers/users/app_roles_controller.rb

I have the same issue with the identities resource.

GUESSES I tried moving the app/views/app_roles folder to be nested under the users folder (i.e. app/views/users), but I get the same error when I then try to go to the http://localhost:3000/app_roles#index to check if it works.

I also tried amending the routes file to:

resources :app_roles,
        :resources => {
          :app_roles => 'users/app_roles'
        }

By that, I mean that I changed the reference to :controllers, to :resources. It didn't work - I get the same error.

Can anyone see what I'm doing wrong?

Upvotes: 2

Views: 775

Answers (1)

max
max

Reputation: 102134

Routing to namespaced controllers

To route resources to a "namespaced" controller you can use the module option:

resources :identities, module: :users

Or scope module: which is useful when declaring multiple resources:

scope module: :users do
  resources :app_roles, module: :users
  resources :identities, module: :users
end

Which is much cleaner than specifying the controller manually which is really only done when you are overriding a library like Devise or when the controller and route names don't line up.

The termology here can be somewhat confusing. Just remember that the only thing a route does is match an incoming request with a controller. It does not influence how the controller does its job.

Looking up views

To me, I think these routes show that app_roles#index should go to the app/views/app_roles/index.html.erb via the controller in app/controllers/users/app_roles_controller.rb

Well thats not how it works. Rails looks up views based on the nesting of the controller class. Thus rails will look for the views for Users::IdentitiesController in views/users/indentities/.

If you want to break with the conventions you can explicitly render the views or prepend/append the view paths. However learn the conventions before you break them, they are actually pretty smart.

Note that your routes do not influence how your controllers lookup views. Thats just up to the module nesting - which is how the object is composed. And has absolutely nothing to do with the concept of nested routes.

"namespaces" and nested routes

The routes you are generating are not nested. A nested route would be for example:

POST /users/:user_id/identities

Which clearly describes the intent. To setup a nested routes for indentities you would do:

resources :users, shallow: true do
  scope module: :users do
    resources :identities
  end
end

shallow: true generates the individual routes without the users/:used_id/ prefix.

           Prefix Verb   URI Pattern                              Controller#Action
  user_identities GET    /users/:user_id/identities(.:format)     users/identities#index
                  POST   /users/:user_id/identities(.:format)     users/identities#create
new_user_identity GET    /users/:user_id/identities/new(.:format) users/identities#new
    edit_identity GET    /identities/:id/edit(.:format)           users/identities#edit
         identity GET    /identities/:id(.:format)                users/identities#show
                  PATCH  /identities/:id(.:format)                users/identities#update
                  PUT    /identities/:id(.:format)                users/identities#update
                  DELETE /identities/:id(.:format)                users/identities#destroy
            users GET    /users(.:format)                         users#index
                  POST   /users(.:format)                         users#create
         new_user GET    /users/new(.:format)                     users#new
        edit_user GET    /users/:id/edit(.:format)                users#edit
             user GET    /users/:id(.:format)                     users#show
                  PATCH  /users/:id(.:format)                     users#update
                  PUT    /users/:id(.:format)                     users#update
                  DELETE /users/:id(.:format)                     users#destroy

Upvotes: 1

Related Questions