How to create a Kanban inspired to-do list application in Ruby, and make version control part of your workflow

How to create a Kanban inspired to-do list application in Ruby, and make version control part of your workflow

Like many keen hobby developers, I've followed many tutorials in an attempt to better my knowledge and understanding of software development. Although often extensive, one of the most significant oversights to many of these tutorials is the inclusion of version control as a part of the process. As a result, version control becomes a separate element to learn, and can make collaborating with other developers a very daunting and difficult prospect.

In this tutorial we’re going to create a simple Ruby on Rails web application, a simple, Kanban inspired to-do list, and show how easy it is to make regular versioning part of your development workflow.

This is quite a lengthy tutorial, starting with the basics of ruby, and leading right through to a fully functional application.

Step 1: The basics

This first step covers the basics. We will install Rails, get a basic introduction to frequent commands in the command line, find out how to generate your first Ruby on Rails project and learn how to commit your changes to your Git repository.

Without further ado, let's get started with the basics of creating our first application in Ruby.

Installing Rails on your local machine

Installing Rails is slightly different depending on the type and version of your operating system. To find specific instructions for your system, the team at installrails.com have put together a comprehensive - yet easy to follow - guide for the majority of users. Head over to installrails.com and follow their step-by-step instructions.

Introducing the command line

Once you have Rails installed on your local machine, it’s time to take a look at the command line.

For this tutorial, we are using OSX and the Terminal application. Windows and Linux users will find there are quite a few differences in the command line, which are too many to go into in this tutorial.

Once you've opened Terminal, we are going to change to the desktop directory:

Type cd ~/desktop and press enter. This will take you into the ~/desktop directory. Type ls -la and press enter, and Terminal will list all the files and folders in your current directory.

Create your first project

We’re going to create a new directory called “projects” on the desktop,

Type mkdir projects and press enter.

Nothing happened, right?

List all the files and folders in your desktop again, using the previous command. You should see a directory called projects. Go ahead and change to the projects directory, the same way we changed to the desktop. Type cd projects and press enter. If you list all the files and folders the results will be blank because we haven’t added anything to the projects directory yet.

Let’s create a new rails project. We will call it “kanban” - though you can use whatever name you choose (just remember to replace kanban with your project name as you go through this tutorial).

Type rails new kanban and press enter. This will create a new rails project called kanban, in a new directory with all the basic project files. Go ahead and list all the connected files and folders as we did before. Your projects directory should contain “kanban”, so let’s go ahead and connect to “kanban”, just as we did the projects directory.

Run the list command, and you will see all the project files and folders rails generated for your new application.

Congratulations! You've just created your first rails application. Want to see what this looks like in your browser?

Go ahead and start a rails server by typing rails s.
Open a browser and head to http://localhost:3000. You should see something like this:

First rails page

It doesn't look like much, but by the end of this tutorial, this will be a fully operational, Kanban-inspired to-do list.

Let's back up all our new files and folders in a Git repository.

Getting started with Git repositories

Your project is all set up, so it’s time to set up your version control.

If you already have a Deveo account, go ahead and login. If not, go ahead and sign up to Deveo’s free cloud account here. Once you have received Deveo’s confirmation email, follow the link in the welcome email, pick a password for your account, and you should be logged in to your newly created Deveo account.

Once you’re logged in, it’s time to add a new project.

Click the “Quick actions” button in the top right hand of the browser window, and select “Create project”.

Enter “Kanban” in the name field, and click “Create project”.

Let's create a repository. Click the quick actions button, and select “New repository”. In the “short name” add “kanban”, and click “Create”.

Congratulations, you have just set up your first project repository. We need to push our changes, and overwrite the code stored in the Deveo repositories. For this, we need to head back to our terminal or command line tool.

In the terminal, type git init and press enter. This initialises Git in the local directory, although we aren’t yet tracking any changes to the files. To start tracking changes, type git add --all and press enter. Now we've registered the changes, we are ready to push them to the Deveo repository.

To make sure we know what changes have been made between versions, it’s a good idea to include a commit message when we commit the changes. As this is our initial commit, the commit message can be “Initial commit”.

Type git commit -m “Initial commit” and press enter. You might have guessed anything between the quote marks after -m is the commit message for the particular commit.

