Introduction to Web development with Sinatra and Build a Todo App.

Peter Ayeni
10 min readNov 21, 2019

Build a simple Todo App with Ruby using Sinatra and give it a good look using CSS.

Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort: — Official Website

Getting started with web development can be so confusing for beginners it not because it so hard to start, beginners are just presented with good problems of so many good tools to choose from. So I think it doesn’t matter the tools you choose given your user a good experience and a web application that solve their problem is the most important thing.

And that is where Sinatra shine, just like the definition above Sinatra help you get started with building web application so quickly and with minimal understanding of Ruby you can start to put amazing things together and this will motivate you to continue to learn and build upon your knowledge before going to the more powerful framework like Rails.

For this tutorial, we are going to be building a simple Todo App that helps users list what they want to do for the day. They can add to their list, see their list show up and then delete what they have done. Simple but will give you the concept of how the web works and you can build on top of that. Plus I make it pretty also so you will pick up some CSS, HTML and ERB. So let’s dig in.

Page user see when they have no todo
Page with todos and delete button

Because Sinatra is a lightweight Ruby Gem you will need other ruby gems to make your development easy. To get you off the ground I here is the link to the repo for this tutorial with the development environment already set up for you. I will quickly go through the important files and in the tutorial explain some in details. If you are new to Ruby check out my previous tutorials.

The app directory is where all our app code is, the config directory has our configuration files, that connect to the database, set our environment, and require our app directory to put all the files together. The db directory like the name suggests holds everything about our database and migrations, the public directory holds our static files like CSS, Images and Javascript, the spec holds our test files, we will not be discussing this today. This setup is put together by Flatiron school, you can check out their amazing introduction to Ruby. And if you are considering a Bootcamp Flatiron is a go-to place. Take time to poke around the folders it will make sense.

Then cd to the project directory on your terminal and then run bundle install to install all the required files in the gemfile. Then make sure you have SQLite install we are going to be using it as our development database. We are also going to be using Rake as our task runner. If you run rake -T you will see the list of tasks you can run. Because we will be using ActiveRecord for this tutorial we are not going to be writing any SQL, ActiveRecord will be doing that for us. Read more about ActiveRecord here.

If you run rake -T

➜  sinatra-todo git:(master) ✗ rake -T
/Users/peterayeni/.rvm/gems/ruby-2.6.1/gems/activesupport-4.2.5/lib/active_support/core_ext/object/duplicable.rb:85: warning: BigDecimal.new is deprecated; use BigDecimal() method instead.
rake db:create # Creates the database from DATABASE_UR...
rake db:create_migration # Create a migration (parameters: NAME,...
rake db:drop # Drops the database from DATABASE_URL ...
rake db:fixtures:load # Load fixtures into the current enviro...
rake db:migrate # Migrate the database (options: VERSIO...
rake db:migrate:status # Display status of migrations
rake db:rollback # Rolls the schema back to the previous...
rake db:schema:cache:clear # Clear a db/schema_cache.dump file
rake db:schema:cache:dump # Create a db/schema_cache.dump file
rake db:schema:dump # Create a db/schema.rb file that is po...
rake db:schema:load # Load a schema.rb file into the database
rake db:seed # Load the seed data from db/seeds.rb
rake db:setup # Create the database, load the schema,...
rake db:structure:dump # Dump the database structure to db/str...
rake db:structure:load # Recreate the databases from the struc...
rake db:version # Retrieves the current schema version ...

Above is the list of tasks you can run with Rake first you should run rake db:create this will automatically create a database for us. Then run: rake db:create_migration NAME=create_todos a file will be automatically generated in the db/migrations put the content below in it

class CreateTodos < ActiveRecord::Migration  def change    create_table :todos do |t|      t.string :todo      t.timestamps    end   endend

We asking ActiveRecord to create a table called todos with a column called todo with a type of string and timestamps that will automatically create additional two columns called created_at and updated_at.

That's all we need as much as the database is a concern for our app, all we want is user to be able to add a todo, see all their todo, and delete it. Simple right lets move on. Now we need to run the migration with the help of Rake again we just need to run: rake db:migrate in our terminal

➜  sinatra-todo git:(master) ✗ rake db:migrate
/Users/peterayeni/.rvm/gems/ruby-2.6.1/gems/activesupport-4.2.5/lib/active_support/core_ext/object/duplicable.rb:85: warning: BigDecimal.new is deprecated; use BigDecimal() method instead.
== 20191120153020 CreateTodos: migrating ======================================
-- create_table(:todos)
DEPRECATION WARNING: `#timestamps` was called without specifying an option for `null`. In Rails 5, this behavior will change to `null: false`. You should manually specify `null: true` to prevent the behavior of your existing migrations from changing. (called from block in change at /Users/peterayeni/Documents/projects/sinatra-todo/db/migrate/20191120153020_create_todos.rb:6)
-> 0.0006s
== 20191120153020 CreateTodos: migrated (0.0006s) =============================

To run start the application run shotgun in your terminal and visit the localhost URL generated.

➜  sinatra-todo git:(master) ✗ shotgun
== Shotgun/WEBrick on http://127.0.0.1:9393/
[2019-11-21 13:02:34] INFO WEBrick 1.4.2
[2019-11-21 13:02:34] INFO ruby 2.6.1 (2019-01-30) [x86_64-darwin18]
[2019-11-21 13:02:34] INFO WEBrick::HTTPServer#start: pid=55158 port=9393

