What are we building
I started to like Sinatra, so I have decided to rebuild the good old Todo app (I did in Flask in this post) in Sinatra. U figured it would help me understand some of the basics (e.g. how to access a database for example).
In this post, we’ll be building an extremely simple todo application in Ruby Sinatra. We will use the following URLs to perform CRUD operations on the database.
GET /todos Retrieves a list of all todo items in the database GET /todos/index Retrieves a single todo item from the database POST /todos Inserts a todo item in the database PUT /todos Updates a todo item in the database DELETE /todos Deletes a todo item from the database
Setting up some tools
A while ago, a colleague of mine told me about the Sinatra framework. As last months, I was pretty focused on Rails I was initially a bit sceptical to yet learn another framework. But boy…if I only knew this before. Sinatra is not new at all, it’s been around since a couple of years already (actually since 9th September 2007) and was creatred by Blake Mizerany. Sinatra is a small and flexible, free and opensource webframework written in Ruby, which does not follow the typical MVC pattern that is found in other frameworks like Rails for instance.
In this post, I wanted to create my good old todo app in Sinatra.
We will use the following gems for this (put the Gemfile in the rootdirectory):
- gem “sinatra”: obviously 🙂
- gem “sqlite3”: we will use sqlite in this application
- gem “activerecord”: facilitates the access to our database
- gem “sinatra-activerecord”: the bridge between Sinatra and the Activerecord interface
- gem “shotgun”: development server so that we don’t need to reload the webapp each time
- gem “tux”: ruby console
Our gem file looks like:
source :rubygems gem "sinatra" gem "sqlite3" gem "activerecord" gem "sinatra-activerecord" group :development do gem "shotgun" gem "tux" end
Obviously, next step:
$bundle install
We also need to create a rackup file. This would be needed if you want to deploy the app with a different Rack handler (Passenger, Unicorn, Heroku, …). Our config.ru file
require "./app" run Sinatra::Application
Creating the database
With ActiveRecord it would be nice to take advantage of database migrations. Therefore, we could add a Rakefile (again under the root folder). The content is:
# Rakefile require "./app" require "sinatra/activerecord/rake"
The “sinatra/activerecord” gen comes with a couple of handy Rake tasks so we can execute them from the command line. To see which rake tasks are available, use the rake -T command. We will use the create_migration task to create our db migration file.
$ rake db:create_migration NAME=create_todos db/migrate/20140811110015_create_todos.rb
You will now see a file called “20140811110015_create_todos.rb” under the db/migrate folder in your Sinatra app. We can now modify this file to create our database tables.
class CreateTodos < ActiveRecord::Migration def up create_table :todos do |t| t.text :content t.boolean :done t.datetime :completed_at t.timestamps end end def down drop_table :todos end end
Executing the following command to do the migration. This command will now create a database called todo.db in the root folder, containing a table called todos.
$ rake db:migrate == 20140811110015 CreateTodos: migrating ====================================== -- create_table(:todos) -> 0.0009s == 20140811110015 CreateTodos: migrated (0.0033s) =============================
Adding some records manually
As we added the Tux gem in our Gemfile, this little handy tool allows us now to store records in our database from the command line.
$ tux Loading development environment (Rack 1.2) >> t = Todo.new(content: "First Todo item", done: false) => # >> t.save D, [2014-08-11T13:04:20.915230 #31773] DEBUG -- : (0.1ms) begin transaction D, [2014-08-11T13:04:20.924566 #31773] DEBUG -- : SQL (0.6ms) INSERT INTO "todos" ("content", "created_at", "done", "updated_at") VALUES (?, ?, ?, ?) [["content", "First Todo item"], ["created_at", "2014-08-11 11:04:20.916906"], ["done", "f"], ["updated_at", "2014-08-11 11:04:20.916906"]] D, [2014-08-11T13:04:20.939124 #31773] DEBUG -- : (13.9ms) commit transaction => true >> Todo.count D, [2014-08-11T13:04:26.180073 #31773] DEBUG -- : (0.2ms) SELECT COUNT(*) FROM "todos" > 1
Writing the app
What we did until now was some preparation work to get our app setup correctly. We didn’t write any decent code yet. As mentioned in the introduction, Sinatra does not really follow an MVC pattern. This means there is a lot of flexibility on how we structure everything. To keep things easy, we will just create an app.rb file under the root directory. Put the following code in the app.rb file:
require "sinatra" require "sinatra/activerecord" set :database, "sqlite3:todo.db" class Todo < ActiveRecord::Base end # Get all of our routes get "/" do @todos = Todo.order("created_at DESC") erb :"todo/index" end
Basically, this code will result in a list of all the todo items when the / route is requested from the webserver. Obviously, since we call the erb “todo/index”, we need to create an index.rb file in the views>todo directory. This file should contain the following code:
To do application
To do application
<ul> <% @todos.each do |todo| %> <li> <h2><a href="/todos/<%= todo.id %>"><%= todo.content %></a> <span><%= todo.created_at %></span> </h2> </li> <% end %> </ul>
To run the code, we will use shotgun. This tool allows us to rerun the application without manually having to restart the server. Note: shotgun is using port 9393 by default, so in your development enviroment, you will find the app through localhost:9393
$ shotgun == Shotgun/WEBrick on http://127.0.0.1:9393/ [2014-08-11 13:56:34] INFO WEBrick 1.3.1 [2014-08-11 13:56:34] INFO ruby 2.0.0 (2014-02-24) [x86_64-linux] [2014-08-11 13:56:34] INFO WEBrick::HTTPServer#start: pid=32423 port=9393
The final result
Eventually, this is what we will get:
Admittedly, this is not something very nice and in a next post we will apply some bootstrap to it, so that it starts to look a bit nice. Stay tuned!!
Note: for this blog post I did not add routes to add, update or delete todo items. For those interested, this can be found here. Also, the full code is available on Github.