Learn Ruby on Rails Book

Adding Tasks controller

Controller and actions

Rails Controllers are Ruby classes, that store a series of actions. Let's generate a controller to carry out tasks related operations:

1$ bundle exec rails generate controller Tasks
2Running via Spring preloader in process 28301
3      create  app/controllers/tasks_controller.rb
4      invoke  erb
5      create    app/views/tasks
6      invoke  test_unit
7      create    test/controllers/tasks_controller_test.rb
8      invoke  helper
9      create    app/helpers/tasks_helper.rb
10      invoke    test_unit
11      invoke  assets
12      invoke    coffee
13      create      app/assets/javascripts/tasks.coffee
14      invoke    scss
15      create      app/assets/stylesheets/tasks.scss

Notice that the above command created multiple files. Let's open the controller file app/controllers/tasks_controller.rb and add action index to TasksController:

1class TasksController < ApplicationController
2  def index
3    render html: "This is index action of Tasks controller"
4  end

Now go to the browser and hit http://localhost:3000/tasks.

We will get a routing error:

1Routing Error
2No route matches [GET] "/tasks"

Defining routes to map requests to controller actions

Whenever we hit URL on the browser, Rails looks up for that path in the routes file. If a match is found, then the mapped action in the controller is invoked.

We want to route all requests with URL /tasks to index action of TasksController.

Open config/routes.rb and add following lines:

1Rails.application.routes.draw do
2  resources :tasks, only: :index

Now visit http://localhost:3000/tasks and this time we should get message This is index action of Tasks controller.

Adding view file

In the above case the content was rendered by the controller itself. We want views to deliver the final html content.

So let's create file app/views/tasks/index.html.erb:

1touch app/views/tasks/index.html.erb

Open app/views/tasks/index.html.erb file and add the following line:

1<H1>List of tasks will appear here</H1>

Open app/controllers/tasks_controller.rb then change the action index:

1class TasksController < ApplicationController
2  def index
3    render
4  end

Now visit http://localhost:3000/tasks and this time we should get message List of tasks will appear here.

Now since our TasksController is working, let's understand what the ApplicationController is all about.

A gentle introduction to Application controller

All our controllers inherit from ApplicationController. This class is contained in the file app/controllers/application_controller.rb, which gets created when we execute the rails new command.

The initial content of this file will be:

1class ApplicationController < ActionController::Base

The ApplicationController in turn inherits from ActionController::Base, which defines a number of helpful methods. We can take a look at the API documentation to understand in detail.

Since ApplicationController is the base controller from which all other controllers inherit, this controller also act as a gatekeeper. This controller can check for authentication, authorization, logging etc. We will see all this in action soon.

Using Rails console to add tasks

Let's open "rails console":

1$ bundle exec rails console
2Running via Spring preloader in process 10511
3Loading development environment (Rails 5.2.2)

Let's create a Task record and save it in the table:

1irb(main):005:0> task = Task.new(title:"This is my first task")
2=> #<Task id: nil, title: "This is my first task", created_at: nil, updated_at: nil>
4irb(main):006:0> task.save
5   (0.3ms)  begin transaction
6  Task Create (0.7ms)  INSERT INTO "tasks" ("title", "created_at", "updated_at") VALUES (?, ?, ?)  [["title", "This is my first task"], ["created_at", "2019-01-30 07:35:50.117114"], ["updated_at", "2019-01-30 07:35:50.117114"]]
7   (196.3ms)  commit transaction

Let's create one more task and try to save this record:

1irb(main):007:0> task = Task.new(title:"This is my second task")
2=> #<Task id: nil, title: "This is my second task", created_at: nil, updated_at: nil>
4irb(main):008:0> task.save
5   (0.1ms)  begin transaction
6  Task Create (0.6ms)  INSERT INTO "tasks" ("title", "created_at", "updated_at") VALUES (?, ?, ?)  [["title", "This is my second task"], ["created_at", "2019-01-30 07:36:01.248102"], ["updated_at", "2019-01-30 07:36:01.248102"]]
7   (181.4ms)  commit transaction

Calling task.save saves the object in the database. A row is added in tasks table.

Now we can see all the tasks stored in the database using Task.all:

1irb(main):009:0> Task.all
2  Task Load (0.3ms)  SELECT  "tasks".* FROM "tasks" LIMIT ?  [["LIMIT", 11]]
3=> #<ActiveRecord::Relation [#<Task id: 3, title: "This is my first task", created_at: "2019-01-30 07:35:50", updated_at: "2019-01-30 07:35:50">, #<Task id: 4, title: "This is my second task", created_at: "2019-01-30 07:36:01", updated_at: "2019-01-30 07:36:01">]>

We will be learning how to display all these tasks in the coming chapters.

Rails MimeResponds

In this section, we will be seeing the different ways of responding with data, by either going through the views or without it.

respond_to is a Rails helper method and it's a way to tell Rails all the formats the controller action knows about.

If different actions support different formats, then this method helps in responding differently based on the request.

Let's take an example. No need to type this since we won't be committing this code:

1class TasksController < ApplicationController
2  def index
3    @tasks = Task.all
4    respond_to do |format|
5      format.html
6      format.xml { render xml: @tasks }
7      format.json { render json: @tasks }
8    end
9  end

In the respond_to block, we are specifying what the action needs to reply with based on the request formats like html, xml or json.

When the format responder is passed with 'json', we are telling it to respond to requests with the .json extension by serializing to JSON.

We need to pass :html with no arguments in cases where we need to handle .html requests in the default way by using conventions and views.

Let's say if the request is for /tasks.xml then the controller will look for a /tasks/index.xml.erb view template to render.

If such a view template doesn’t exist then it tries to directly render the resource in the xml format by invoking to_xml.

There is another helper method called respond_with.

In respond_with method, we don't need to explicitly tell what needs to be sent for different mime-types. This makes our work much easier and also this is the benefit abiding to the DRY principle.

Let's take an example:

1class TasksController < ApplicationController
2  respond_to :html, :xml, :json
4  def index
5    respond_with(@tasks = User.all)
6  end

Rails URL helpers

The ActionView::Helpers::UrlHelper module provides a set of methods for creating links and fetching URLs that depend on the routing subsystem; ie: ActionDispatch::Routing.

This module allows us to use custom method names to denote an URL, such that we can use the same format or method names for denoting links in views and controllers.

In this section, we will see the two most commonly used URL helpers called link_to and button_to.

In plain HTML, we create a link like this:

1<a href="/ruby-book">Improve Your Ruby Skills</a>

The same above code can be re-written using link_to URL helper:

1<%= link_to "Improve Your Ruby Skills", "/ruby-book" %>

link_to takes two arguments.

The first argument is the text on the link and the second is the URL you're linking to:

2`link_to` also takes an optional third argument called `method`.
4The default `method` value for `link_to` is `GET` method.
6However, if you want to post data using `link_to`, pass the third argument as
7`method: :post`.
10<%= link_to "Improve Your Ruby Skills", "/ruby-book", method: :post %>

There is another URL helper called button_to which is mostly used with forms:

1<%= button_to "New", action: "new" %>

By default the :method is POST for button_to.

When using RESTful routes, we can pass the :method to change the HTTP verb used to submit the form.

Sometimes figuring out these path helper names can be tricky. In such cases we can run the following statement from our Rails console to view all such helper names, based on our current routes:


Now let's commit changes:

1git add -A
2git commit -m "Added tasks controller and index action"
    to navigateEnterto select Escto close