Today we will be making a to do list application in Ruby on Rails. Ruby on Rails is a web application framework written in Ruby. This tutorial will require some basic knowledge using Terminal. Also, if you are new to ruby on rails, I recommend you check out Railscasts and the Lynda Rails tutorials are great as well. I won’t get into installing ruby here, but if you are on a Mac you already have it. Make sure you have rails (or the latest version, I will be using 2.3.2) by opening Terminal and running:
sudo gem install railsWe will also be using the nifty_generators gem, (on github) so run:
sudo gem install nifty-generators
Then, to set up a new rails application, run:
rails todolist -d mysqlThe -d flag specifies that we want to use mysql for our database. The first file we will want to edit is config/database.yml, which is the database configuration file. Edit the development part, as that’s all we will be doing in this tutorial. My section looks like this:
# file: config/database.yml
development:
adapter: mysql
encoding: utf8
reconnect: false
database: todolist_development
pool: 5
username: root
password:
socket: /tmp/mysql.sockMake sure you create a database with that name. Following on, we will be working through the terminal, so you won’t need something like PHPMyAdmin anymore. Let’s start up the server and see what we are presented with. To start the server, run script/server in your terminal. Fire up your web browser and go to 127.0.0.1:3000. Make sure you are using the same version of Rails so that we are on the same page :)
Now we are going to get right into developing. Also, there are only two folder you really have to worry about, the app and public folders. All of your application’s logic goes into the app folder, and things like javascript files and style sheets go into the public folder.
So, let’s get started coding the app! First, pull up Terminal again, and change (cd on unix & mac os x) into your directory, so assuming you left off at running the rails command, run:
cd todolist
Then, we need to create a controller. Rails follow the Model-View-Contoller format. To create our controller, which we will name tasks, run:
script/generate controller tasks
Now that we have our controller, and before we proceed to work on the app, I am going to run:
script/generate nifty_layout
This will give us a nice layout that will be automatically used by Rails, as it will create the application.html.erb template in the app/views/layouts directory. We also need to create a model, which represents a table in our database. To create ours, run the following in Terminal:
script/generate model Task name:string finished:boolean rake db:migrate
The first line create a model name Task, with two fields, one is name which is a string, and finished which is boolean. While it may look like this is all the table has, Rails automatically adds in three columns: an id column which is set as the primary key, as well as created_at and updated_at.
Now, here is the code for the controller, and I will explain every piece after.
# file: app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
new
@tasks = Task.all
end
def new
@task = Task.new
end
def create
@task = Task.new(params[:task])
if @task.save
flash[:notice] = 'Task was successfully created.'
redirect_to :action => "index"
else
@tasks = Task.all
render :action => "index"
end
end
def finish
@task = Task.find(params[:id])
new = {:finished => true}
@task.update_attributes(new)
redirect_to :action => "index"
end
def unfinish
@task = Task.find(params[:id])
new = {:finished => false}
@task.update_attributes(new)
redirect_to :action => "index"
end
def destroy
@task = Task.find(params[:id])
@task.destroy
redirect_to(tasks_url)
end
end
So starting off with,
class TasksController < ApplicationController
class declares a new class in Ruby, and in this case called TasksController, which inherits from ApplicationController
# file: app/controllers/tasks_controller.rb def index new @tasks = Task.all end
The def declares a new method named index. When you create your rails application, the default routing is controller/method/id, so going to tasks or tasks/index will load the index method. The next line only has new, which tells ruby that you want to execute the new method (this doesnt mean show that page). The next line sets a class variable named tasks, and sets it equal to whats is returned by Task.all. Task (with a capital letter) refers to the model we created earlier, and we execute the method all on it so that it queries the Tasks table for all the records, equaling the SQL code “SELECT * FROM `tasks`”
# file: app/controllers/tasks_controller.rb def new @task = Task.new end
This method named new only sets up a new Task (not created in the database) and passed it to the page (or returned when the method is run)
# file: app/controllers/tasks_controller.rb
def create
@task = Task.new(params[:task])
if @task.save
flash[:notice] = 'Task was successfully created.'
redirect_to :action => "index"
else
@tasks = Task.all
render :action => "index"
end
endThis method actually creates the entry in the database. It takes the data passed to it through post and creates a new instance of a Task, and sets it to a class variable. Then, if the task is saved, the notice is returned and the user is redirected to the index. Otherwise, if it does not save, we set up the class variable with all the tasks that the index action requires, and then redirects them there.
# file: app/controllers/tasks_controller.rb
def finish
@task = Task.find(params[:id])
new = {:finished => true}
@task.update_attributes(new)
redirect_to :action => "index"
end
def unfinish
@task = Task.find(params[:id])
new = {:finished => false}
@task.update_attributes(new)
redirect_to :action => "index"
end
These two actions are about the same, with one difference, the finish method finds the task passed to it in the url, sets a local variable to the hash where the symbol finished is set to true. Then we update the row and redirect home. The unfinish method does the exact same thing, except it sets the symbol finished to false. If you are wondering why we are using a symbol here, (the colon in front of the name) it is because it refers to the column.
# file: app/controllers/tasks_controller.rb def destroy @task = Task.find(params[:id]) @task.destroy redirect_to(tasks_url) end
This last method is pretty self-explanatory, but I will still explain it. First we set a class variable to the task passed to it. Next, we destroy it. Then we redirect the user to tasks_url, which refers to the Tasks controller, and thus the index.
Now let’s create our view. Add a new file in app/views/tasks named index.rhtml
# file: app/views/tasks
<h1>Tasks</h1>
<% form_for @task do |f| -%>
<%= f.error_messages %>
<p>
<%= f.text_field :name %>
<%= f.submit 'Add' %>
</p>
<% end -%>
<hr />
<table>
<tr>
<p><b>Task</b></p>
</tr>
<% @tasks.each do |task| %>
<tr>
<td width="500"><%=h task.name %></td>
<% if task.finished %>
<td><%= image_tag("green.png", :size => "20x20") %></td>
<% else %>
<td><%= image_tag("red.png") %></td>
<% end %>
<td><%= if task.finished
link_to 'unmark finished', :controller => "tasks",
:action => "unfinish", :id => task.id
else
link_to 'mark finished', :controller => "tasks",
:action => "finish", :id => task.id
end %></td>
<td>| <%= link_to 'delete', task, :confirm => 'Are you sure?',
:method => :delete %></td>
</tr>
<% end %>
</table>
Hopefully this will look somewhat familiar, as it is somewhat html code. The part you might not understand is all the code in either the <%= %> and <% %> tags. This is ruby code. The first part where it says form_for, we are just creating a form by which we can add tasks.
@tasks.each do |task|
This part says, take each item in @tasks and repeat a block for each one with tasks the variable passed.
The code following then refer to columns for each row, so task.name would return the name of the task we are currently working with. Since, finished is a boolean value, we can test it to see what it returns and thus display either a greed or a red dot.
The last code you might be interested in would be the delete link, where we add in a confirm option, which automatically creates javascript (plain-old javascript, no library) that makes sure you really want to delete it.
The last file you will want to take a look at is the app/models/task.rb. We are going to add in some quick validation to make sure they entered a task, so the file will look like:
# file app/models/task.rb class Task < ActiveRecord::Base validates_presence_of :name end
Since name is the name of the column we want to make sure text is entered into, we add in that code.
There is one last thing we want to add is so that when we got to the site, the site automatically loads the tasks controller. There are two things that need to be done to make this happen. First delete index.html in the public folder. Next open up config/routes.rb. You will need to add in two lines to make sure everything works in the app. Do not forget to add the code before the end tag at the end.
# file: config/routes.rb map.resources :tasks map.root :controller => "tasks"
At the very end, you should have something that looks like this when you start up the local web server by running script/server in your terminal.

