Sinatra Todo app with Datamapper using Sqlite

By | 12/09/2014

In this blog post, I’m continuing to use my To Do application that was build in this post and this post, but instead of using the ActiveRecord ORM, I will be using the Datamapper ORM. An example of the final result is shown in the picture above.

First thing to do is to configure Datamapper. To achieve this, you need to include the datamapper gem as well as the sqlite3 and dm-dqlite-adapter in your Gemfile. The Gemfile I’m using looks as follows:

source :rubygems
 
gem "sinatra"
gem "sqlite3"
gem "datamapper"
gem "dm-sqlite-adapter"
gem 'rack-flash', '0.1.2'

group :development do
  gem "shotgun"
  gem "tux"
end

To really setup Datamapper, you will need to do the following in the app.rb file

require "sinatra"
require "data_mapper"

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/todo.db")

The above tells Datamapper to setup a SQLite database , called todo.db, in the current location.

The next thing to do really, is to define the model for the database. In below snippet, you can see that we create a class Todo (a database table really) that has some fields like the id, content, done and then two DateTime fields to keep track of when a todo item was created and when it was finished. Also notice the id is of type Serial, which is Datamappers way of saying this is a primary key (auto-increment).

class Todo
  include DataMapper::Resource  
  property :id,           Serial
  property :content,      String
  property :done,         Boolean,  :default => false
  property :completed_at, DateTime
  property :created_at,   DateTime
end  

DataMapper.finalize
Todo.auto_upgrade!

The DataMapper.finalize method is used to check the integrity of all the models you defined. It should be called after all your models have been created and before your app starts interacting with them.

Next, we will need to define the routes for the CRUD operations.

  get "/todos/?" do
    @todos = Todo.all(:order => :created_at.desc)
    erb :"todo/index"
  end

For your information, the corresponding method to retrieve all todo items using ActiveRecord can be seen in below snippet. Not a lot of difference all in all.

  get "/todos" do
    @todos = Todo.order("created_at DESC")
    erb :"todo/index"
  end  

In the above code snippet, we call an ERB file called index in the todo folder under the views directory. Go ahead and create this todo folder. The index file is as follows:

<table class="table table-bordered">

<div class="control-group">
  <a href="/todos/new" class="btn btn-primary">Add todo item</a>
</div>
     <thead>
        <tr>
            <th>Id</th>
            <th>Todo item</th>
            <th>Created</th>
            <th>Completed</th>
            <th>Done</th>
            <th>Actions</th>
        </tr>
     </thead>
     <tbody>
      <% @todos.each do |todo| %>
        <tr>
          <td><%= todo[:id] %></td>
          <td><%= todo[:done] ? "<del>#{todo[:content]}</del>" : todo[:content] %></td>
          <td><%= pretty_date(todo[:created_at]) %></td>
          <td><%= pretty_date(todo[:completed_at]) %></td>
          <td>
            <% if todo[:done] %>
              <span class="label label-success">Completed</span>
            <% else %>
              <span class="label label-warning">Pending</span>
            <% end %>
          </td>
          <td><a href="/todos/edit/<%= todo[:id] %>" class="btn btn-primary">Edit</a>
              <a href="/todos/delete/<%= todo[:id] %>" class="btn btn-danger">Delete</a>
          </td>              
        </tr>
      <% end %>
     </tbody>
</table> 

You’ll see that I define a table using some bootstrap styling. This table has a heading (thead) to define all the columns and a body (tbody)containing all the rows (tr) with the data retreived from the Todo Model.

Note: before you can run this app locally, you have to create the database. Therefore you need to first do “rake migrate” in your console before you start the app using “shotgun”.

For the complete example, including all routes and views, I refer to my Github repository. You can find it here