In our last blog we talked about the routes in a Rails application. Today we will take a deeper look at a Rails Controller. As you might know, Rails follows the MVC (Model View Controller) design pattern, when a user goes to a valid link on your site, it is handled by a controller. The controller might communicate with a model (database) to perform CRUD on some data, with the routes to obtain some dynamic values (via a special hash called params), or redirects the user to a view page. We will go through all these cases shortly.
The starter code can be found at my GitHub page. Our app currently has a homepage, a contact page, and a catch-all page, all of which are mapped to separate actions in the pages controller.
We will start by manually adding in routes and the full set of CRUD actions in the controller and creating the view pages. This is a good way to understand how each component in Rails works, whereas running rails g resource or rails g scaffold can save us some time by generating codes and files for us, but we can run those commands anytime after we have a solid foundation of understanding how data flow works first.
Suppose we are building out a bookstore, each book in our model will have a title, audience (children, teen, adult) and isbn. All of these are arbitrary and in a real life scenario, a book would have more information such as author, publisher and such.
First, in our routes.rb file, we can simply call the resource method:
Just by adding resource :books, we can see Rails adds the full CRUD routes for us. If we run rails routes | grep book in our Terminal:
We also have a similar resources method in the routes. The difference between resources and resource is the former will also create an index route and action, resource does not. Check the subtly below:
Each route is mapped to an action (index, new, edit, show, update, etc) in the books controller. So the next logical step is to create a books controller and define those actions inside of it:
This is the point where the controller usually interacts with the model (database) and retrieve/insert/delete some data from it. Let’s next create a simple books database with three columns in it: title, audience, and isbn.
Inside the Terminal, run rails g model Book title:string audience:string isbn:integer. This creates the Book model and a migration file with our three columns. Run rails db:migrate afterwards.
If we check in our db/scheme.rb file, we can see the database is created successfully.
Now that we have a database, we can go back to implement the controller actions. A basic implementation of all the actions would look something like this:
Notice we are repeating a few lines of codes, especially when we try to call Book.find(params[:id]) in some methods. We can further refactor it into:
We had defined a custom private method named set_book on line 32. On line 2, we set a before_action, which is called before any method executions, but only for the show, edit, update and destroy methods, since the other methods don’t concern themselves with finding a particular book.
In the create and update methods, we break down the params method into its own private book_params method:
Next up we can go ahead of create the view pages. Inside of the app/views folder, create a sub-folder named books, and inside of that, create an index.html.erb, show.html.erb, new.html.erb, and edit.html.erb files:
Let’s now create some test dummy data in our db/seeds.rb file:
Running rails db:seed will create our books. If we head into the Rails console, and do a quick Book.count to see the number of books in our database, we can see it’s 6, which is correct.
Going back to our books’ view files, if we run the Rails server and head to localhost:3000/books, we can see the page:
The last step we need to do is update the view files. Remember how in the books_controller actions, we assign instant variables such as @book or @books for actions, each corresponding view files have access to those variables. In other words, we can make use of the @books variable in our index.html.erb file because we define such variable in our books_controller index action:
We call the each method on @books to loop through all the books, and output its title. The |book| block variable can be whatever name you choose, but book is a descriptive name in our case. Save and refresh the books index page, we can see everything is displayed as intended:
In our next blog, we will continue our exploration on Rails controller, and discuss the concept of parameters, how we can use dynamic values passed in from the route, capture them in the params hash, and use them inside our controller, as well as the controller concerns. See you next week!