A quick note on using the final app: Since this is the final app, you may be confused on what to do so that you can run this app yourself. First you will need to open up the config/database.yml file and make sure the mysql settings are correct for your system on all three sections, development, production and test. Now once that is done, if you have not created the database yet, open up terminal and run:
rake db:create rake db:schema:load
This will create the database and create the tables defined in db/ schema.rb. After this, run script/server and welcome!
Cool tutorial. I am not really messing around with ROR anymore but will keep this bookmarked for a rainy day ![]()
To be quite honest about it, ROR confuses me greatly.
I’m totally new to rails, though I a little experience with rails.
For me, I had to type
$ rake db:migrate
as opposed to
$ rake dbmigrate
after creating the task model. I don’t know if this is something in my setup or if it’s a typo.
Thanks for the article!
Mike, thanks! That probably happened when I was replacing all the symbols with html character signs to make it display right.
And I am glad you enjoyed it!
I had to fix a couple of things in index.rhtml to get the above code working.
1. To create tasks, i had to change the form_for as
{:action => “create”} do |f| -%>
2.I had to change the code for delete to this
| “tasks”, :action => “destroy”, :id=> task.id, :confirm => ‘Are you sure?’ %>
Thanks a bunch. I was trying and trying to learn more about rails but never had the opportunity. You gave it to me.
Thanks again. Are you using .rhtml instead of .html.erb ’cause you want, right?
great tutorial, thanks mate.
September 3rd, 2009 at 11:38 am
hi, I am relatively new to the whole web development thing — I get this error when I attempt to browse to my default page: Mysql::Error: Table ‘todolist_development.tasks’ doesn’t exist: SHOW FIELDS FROM `tasks`
I believe I have created the database todolist_development, but it appears the table is not there. Did I miss a step?
Thanks!
September 3rd, 2009 at 12:49 pm
Ok, kind of a noob error…not too sure what I did wrong, but I’ll tell how I fixed. When I first downloaded mysql, I created the database using mysqladmin from the shell. When I was at the mysql prompt, the db didn’t exist! I had to add in the db under mysql for my user name, enter the table and columns along with datatypes and the app is now working…joy
December 13th, 2009 at 2:05 pm
Thanks for the tutorial. How do I get .rhtml files to open in Firefox? I’m pretty new at this. Double clicking open gedit. File > open in Firefox asks which app to use.
Ubuntu Karmic Koala, rails 2.3.5
thanks for writing this tutorial, this is really a big help specially to RoR noobs like me. ![]()
Hi, been reading your blog for a long period. I run a related blogging site although I keep receiving a pile of spam responses, how do you keep your blogging site so clean?
Thank you sir!!
Great tutorial!!!
Twitter
Follow me on Twitter to keep up to date!
RSS Feed
Keep up with all of our updates by subscribing to our RSS feed!
FaceBook
Join our group on Facebook and become a fan of us!