We’ve committed the changes, and we are nearly ready to push the changes to the Deveo repository. For this first push, we’ll need to add a new remote to the local repository. The command that needs to be run is listed at the bottom of your empty repository page in Deveo (we're helpful like that!). In the following command, replace <<YOUR USERNAME>> with your username, without the chevrons.

In terminal, type git remote add origin https://<<YOUR USERNAME>>@app.deveo.com/<<YOUR USERNAME>>/projects/kanban/repositories/git/kanban and press enter. Then to push the changes to the Deveo repository, type git push -u origin master and press enter.

If you return to your repository in Deveo and refresh your browser, you will see your files and directories, with the commit you added.

Step 2: Creating the bare bones of everything

We have the basics covered, and the next step is to set up the basic architecture of our application. Don’t be too overwhelmed, what we will be using most are the /controllers, /models, and /views directories.

The Rails framework follows the Model-View-Controller (MVC) design pattern which enforces separating data management (M) from the presentation and controlling layers. At this point we have no models yet, so let’s create a sample controller and view. Head on over to Terminal and type rails generate controller Pages home, and press enter. What we are saying here is for rails to create a controller with the name pages and the action home.

Head to your Controllers directory, you’ll see a new file has been created called pages_controller.rb. If you open up this file in your text editor, you’ll see the defined (def) action home. This is a route request, asking rails to serve a pages_home_path file whenever /pages/home is requested by the URL.

If you head to the /views directory you’ll see rails has created a new folder called pages, and inside that folder is a file called home.html.erb. This will be our home page view file. Go ahead and start your rails server, then click: http://localhost:3000/pages/home and you’ll see the route ‘GET’ request in action.

Head to your pages directory and open home.html.erb in your text editor. Remove all the HTML and add a header like this <h1>Hello world!</h1>. Save your file, head back to http://localhost:3000/pages/home in your browser and refresh to see the changes we’ve just made.

Re-routing rails

By default, our application is still serving the basic rails page as the home page. Really, we want our home page to show up whenever the root (http://localhost:3000) is requested. We can do this using rails routes.

Head to the ~/desktop/projects/kanban/config directory, and open a file called routes.rb in your text editor. You’ll see the route GET request for 'pages/home'.

What we want to do is make home our root route. So, on a new line below the 'pages/home' GET request, type root 'pages#home' and save the file. If you start the rails server and navigate to http://localhost:3000 you’ll see the home page file we have just changed.

As we’ve done quite a lot here, it’s worth committing and pushing our changes to our Git repository.
Head over to your terminal and close your rails server by pressing ctrl+c.
Type git add --all, and press enter.
Type git commit -m "Created the home page and amended root route" and press enter.
Type git push -u origin master and press enter. Note: you may need to enter your Deveo password. This is how to connect your local and remote repositories, and create a project milestone.

Introducing Partials

This would be a good time to introduce the concept of partials. Partials are exactly what you think they would be, snippets of repetitive code that can be called inside other files. This is particularly great for generic blocks of code, such as headers, footers, and content loops.

To illustrate the use of partials, let’s go ahead and create a header partial for our application.

In your text editor, create a new file. Save the file in app/views/layouts using the filename _header.html.erb. It’s super important not to forget the underscore and .erb, as this is what tells rails this particular file is a partial file.

For now, we will add some stock HTML inside the header partial, so we know when it is rendering properly. Add <h1>This is where my header goes</h1> to your new header partial, and save the file.

Let's open up the main application.html.erb view file in the app/views/layouts directory, and add the code <%= render 'layouts/header %> after the opening <body> tag. Save the file, restart your rails server, and head to your application (http://localhost:3000/). You should see the header rendering correctly.

Let’s push the changes to your repositories, remembering to add a commit comment before you push.

Taking design seriously

So, we’ve successfully added pages, a little bit of code, and rendered a partial, but let’s be honest, it all looks a bit ugly. So, before we go any further, let’s make the design a bit more appealing.

Rather than starting from scratch with our design, for this application, we’re going to use a CSS framework called ‘Materialize’, which is based on Google Material design.

Installing Materialize is as simple as adding a line of code to your gemfile and running a couple of installation commands in terminal. Head to your kanban project directory ~/desktop/projects/kanban and in your text editor, open the file called Gemfile.

Your Gemfile contains references to third-party libraries, which are referred to as "Gems". When you add gems to your gemfile, and run the bundler in terminal (bundle install) rails looks at your gemfile, and installs all the necessary components. Gems, and your gemfile are a way to add functions to your application by making use of other developers hard work, instead of coding from scratch.

To install Materialize, scroll to the very bottom of the file, and on a new line, add gem 'materialize-sass'. Save the file and head over to your terminal. Make sure you’ve closed your rails server (ctrl+c), then type bundle install and press enter.

Once Materialize has finished installing, head to app/assets/stylesheets and look for the application.scss file. (Note: If application.css exists instead, simply rename it to .scss). Open up application.scss, clear all it's contents, and type @import "materialize";. Save and exit the file.

Make sure your rails server is running, and refresh your app in your browser. The header styles should have updated to look something like this:

If you want to dig deeper into Materialize, there is a lot of documentation about it here.

As we have our stylesheet in place, we can start making our header look better. Let’s go ahead and add a “Right Aligned Links” navbar (if you'd like to add something different, head to http://materializecss.com/navbar.html and copy from the available code):

<nav>  
    <div class="nav-wrapper">
      <a href="#" class="brand-logo">Logo</a>
      <ul id="nav-mobile" class="right hide-on-med-and-down">
        <li><a href="sass.html">Sass</a></li>
        <li><a href="badges.html">Components</a></li>
        <li><a href="collapsible.html">JavaScript</a></li>
      </ul>
    </div>
  </nav>

In your header partial, delete everything in the file and paste the code we just copied from Materialize. Save the file and refresh your app in your browser. It should look a lot nicer than it was before, with some nav links, and a logo placeholder.

We’re going to design and build a way for users to sign up for the app - though we won’t touch on the logic of this just yet.

In your text editor, open up your home view home.html.erb and delete the <h1>Hello world!</h1> text we added earlier. We are going to replace it with a Materialize card, and form. Add the “Basic card” code from Materialize:

<div class="row">  
        <div class="col s12 m6">
          <div class="card blue-grey darken-1">
            <div class="card-content white-text">
              <span class="card-title">Card Title</span>
              <p>I am a very simple card. I am good at containing small bits of information.
              I am convenient because I require little markup to use effectively.</p>
            </div>
            <div class="card-action">
              <a href="#">This is a link</a>
              <a href="#">This is a link</a>
            </div>
          </div>
        </div>
      </div>

Although there is a lot here we don’t need, it gives us a basic container for a welcome message and a link to a signup form. Let’s go ahead and change the header and text to reflect our app. Change the card-title text to “Kanban”, and the paragraph to something like “Welcome to Kanban, the simple, Kanban inspired to-do list. You should end up with something like this:

<div class="row">  
        <div class="col s12 m6">
          <div class="card blue-grey darken-1">
            <div class="card-content white-text">
              <span class="card-title">Kanban</span>
              <p>Welcome to Kanban, the simple, Kanban inspired to-do list.</p>
            </div>
            <div class="card-action">
              <a href="#">This is a link</a>
              <a href="#">This is a link</a>
            </div>
          </div>
        </div>
      </div>

In the card-action div, remove both the links, and add a ruby link: <%= link_to 'Sign up', "#" %>. Save your file and refresh your browser to see your changes. This is good, but it would be great if the signup link was a button. Go back and add a button class to your ruby link by typing , class: “btn” after ”#”. The code for your link should look like this: <%= link_to 'Sign up', "#", class: "btn" %>. Save and refresh your browser.

So this all looks good. We’ve done quite a lot here, so let’s push the changes to your repository, remembering to add a proper commit message.

Creating the user sign up process

Next, we’re going to add a couple of gems to make the user signup process a whole lot cleaner and easier.

First, we’ll add Simple Form, a gem designed to streamline the process of adding forms to ruby on rails applications.

Open up your Gemfile, scroll to the bottom and in a new line type gem 'simple_form', and save the Gemfile. We need to run bundle install again, so head over to your terminal and make sure the rails server is off. Type bundle install and press enter. Then run the simple form generator, type rails generate simple_form:install and press enter.

Next, we’ll install Devise, a gem to take care of all the signup and user-specific processes. Go to your Gemfile and add gem 'devise' to a new line, at the bottom of the file. Then run the bundle install again, and finally add all the devise files by typing rails generate devise:install. Pay attention to the output in the terminal. You’ll see it outlines specific manual setup we will need to do, in order to get Devise working in our app.

First, we need to set the default options for the mailer function in our development.rb file. Copy the first line from the output: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } then open config/environments/development.rb in your text editor. Scroll to the bottom of the file, and paste the code before the end.

We’ve already defined the root_url, so the last thing we need to do is copy: <p class="notice"><%= notice %></p><br><p class="alert"><%= alert %></p> and paste into the body of our application view file (kanban/views/layouts/application.html.erb), just below our header. Save the file and return to the Terminal.

We need to set up the Devise users in our database. Thankfully rails runs a database by default, so all we need to do is add in some fields and functions to store User data. To do this, type rails generate devise User and press enter. This will output a way for our app to store user data in a table called :users:. We need to make sure this is all playing nice with our database, so we need to run a rake command for database migration. In the terminal, type: rake db:migrate and press enter. You’ll see the output creates the users table in our database.

The last thing we need to do to install Devise, is to create the user views. In the terminal, type rails generate devise:views and press enter.

We’ve made about quite a few file changes since we last pushed to our repositories, this would be a good time to do so, if you haven’t been doing it all along.

Creating user sign up functionality

As we have the ability to collect and store user data in our database, let’s start making our app work, and create sign up links in the header, and on the home page.

Open up the home page view (views/pages/home.html.erb) in the text editor. We already have a signup button, but it’s not linking anywhere. What we need is to get the signup button to link to a new user registration form. But where is that?

The easiest way to find it is to run a rake routes command in terminal. This will output all the available action paths and routes. Go ahead and type rake routes into your terminal, and press enter. If you look closely, you’ll see a Prefix new_user_registration attached to the devise/registrations#new route. This is the path we want our button to link to.

Head back to the text editor, and in the home view page, you should see the code "#" inside the button we added earlier. Delete it, and in its place add new_user_registration_path,.

Your button should look like this:

<div class="card-action"> <%= link_to 'Sign up', new_user_registration_path, class: "btn" %>  

Go ahead and save you page. Restart your rails server, and refresh your browser. If you click on the “Sign up” button, you should be taken to the signup form.

So, we can sign up, but what happens if you’re already signed up? Let’s keep going with these links and create a login link in the header.

In your text editor, open up your header partial. You should have something like this:

<nav>  
    <div class="nav-wrapper">
      <a href="#" class="brand-logo">Logo</a>
      <ul id="nav-mobile" class="right hide-on-med-and-down">
        <li><a href="sass.html">Sass</a></li>
        <li><a href="badges.html">Components</a></li>
        <li><a href="collapsible.html">JavaScript</a></li>
      </ul>
    </div>
  </nav>

We only want 1 link, to take us to the sign in form, so go ahead and delete 2 of the 3 <li> list items. We are going to use the same link_to tag helper we used for the previous link, so you can delete everything from the remaining <a hrefto the </a> (including those tags).

In the remaining list item (where we just removed the a tags) we are going to add a new sign in link. Just like the previous before, we need to know the path for our link, so go ahead and stop your server, and rake routes again. You’ll see the path we need to use is new_user_session_path.
In the remaining list item add: <%= link_to 'Sign in', new_user_session_path %>.

While we are in the header partial and updating links, let’s go ahead and make the logo link the same. Change the existing code <a href="#" class="brand-logo">Logo</a> to <%= link_to "Kanban", root_path, class: "brand-logo" %>. Let’s save, start the server, refresh, and check out our application.

Go ahead and sign up, and let’s see what happens. You should see a message saying you’ve successfully signed in, and if you click on the sign in, or sign up buttons, you’ll see a message saying you’re already logged in.

We need to add a little logic saying if the user is signed in, some alternative information is displayed, such as a button to allow users to sign out, as well as some access to manage their account.

For this, we will add some operators inside embedded ruby tags <% %>, which are slightly different to rendering tags <%= %>. To find the right paths for these pages and views, we need to use the rake routes command. This shows the edit user path is edit_user_registration_path and the sign out path is destroy_user_session_path. By default, browsers see a link_to tag as a "GET" command, meaning it will try to read the data available, but nothing else. If we want something else to happen, we will need to specify the command. In this case, we need to specify :delete as our sign out method.

So, our logic should be as follows:

<% if current_user %>  
    <li><%= link_to 'Edit', edit_user_registration_path %></li>
    <li><%= link_to 'Sign out', destroy_user_session_path, method: :delete  %> </li> <% else %>
    <li><%= link_to "Sign in", new_user_session_path %></li> 
<% end %>  

Add this code to your header partial, in place of the existing list item. Your header partial should look something like this:

<nav>  
  <div class="nav-wrapper"> 
    <%= link_to 'Kanban', root_path, class: "brand-logo" %> <ul id="nav-mobile" class="right"> 

    <% if current_user %>
      <li><%= link_to 'Edit', edit_user_registration_path %></li>
      <li><%= link_to 'Sign out', destroy_user_session_path, method: :delete  %></li> 

    <% else %>
      <li><%= link_to 'Sign in', new_user_session_path %></li> 
    <% end %> 
   </ul> 
  </div> 
</nav>  

Now you can sign in, edit, cancel your account, and sign out of your application, it’s a good time to push to your repositories.

Adding the task functionality

In this section, we’ll add all the functionality required to create tasks, and change their states. To do this, we’ll need to make use of rails scaffold commands. Head to your terminal and turn off the rails server. In the terminal, type rails generate scaffold Task content:text and press enter. The rails scaffold command creates new models, along with controllers, views and database tables. In our case it will create new model called Task with content attribute, which has a text format.

The output is quite extensive. As this has created a database migration file, we will need to run the migrate command in order to create a table in our database called tasks. In your terminal, type rake db:migrate and press enter.

Restart your rails server, and head to http://localhost:3000/tasks. You’ll see a page with a "Tasks" heading, a "Content" heading, and a link to add a "New task".

Go ahead and create a new task. Click “New task” and follow the instructions in the following forms. Once submitted, click “back”, and you’ll see your new task, with the option to show, edit, or destroy. “Show” will take you to a more in-depth view of your task. “Edit” will take you to an edit view, where you can amend your task, and “Destroy” will ask you if you’re sure, before allowing you to delete the task. Before we continue and refine this, add a couple of new tasks so we have some data to work with.

Adding states

As we have some tasks, let’s look at adding states to them, so we can mark when something is open, in progress, or complete. To do this, we need to create a new migration. In the terminal, turn off your rails server, then type rails generate migration AddStateToTasks state:string and press enter.

Head over to kanban/config/db/migrate and open the file ending with _add_state_to_tasks. You’ll see a column has been defined in tasks with the name state, and with the format string. What is missing here, is a default value for the state, so let’s go ahead and add this. After :string type , default: 'open'. This will give all new tasks a default state of “open”. Go ahead and save the file. Head back to the terminal and run rake db:migrate. So, we have the default state of open, and the ability to store the state in our database, but currently rails will not let us update our state, as we have not whitelisted the parameters.

To whitelist your state parameters and allow us to change the state in the database, open up your tasks controller kanban/controllers/tasks_controller.rb in your text editor. Scroll to the bottom of the file and find def task_params, and add the content and state parameters. It should look like this: params.require(:task).permit(:content, :state). While still in the tasks controller, head to the top of the document, and let’s add our task states.

Since we only want to display tasks of the current user, find def index and remove @tasks = Task.all. Then, on separate lines add @open = current_user.tasks.where(state: "open"), @wip = current_user.tasks.where(state: "wip"), and @closed = current_user.tasks.where(state: "closed"). This section should look something like this:

 def index
    @open = current_user.tasks.where(state: "open")
    @wip = current_user.tasks.where(state: "wip")
    @closed = current_user.tasks.where(state: "closed")
  end

Save, and refresh your browser. You should be faced with what looks like a critical error in your code. Don’t worry, that’s supposed to happen right now, because although we are telling rails to only show a specific user’s tasks, we haven’t defined how to find the user.

Head over to the terminal and close your rails server. We are heading into the Rails Console, which can be opened by typing rails console and pressing enter. Exiting the console is as easy, as you simply type exit and press enter.

Once you’re in your rails console, type Task.connection and press enter. Then type Task and press enter, and pay attention to the output. This is all the database information being connected to an individual task. We can see the id, content, created_at, and even our newly create state, but what’s missing is the User ID. To add this, exit the rails console by typing exit and pressing enter. Back in the regular terminal, type rails generate migration AddUserIdToTasks user_id:integer and press enter. As we’ve just made a change to our database, we need to migrate. Run rake db:migrate in your terminal.

Open the rails console as we are going to simulate what happens when a new task is created, to make sure the correct data is pulling through. Run Task.connection, and then run Task. you’ll notice a new field user_id: integer has been created.

As we’ve created a user_id with the task, we need to create an association between user and tasks. Associations are essentially the relationship between elements. In user-task terms, this is expressed as: User has_many tasks. Open up the user.rb file in your text editor, you’ll find it in kanban/app/models/. Before the end type has_many :tasks, dependent: :destroy. This is saying a user can have many tasks, and tasks are associated with a user. This also means, if a user is deleted, the tasks will also be deleted, and will not be orphaned.

Next, open the task.rb model, and associate the task with the user. Add belongs_to :user before the end. Let’s open up the tasks_controller.rb (in app/controllers/) and change the @tasks variable to reflect the current_user, like this:

def index  
@tasks = current_user.tasks
end  

Similarly, let’s also update the @create action from @task = Task.new(task_params) to @task = current_user.tasks.new(task_params). And finally, let’s make it so only authenticated users can access the tasks, add before_action :authenticate_user! before before_action :set_task, only: [:show, :edit, :update, :destroy] and save your file.

Start your server and take a look. If you log out and try to visit http://localhost:3000/tasks, you will be redirected to the login page.

As we’ve made quite a few big changes, this is a really good time to commit our changes and push them to Deveo.

Improving the UI and introducing Kanban elements

Open up the tasks index page (views/tasks/index.html.erb) in your text editor. Right now, everything is in the tables automatically added when we generated our rails application. The trouble is, tables can cause a few issues when it comes to cross-browser compatibility. Really, Materialize works best with Divs, so let’s start by removing all the table tags. Leave all the ruby render and embed code, and change the <th>...</th> tags to <h2>...</h2>. You should end up with something like this:

<p id="notice"><%= notice %></p>  
<h1>Tasks</h1>  
      <h2>Content</h2>
    <% @tasks.each do |task| %>
        <%= task.content %>
        <%= link_to 'Show', task %>
        <%= link_to 'Edit', edit_task_path(task) %>
        <%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %>
    <% end %>
  <br>
<%= link_to 'New Task', new_task_path %>  

The first thing we need to do is wrap everything in a container. So, between <p id="notice"><%= notice %></p>and <h1>Tasks</h1> add a new div and give it the container class: <div class=”container”>. Scroll to the bottom and add the closing div tag </div> to the bottom of the page. Save the file and head to your browser to see what we’ve changed.

Currently, our tasks page is showing a list of all the tasks in the default order, spanning the width of the page. As we are going for a Kanban-inspired board style, we need to have 3 columns spread across the page, relating to each state: Open, WIP, and Closed. So let’s style them out.

Let’s add a new div after the opening container tag, and give it the class “row”: <div class=”row”>, and add the closing div tag just before the “New task” link. Wrap the new task link in another row. Your page should look something like this:

<p id="notice"><%= notice %></p>  
<div class="container">  
  <div class="row">
<h1>Tasks</h1>  
      <h2>Content</h2>
      <% @tasks.each do |task| %>
        <%= task.content %>
        <%= link_to 'Show', task %>
        <%= link_to 'Edit', edit_task_path(task) %>
        <%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %>
    <% end %>
  <br>
  </div>
  <div class="row">
    <%= link_to 'New Task', new_task_path %>
  </div>
</div>  

Let’s add our columns. Materialize containers must span 12 standard columns. So, if we only want to use 3 columns for our application, they will need to span 4 standard columns in order to work effectively. Luckily Materialize has an easy way to do this with their CSS classes.

Let’s add a new div after <h1>Tasks</h1> and give it a class of “col l4 s12”: <div class="col l4 12">. The “col” class indicates a column, and “l4 s12” indicates a span of 4 standard columns on a large screen (desktop) and 12 columns on small screens (mobile). Go ahead and close the div, right after the <% end %> ruby tag. While we’re here, let’s change <h2>Content</h2> to <h2>Open</h2>. Go ahead and save, and take a look. You’ll see all our tasks are nicely contained in a column on the left-hand side. But it still doesn’t look great. It would be great if our tasks could appear on separate cards like we had on our home page. So let’s do it!

After <% @tasks.each do |task| %> add a new div with the class "card blue-grey darken-1", then add another div with the class "card-content white-text". Close both divs before the <% end %> tag. Since we will show all the task information on the main board level, let’s also go ahead and delete <%= link_to 'Show', task %>.Your column div should look something like this:

<div class="col l4 s12">  
        <h2>Open</h2>
          <% @tasks.each do |task| %>
         <div class="card blue-grey darken-1">
            <div class="card-content white-text">
         <%= task.content %>
          <%= link_to 'Edit', edit_task_path(task) %>
          <%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %>
         </div>
         </div>
          <% end %>
    </div>

Save, restart your server, and refresh your browser.

We are going to create columns for our remaining 2 states. Copy the column code you’ve just created, and paste twice. Change the <h2>Open</h2> on the other 2 states to <h2>WIP</h2> and <h2>Closed</h2>.

All 3 columns are currently displaying all of our tasks. So, we need to go back to the task controller, and add the different states. Open your tasks_controller.rb file (in app/controllers/) and amend the def index @tasks variable. Instead of @tasks = current_user.tasks we want to define the difference between the open, wip, and closed states we’ve created. Quite simply, our open state is defined as follows: @open = current_user.tasks.where(state: "open"). This is saying the @open variable is the current users tasks with a state of open. Similarly, wip is @wip = current_user.tasks.where(state: "wip"), and closed is @closed = current_user.tasks.where(state: "closed"). Add these states, and remove the @tasks variable.

Head back to your index.html.erb and change @tasks.each to reflect the state column the code sits in. For example:

<h2>Open</h2> <% @tasks.each do |task| %>

Becomes

<h2>Open</h2> <% @open.each do |task| %>

Do this for the remaining columns. Save your file, and refresh your browser. You’ll see the tasks only exist in the Open column. We need to create a way to change the states.

To do this, we need to add a custom action to our tasks_controller.rb file. Go ahead and open tasks_controller.rb in your text editor. At the top, you’ll notice the following code before_action :set_task, only: [:show, :edit, :update, :destroy]. Although we could attach our state edit to the :edit function, it would be much cleaner to have a separate specific action to change the state specifically. So, go ahead and add , :change after :destroy. We should define the change action in our tasks controller, which we will do just before the private code.

Start by opening and labeling the action def change. As we are updating the state attribute, the function you want to run against the task is update_attributes, so type @task.update_attributes(). Inside the parenthesis, we are going to define what attributes will be updated, and how. As our :change action is being run by the set_tasks before action, it already carries the right information we will need. So, inside the parenthesis, we simply need to say state: params[:state]. It should look like this:

def change  
@task.update_attributes(state: params[:state])
end  

Before we go any further, there is some UX that would be good to update here. Currently, when a task state is changed, we would be redirected to the individual task page. For ease of use, this is unnecessary. So, let’s go ahead and add our response.

If you look at other actions, you’ll see a respond_to do command. This command is essentially redirecting the browser to a specific place after the action has taken place. As we want to stay in the same place, let’s add:

respond_to do |format| format.html { redirect_to tasks_path, notice: "Task updated"}

It should look something like this (remembering to end both actions):

def change @task.update_attributes(state: params[:state]) respond_to do |format| format.html { redirect_to tasks_path, notice: "Task updated"} end end

Now, we have created the action and set up a route for it. As this is an update action, the route we use should be a put (update) route, relating to change tasks. Go ahead turn off your rails server, and run rake routes in your terminal. You won’t see the route, so we will have to go and create it in the routes.rb file (kanban/config/).

Open the routes.rb file in your text editor. We need to add a member on the task resources. To do this, we need to open a new action, add the member action, and add the put :change route. It should look like this:

resources :tasks do member do put :change end end

Save the file, head back to the terminal, and run rake routes again. You’ll see the change_task put route exists.

As we have the ability to change the state, we need to add some buttons in the UI to make it work.

Open up index.html.erb file. Let’s create a new link_to tag for each of the state change actions. In the open column, we want to be able to change to wip. In wip, we want to be able to change to open or closed. In closed, we want to be able to change to wip.

In the Open and Closed columns, on a new line under the destroy link_to tag, add:

<%= link_to 'Mark as WIP', change_task_path(task, state: 'wip'), method: :put %>

On the WIP column, under the destroy link, add:

<%= link_to 'Mark as Open, change_task_path(task, state: open), method: :put %>

<%= link_to 'Mark as closed, change_task_path(task, state: closed), method: :put %>

Save the file, restart your rails server, and head to the browser. You’ll see the state change in action.

We’ve changed a lot here, so let’s commit our changes and push to Deveo.

Step 3: Tidying up and deploying

We have all the functionality of creating, updating states, editing, and deleting our tasks, all that’s left is to tidy up a few things before we deploy to our servers.

Cleaning up the forms

Our “New task” and “Edit task” forms look a bit ugly, as they are stretched across the entire width of the page. Thankfully we know we can solve it by putting the form inside a container.

In your text editor, open up your new task page (views/tasks/new.html.erb). Let’s just add a new div before any other code, and give it the class "container" <div class=”container”>. At the very bottom of the file, after all the other code, close the div </div>. Save it, and return to your new task form.

Not bad! The form is centered nicely, and the title and link are aligned to the left. Let’s make the “back” link a red button here, to make it stand out from the rest of the page. In your text editor, let’s add the class of “btn red” after the tasks_path, like this <%= link_to 'Back', tasks_path, class: "btn red" %>. Before we save, let’s add a line break <br /> between our buttons. Go ahead and save it, and return to your browser.

It’s all looking a bit better, so let’s go ahead and do the same with our edit.html.erb. In this file, we also have a “show” link_to tag, which can be removed, since we are showing all the info on the tasks page.

Let’s also do the same with our user edit view, which we get to by clicking on “Edit” in the navigation bar. Go ahead and open kanban/app/views/devise/registrations/edit.html.erb. Just as before, wrap the whole page in a container div. Here, there is also link_to "Cancel my account". Let’s also make this a red button, but let’s leave the “back” link_to tag as a standard link, to avoid looking too messy. Save the file and take a look.

Getting to the “User account” page through a link called edit might cause some confusion. Let’s quickly change it in our header partial. Open up your _header.html.erb partial in your text editor, and change the link_to “Edit” to link_to “Manage account”.

This is all looking really good. So, the last design elements we should change, are the tasks themselves.

Each task looks something like this:

Which is quite messy. We’re going to go ahead and layout each of our tasks with a space for the edit and delete links, the task content, and buttons to change the states, and also tidy up our code.

If you open up our index.html.erb file, you’ll notice we are repeating a lot of the same code, such as the <%= task.content %> and all the <%= link_to. As such, there is a better way to call the code that won’t put unwanted strain on your application. That’s right, we can use a partial.

In your tasks folder, create a new partial called _task.html.erb, and open it in your text editor.

Copy the opening card blue-grey darken-1 div code from your index.html.erb. It’s good practice to close it now, just to make sure we don’t forget later on. It should look like this:

<div class="card blue-grey darken-1"> </div>

In your card div, let’s add a new div, with the class “card-panel white” <div class="card-panel white"> . This is going to be our card header, where we will have our edit link, display how long ago the task was created, and have a “trash can” for easy deleting.

Inside the card-panel div, add a link_to tag, to edit the task <%= link_to edit_task_path(task). Then start a new action, and add Created <%= time_ago_in_words(task.created_at) %> ago. You should have something like this:

<%= link_to edit_task_path(task) do %> Created <%= time_ago_in_words(task.created_at) %> ago <% end %>

As you might have guessed, this is displaying the how long ago the task was created as a link to edit the task.

We are going to add icons next, and there are some great, free to use icons from font-awesome. Luckily, installation to ruby is super simple. Open up your Gemfile, and on a new line at the bottom, add gem 'font-awesome-rails'. Save, head to terminal and bundle install. Open up your application.scss file and on a new line below @import “materialize”; add @import "font-awesome";. Save, and head back to your task partial.

On a new line inside the card-panel div, add <%= link_to "<i class='fa fa-trash-o' aria-hidden='true'></i>".html_safe, task_path(task), method: :delete, data: { confirm: 'Are you sure?' }, class: "right" %>.

After the closing tag of the card-panel div, let’s add a new div with the class card-content white-text. You might have guessed, this is where our task content will go. So go ahead and add <%= task.content %> inside the card-content div.
Your _task.html.erb partial should look like this:

<div class="card blue-grey darken-1"> <div class="card-panel white"> <%= link_to edit_task_path(task) do %> Created <%= time_ago_in_words(task.created_at) %> ago <% end %> <%= link_to "<i class='fa fa-trash-o' aria-hidden='true'></i>".html_safe, task_path(task), method: :delete, data: { confirm: 'Are you sure?' }, class: "right" %> </div> <div class="card-content white-text"> <%= task.content %> </div> </div>

Let's add a space for the state changing buttons. After the closing tag of the card-content div, add a new div with the class “card-action”.

As this is a partial, it will be called into each state column in exactly the same way. The issue is we want to be able to move our tasks along each state in sequence, so from open to wip, from wip to with open or closed, and from closed back to wip. So, let’s employ some ruby logic.

Since both open and closed states will show a link to wip, only wip needs to show different links. So, inside your card-action div type

<% if task.state == 'wip' %> <%= link_to "<i class ='fa fa-arrow-left'></i>".html_safe, change_task_path(task, state: "open"), method: :put, class:"btn red", style: "width:45%;" %><%= link_to "<i class ='fa fa-arrow-right'></i>".html_safe, change_task_path(task, state: "closed"), method: :put, class:"btn right", style: "width:45%;" %>

<% elsif task.state == 'closed' %> <%= link_to "<i class ='fa fa-arrow-left'></i>".html_safe, change_task_path(task, state: "wip"), method: :put, class:"btn btn-block red" %> <% else %> <%= link_to "<i class ='fa fa-arrow-right'></i>".html_safe, change_task_path(task, state: "wip"), method: :put, class:"btn btn-block" %> <% end %>

Let’s break this down. What we are saying here is if the task’s state is wip, show links to change the state to open, and closed. Otherwise, show a link to change the state to wip. We have also included the button classes, to make the links more visible, and conditions to display arrows pointing in the right direction, depending on whether the task is open or closed.

Let's render the partial inside the index.html.erb. Open up your index file, and delete everything inside the first row div! Yep! That’s right. Let’s also go ahead and remove the “notice” paragraph. It should look something like this:

<div class="container"> <div class ="row"> </div> <div class="row"> <%= link_to 'New Task', new_task_path %> </div> </div>

Inside the first row div, we're going to create 3 columns, each rendering a version of our new partial depending on the state. Go ahead and open the first column div <div class="col l4 s12>, add a header <h3>Open</h3> and then add the render code:

<% @open.each do |task| %> <%= render @open %> <% end %>

Do the same for wip and closed columns, and you should end up with something like this:
<div class="container"> <div class="row"> <div class="col l4 s12"> <h3>Open</h3> <%= render @open %> </div> <div class="col l4 s12"> <h3>WIP</h3> <%= render @wip %> </div> <div class="col l4 s12"> <h3>Closed</h3> <%= render @closed %> </div> </div> <div class="row"> <%= link_to 'New Task', new_task_path %> </div> </div>

This would be a good time to commit your changes and push them to Deveo.

So, we are basically ready to deploy, though there are a few things we should tweak before we do. Currently, if a user is signed in they will still be presented with a “sign up” link on the home page. What we want to do is redirect a signed in user to the tasks page.

Go ahead and open up your pages_controller.rb (in app/controllers). Let’s define the home logic to redirect a current user to the tasks_path. On a new line inside def home, add

if current_user redirect_to tasks_path end

Save the file, and return to your browser. With your rails server on, sign into your to-do list and go to http://localhost:3000. You should be automatically redirected to your tasks.

Finally, the last thing we need to do is stop any empty tasks from being created, or any orphan tasks occurring without any user_id attached.

Open up your task.rb file in kanban/models/. On a new line after belongs_to :user add the validates statement, like so:

validates :user_id, presence: true validates :content, presence: true

This looks for the presence of both content, and a user is, before submitting to the database, ensuring your database is free from any orphaned tasks.

You may have also realized we haven’t yet wrapped our user login form, or new sign up form, in a container, so let’s go ahead and do it. Open up new.html.erb located in app/views/devise/registrations, and new.html.erb located in app/views/devise/sessions. Add the opening div tag of your container div at the very start of the files, and the closing tag at the very end, just as we did before.

Now, we’re going to do is make the “new tasks” link a more attractive button, and move it above our tasks. Open up your index.html.erb, and find the New Task link_to tag, near the bottom of the page. Move the entire row div surrounding it, to the top of the page, just inside the opening container div. Change the link_to tag to <%= link_to "<i class='fa fa-plus' aria-hidden='true'></i>".html_safe, new_task_path, class: "btn-floating btn-large blue" %> and save your file, and take a look.

Go ahead, commit your changes and push the changes to Deveo, because we are just about done!

Let's Deploy

So, we have a fully functioning application built in Ruby on Rails, and the final thing we need to do is release it on the world.

For this, we're going to use Heroku, a great server platform offering a pretty substantial free tier.
Head on over to heroku.com and download the Heroku Toolbelt, and while you're waiting for it to download, familiarize yourself with a their rails specific documentation.

Once you have Heroku toolbelt downloaded and installed, head to your terminal and login to it, type heroku login, and enter your username and password.

As you'll see from the documentation, Heroku supports postgresql. As the default database we have been working is sqlite, there are a few things we need to adjust with the database, to make sure the application is deployed properly.

Open up your gemfile, and find the section group :development do. On a new line before the end of the development section, add gem 'sqlite3'.

Now we need to add the rails_12factor and pg gems. On a new line after the end of the development section, add:
group :production do gem 'rails_12factor' gem 'pg' end

Let's update the database.yml configuration file in kanban/config. On a new line at the bottom of the file, add:

production: &default adapter: postgresql database: todoism encoding: utf8 min_messages: warning pool: 5 timeout: 5000

As you'll see from the rest of the file, indentation is super important here, so make sure the indents are 2 regular spaces.

When you're ready, run your bundle install command in terminal.

If you get an error "while installing pg", you might need to download and run Postgres, and in terminal run
gem install pg -- --with-pg-config=/Applications/Postgres.app/Contents/Versions/9.5/bin/pg_config. Note: you made need to run brew update and agree to the Xcode service agreement before the aforementioned terminal command will run successfully.

Create a new Heroku app, in terminal type heroku create.

To save space, let's precompile all our files before deploying to the server. Run RAILS_ENV=production bundle exec rake assets:precompile in terminal.

Scroll up the terminal output, and if there is the message rake aborted! Devise.secret_key was not set. Please add the following to your Devise initializer: copy the config.secret_key = '...number...' and add it before the end in the devise.rb file (kanban/config/initializers/devise.rb).

For the last time, push your changes to your Deveo repositories:
git add --all git commit -m "Prepared to deploy"
git push -u origin master

Let's make sure we add remote access to Heroku by running git remote add heroku <<Heroku application URL>>, replacing <<Heroku application URL>> with the url of your app in Heroku.

Finally, we can deploy by pushing our code to our Heroku repository git push heroku master.

That's a wrap!

Congratulations! You've reached the end of this tutorial, and made versioning your files a part of your workflow. Not only have you created a fully functional ruby application, you've also managed to create multiple versions of the same application files, and record all your changes in a secure way. Now, should you wish to inspect the history attached to your application files, you can login to app.deveo.com, head to your repositories, and view your history.

If you've found this tutorial helpful, have noticed any bugs, or just want to get in touch feel free to leave a comment.

Seamless software development.

Code management and collaboration platform with Git, Subversion, and Mercurial.

Sign up for free
comments powered by Disqus