Let's add some routes to our routes.rb
for cats
that will show all cats, show one cat, delete a cat, and edit a cat.
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
At this point, when we run rake routes
, we get the following:
Prefix Verb URI Pattern Controller#Action
cats GET /cats(.:format) cats#index
GET /cats/:id(.:format) cats#show
PATCH /cats(.:format) cats#update
cats_edit GET /cats/edit(.:format) cats#edit
DELETE /cats/:id(.:format) cats#destroy
Let's say we have:
cats
administrators
We want a way to distinguish your routes so an admin has additional functionality/control over your application.
For example, say we want http://localhost:3000/admin/cats
to show edit
/delete
buttons for each individual cat and only admins can get here.
We also want http://localhost:3000/cats
to show a list of cats (and anyone visiting our application can get here).
What can we do?
# config/routes.rb
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
scope :admin do
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
end
Adding scope
to our routes gives us the following when we run rake routes
:
Prefix Verb URI Pattern Controller#Action
cats GET /cats(.:format) cats#index
GET /cats/:id(.:format) cats#show
PATCH /admin/cats(.:format) cats#update
cats_edit GET /admin/cats/edit(.:format) cats#edit
DELETE /admin/cats/:id(.:format) cats#destroy
We're going to need a way to differentiate our controllers. We want what we already have (the url prefix) AND a separate controller to encapsulate the different functionality.
We want both /admin/cats
and /cats
to be handled by our controllers in different ways.
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
scope :admin, module: :admin do
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
end
If we have scope
with module
in our routes, we will get the following rake routes
output:
cats GET /cats(.:format) cats#index
GET /cats/:id(.:format) cats#show
PATCH /admin/cats(.:format) admin/cats#update
cats_edit GET /admin/cats/edit(.:format) admin/cats#edit
DELETE /admin/cats/:id(.:format) admin/cats#destroy
By using module
, Rails looks for our controller in a different place.
# When we hit "http://localhost3000/admin/cats"
# app/controllers/admin/cats_controller.rb
class Admin::CatsController < ApplicationController
def index
@cats = Cat.all
end
end
What does that ::
(scope resolution operator) remind us of?
Note: Where do you think Rails will look for this view template? It will look in the views/admin/cats
folder.
- What have we done so far to our routes?
- What did
module
change for us? - Do you notice anything missing when you run
rake routes
?
As you may have noticed, we don't have any path helpers that are specific to this "special" admin
prefix. Again, Rails can help us out with this.
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
scope :admin, module: :admin, as: :admin do
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
end
Let's run rake routes
once again!
Prefix Verb URI Pattern Controller#Action
cats GET /cats(.:format) cats#index
GET /cats/:id(.:format) cats#show
admin_cats PATCH /admin/cats(.:format) admin/cats#update
admin_cats_edit GET /admin/cats/edit(.:format) admin/cats#edit
admin DELETE /admin/cats/:id(.:format) admin/cats#destroy
So what does using scope
, module
, and as
provide for us?
- path helpers via the prefix (
admin_cats_path
) - controller prefix (
Admin::CatsController
) for more organization - url prefix for user's to see in their browser (
http://localhost:3000/admin/cats
)
As you may have expected, this seems like a lot of work for something that's used quite often. Rails actually makes this even easier for us.
namespace
= scope
+ module
+ as
Rad!
Update the routes file to the following:
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
namespace :admin do
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
end
vs
get '/cats', to: 'cats#index'
get '/cats/:id', to: 'cats#show'
scope :admin, module: :admin, as: :admin do
patch '/cats', to: 'cats#update'
get '/cats/edit', to: 'cats#edit'
delete '/cats/:id', to: 'cats#destroy'
end
- readability
- organization
- specificity
Can you imagine what happens when you have 400 lines in your routes file?! You'll be thankful these route blocks exist for organization alone.
By the end of this work period have a written response to each of the following questions.
- Describe what each of the following things does in the context of our routes file:
scope
module
as
namespace
- Why might it be beneficial to have two controllers for Songs (one in
controllers/admin
and one just incontrollers
)? Would it have any downsides? - What about different routes? Would we ever want to have
/admin/songs
and/songs
? Why or why not?