October 22, 2016
Mapping RESTful urls to controller actions
In its entirety, the MVC architecture is difficult to explore. This is especially true when many concepts and structures are being layered in order to serve a webpage for a simple GET request. Yes, in short, the Rails framework is heavy. The good news is that is completely possible to follow the flow at which messages are transported from one file to another.
For a simple navigation bar on your website comprising of links to Home
, About
and Contact
, a website needs to serve a webpage for each when clicked on. At a basic level these links are considered ‘static’ or ‘flat’ because there’s nothing required beyond GET
ting that single webpage.[1] Compare this Navagation
resource to a User
resource where you are requiring a RESTful architecture to manage forms with inputted details and integrating that data to serve a tangible instance of a new user - for example, think about account creation on GitHub.
On a full circuit, the 4 stations that are visited in the MVC architecture are:
This full circuit is required in the Users
resource because the premise of data persistence exists. If we don’t make a trip to the model, we cannot save the data - there would be no back end to give this webpage its dynamic quality! In contrast to the Navigation
resource, we don’t need to save or serve any data so a trip to the model can be skipped.
Another way to put this is that the controller actions in the ‘Users’ resource point towards the model before serving a view. While in the ‘Navigation’ resource, the controller actions point to a corresponding view.
For generating the ‘Users’ resource we would run a scaffold
command which sets up all 4 stations in a RESTful manner:
For generating the ‘Navigation’ resource we would run a controller
command. This command only sets up 3 out of the 4 stations:
which outputs:
The output of generate controller
sets up your routes, controller and views. Notice there is no model file:
The scaffold
and controller
commands don’t only differ in the number of stations visited, but they also differ in the manner in which a HTTP request for the resource is routed.
When you open up the config/routes.rb
file what you find is that all the RESTful routes for the ‘Users’ resource have been hidden under a neat helper method called resources
. Whereas the routes for the ‘Navigation’ resource are made explicit:
When you follow the get 'navigation/about'
route to the various files and methods it connects to, you will find that these exact files and methods included are actually comprised from these pairs of words:
Lets begin tracking:
To cement how each file-path or method is linked to each other pay attention to:
- Whether the resource is pluralised or singular.
- What files are named after the resource or the controller action
- What methods are named after the resource or controller action
Did you see the pattern?
Lets examine what get 'navigation/contact'
involved:
- At Station 2:
- the pluralised resource name is a prefix for the controller file path; this can be confusing since the word
navigation
itself is both the plural and the singular. - the
contact
action corresponds to method listed in the controller file
- the pluralised resource name is a prefix for the controller file path; this can be confusing since the word
- At Station 4:
- the pluralised resource name corresponds to the folder name holding its view files
- the view file is named after the action.
You’ll really know how to navigate your project tree if you can fill in these blanks for the get 'navigation/about'
or get 'navigation/home'
route:
A simple rule to follow is that the naming of these file paths-and methods pivot off that one simple line in your route file - get 'navigation/about'
.
Now for the RESTful routes:
If we try to track a RESTful route for the ‘Users’ resource we immediately run into two problems. The first is that we get stuck with a helper method that hides the pathing and makes it all implicit:
Well we know what the RESTful routes look like through this table:
HTTP method | URL | Action | Purpose |
---|---|---|---|
GET | /users | index | get all users |
GET | /users/:id | show | get user object labelled :id |
GET | /users/new | new | get form for creating new user object |
GET | /users/:id/edit | edit | get form for updating info on user object labelled :id |
POST | /users | create | create a new user object; its :id is the next available row in your database |
PUT | /users/:id | update | update a user object labelled :id with stuff |
DELETE | /users/:id | destroy | destroy the user object labelled :id in the users database |
Alright so maybe the route file looks something like this:
The second problem becomes apparent when you try to follow the get /users
route. The reliable pl_resource/controller_action
pair rule disappears and we must throw our previous assumptions out the window.
This is where the to:
key becomes really handy. This connects the route back to the our pl_resource/controller_action
pair. And once again we’ll have all our file-paths and method names matching up again.
But I don’t want to write out all those RESTful routes and then map them to a controller. So sticking with resources :users
is a great idea. Now I’m not exactly sure what the implications are for mapping your routes to customised controller actions… but it has been a great exercise to understand how all these file paths are names are generated by first looking at the files created in rails generate controller
command and then compare it with the files created in a rails generate scaffold
command. In any case, it’s another interface of the Rails gem I have come to understand a lot better.
Maybe an amazing Rails App using customised controller actions is waiting just round the corner? Who knows.