Now if you have SQLite browser install you can then see our table have been created.

Now if you see our app folder you will see we have three folders controllers, models and views this is what makes up the MVC of you are just hearing about it. MVC is a pattern of web development that is very popular and we are going to be using it.

M: Model

To the first M in our, MVC create a file call todo.rb in our models folder that will hold all our logic for our database.

class Todo < ActiveRecord::Base  def created_time    self.created_at.strftime("%F %T")  endprivateend

We create a class Todo that inherits from the ActiveRecord Base this makes our Todo class a superman in anything that concerns working with SQL. We also have an instance method created time that help us nicely format our created_at attributes and make it more easy to read when we present it on the web. Cool right that’s it for our model.

C: Controller

class ApplicationController < Sinatra::Base  configure do    set :public_folder, 'public'    set :views, 'app/views'  end# code actions here!end

Above is our application_controller we have a simple configuration file that helps us tell Sinatra about our Static Files and application views. This way Sinatra know where to look for when loading these files.

Then to our main controller todos_controller, this file holds the logic for our todo controller. Controller bridge the gap with the Model and Views. It listens to the user request talk to the Model to get required data put it together and forward it to appropriate View to present ot to the User.

class TodosController < ApplicationController  get '/' do    @todos = Todo.all    erb :'todos/index'  end  post '/' do    @todo = Todo.create(params)    redirect '/'  end  delete '/:id' do    id = params[:id]    Todo.destroy(id)    redirect '/'  endend

Here we have three routes making get, post and delete request all on a single page. That is what makes HTTP pattern cool, you can make a different request to a single route and each of them will do different things depending on the verb.

stackoverflow.com

In this picture, we see a single resource /photos and different kind of request that can be made to each of the base on the action a user one to perform.

get: Our first request is the get to the index route ‘/’ and what this route does is define an instance variable todos and ask the database to get all Todo and store it there, then this instance variable is available to the view that gets called via erb :’todos/index’ in our view folder you will see a folder todos where all the views related to this resource are located. The index have the view template to render the home page. ERB is a templating engine for Ruby you can read more about it here. It allows you to embed Ruby syntax together with HTML and render and HTML to the user. Sweet!

The code is available in the repo for this course, if you gently read through you will see that the code is basically self-explanatory. In the If section we are first checking if our instance variable is not empty, if it is empty we render the header with a class of “list-todo__empty” to tell the user they don’t have a todo.

And if it is not empty we loop over the todos instance variable to list each of the todos on the page. The rest is just fancy HTML to render our page nicely.

The next part of the controller is the post where we allow a user to create a todo by using a form. You will see that I abstract the form away to another erb file of is own to make our page easier to read and embed it via

<header class="add-todo__header">  <h1 class="add-todo__title">SimpleTodo</h1>    <%= erb :'todos/form'%>  <p class="info">Crafted with love by: @peterayeni<p></header>

So we have our form here

<form action="/" method="post"> <input class="add-todo__text" type="text" required value=""    name="todo" placeholder="Whats on your mind..."> <input class="add-todo__btn" type="submit" value="Add to do"></form>

You can see our form action go to the same index route “/” and use the method post because that is why the controller knows hmmm the user is trying to create a todo. Clever right!

The last part of our controller is the delete part and this is a little tricky because we don’t have a way to tell our form we are sending a delete method it always defaults to “post” so we need to get back to our server engine Rack that Sinatra is built upon and tell him to hijack our delete request and change the method to delete on submit. So how do that work?

In our config.ru file, we have this line use Rack::MethodOverride this help us to do the magic in the background then in our delete form we need to do this.

You will see in addition to our delete button input will also have a hidden button type this what Rack need to interject the request and change it to delete.

<input type='hidden' name="_method" value="delete">

And then our delete button can successfully send a delete request. So how does it no what to delete. When we loop over the list of todos we are able to get each of the todo and store it in a local variable ‘t’ that is inturn composed with our action url to get the right todo to delete.

<form action="/<%= t.id %>" method="post">

And there we go. Our app is done, there is a lot that can be done, we can allow users to mark a todo done, edit a todo and so many. I keep it simple for instruction purpose and also to give you a head start to create something that will motivate you to continue to explore.

Styling

I am already doing a series on HTML, CSS and JavaScript building Flatiron School homepage. The best way to learn CSS is by tweaking, Googling and seeing how others solve problems and find the best away that works for you.

On a high level, I use Flex for layout, box-shadow to make the shadow and if you go through the code you will pretty understand whats going on. Try and change some parameters and see how it changed the design. To run the server just type shotgun in your terminal

➜  sinatra-todo git:(master) ✗ shotgun
== Shotgun/WEBrick on http://127.0.0.1:9393/
[2019-11-21 13:02:34] INFO WEBrick 1.4.2
[2019-11-21 13:02:34] INFO ruby 2.6.1 (2019-01-30) [x86_64-darwin18]
[2019-11-21 13:02:34] INFO WEBrick::HTTPServer#start: pid=55158 port=9393

Shotgun automatically reload your changes as you develop.

There we have it! Now we can Dance

Thanks for reading post any question and feedback in the comment. See you again soon.

--

--

Peter Ayeni

Senior Frontend Engineer. I change the world by helping people get started in Tech and Social Entrepreneurship.