Using Sinatra to Create Forms

Monajaved
10 min readJan 24, 2021

Our most recent project from Flatiron School required us to create a web page that had a form in it using Sinatra. This was probably the first time I felt like a programmer because of all the web pages we had to create. It was super exciting and I want to share what I learnt through this project.

First things first, you need a file structure. I used the corneal gem that basically creates this and also adds a lot of other sinatra related code that I will explain later.

The basic file structure you actually need is as follows: (corneal adds a few extra files)

>app
>controllers
>models
>views
>config
>environment.rb
>db
>migrate
>schema.rb
>config.ru
>Gemfile
>Gemfile.lock
>Rakefile
>Readme.md

I used materialize for css styling which is a UI component library created with CSS, JavaScript and HTML, you can instead add another folder called “public” with files “images”, “javascript” and “stylesheets” should there be a need. You can also add a folder called “test” if you are going to test your project. I however, did not use testing for this project.

To use materialize all you need to do is add the following link to your layout.erb.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

So before we dive in, what is Sinatra? Sinatra is a Domain Specific Language used for quickly creating web applications in Ruby with minimal effort. All you need to do is type “gem ‘sinatra’” in our gemfile and bundle install.

List of gems I used for this project -

source 'http://rubygems.org'gem 'sinatra'
gem 'activerecord', '~> 4.2', '>= 4.2.6', :require => 'active_record'
gem 'sinatra-activerecord', :require => 'sinatra/activerecord'
gem 'rake'
gem 'require_all'
gem 'sqlite3', '~> 1.3.6'
gem 'thin'
gem 'shotgun'
gem 'pry'
gem 'bcrypt'
gem 'tux'
gem 'dotenv'
group :test do
gem 'rspec'
gem 'capybara'
gem 'rack-test'
gem 'database_cleaner', git: 'https://github.com/bmabey/database_cleaner.git'
end

I used another gem called ActiveRecord for our models. ActiveRecord is a gem that is a part of Ruby. It basically helps in Object Relational Mapping, i.e. it takes data which is stored in a database table using rows and columns, which needs to be modified or retrieved by writing SQL statements (if you’re using a SQL database), and it lets you interact with that data as though it was a normal Ruby object. For example, you won’t need to type SQL specific commands to search through your database, you will just need to type regular ruby commands to get the data you need. Pretty cool, right?

So now that we have our file structure and the gems sorted, we can move on to think of our models and the relations between these models.

But before we get there let’s talk about knowing the flow of your project. It is very important to grasp this concept to truly understand what is going on in your application.
The MVC structure of the application plays a key role in this.
Look at it this way, the restaurant example, the chef is the model of your application, the waiter is the controller and the view is the customer. The customer cannot directly converse with the chef, just like the views of your application cannot directly talk to your models. This is where controllers come in. The controller takes the information from the user and passes it to the models; the models deal with the database of the application and then send the information back to the controller which then renders the appropriate erb file for the user.

Here’s a diagram to make it easier to understand.

Now that we have that settled lets get back to the application.
I had this idea of creating an app that basically helps you in starting any sort of activity by helping you make a list of items you would need to get started. Really wanted to take up hiking as a serious hobby and we just did not know what all to buy or where to get started. Enter Flatiron project!

Whenever you are thinking of starting a new project, the key is to think about what sort of models are you going to have. This is something that can be changed later on so it’s not very important to figure it all out in the beginning. For my project, an activity model was definite. This activity is going to contain items, so that means we are going to have another model called Item. Now to think of the relation between them. We know that Activity Model is going to have many Items and Items belong to an Activity. That means there’s a ‘has many’ and ‘belongs to’ relationship between these models.

I also went ahead and created a join table so it would create a whole of code for me to connect these two models. My join table is called “activity_item”. Now our models are connected through this join table. That means our initial declaration of a ‘has many’ and ‘belongs to’ changes slightly. Since my application is going to be user based, we are going to have a user model as well. Our Activity model is going to belong to this User.

class Activity < ActiveRecord::Basebelongs_to :user
has_many :activity_items
has_many :items, through: :activity_items
validates_presence_of :title
end
class Item < ActiveRecord::Basehas_many :activity_items
has_many :activities, through: :activity_items
validates :name, uniqueness: true
end
class ActivityItem < ActiveRecord::Basebelongs_to :item
belongs_to :activity
end
class User < ActiveRecord::Basehas_many :activities
has_many :items, through: :activities
has_secure_password
validates :username, presence: true, uniqueness: true
end

That is how we establish the relationships between our models. ActiveRecord gives us multiple lines of code just from establishing these relationships.

A side note on “has_secure_password”; this is another meta-programming way that basically gives our application a way to encrypt the password a user types in. Basically this line of code makes the developer not read the actual the password. This functionality is given to us by the the gem ‘bcrypt’.

Now let’s get to the database part of our project.
My activities table is going to have a string of title and an integer for user id, whereas items table is going to have a string of name. Our join table activity_items is going to have integers of both activity id and item id.
Our users table is going to contain username and password_digest. This “password_digest” is another method that is given to us by the gem ‘bcrypt’. So we are going to go ahead and create the migrations for these by typing in “rake db:create_migration NAME= *name of the table*” and then after these are created you need to run rake db:migrate in your terminal so these tables are actually migrated.

So now that we have the models and our databases established. We can move on to the controller and views section of the project.

We are going to have an “application_controller.rb” that is going to inherit from Sinatra like:

require './config/environment'class ApplicationController < Sinatra::Baseend

This controller is also going to require the environment.rb file in config that has all the code that establishes the connection with ActiveRecord and Sinatra.

Our other controllers are going to inherit from this ApplicationController. Why have multiple controllers? There can be multiple routes directed that can make it very hard to understand if you put all these routes in one controller. Imagine having 14 routes in one controller. With coding, you want to keep your code as clean and easy to read as possible.

Since we are talking of code being easy to read and controllers, let’s get to RESTful routes. Representational State Transfer (ReST) is a web architecture style coined by Roy Fielding. A RESTful route is a route that provides mapping from HTTP verbs (get, post, put, delete, patch) to controller CRUD actions (create, read, update, delete).

An example of ReSTful routes along with CRUD actions.

GET requests are basically the verb that displays the html (erb) file to the user. POST requests are used to create a form rendered through the photos/new. The PUT request updates an existing form. PUT basically updates the whole form again while PATCH request updates only the fields that have been changed. DELETE request is pretty simple, it deletes the object entirely.

I used three controllers for my project. My ApplicationController consists of the code required to enable sessions which means that a user who is logged in can have access to only his session and logging out would clear his session.

Note: All the code pasted below is only to give you a rough idea of the application, most of the key information and methods are missing.

class ApplicationController < Sinatra::Base
configure do
enable :sessions
set :public_folder, 'public'
set :views, 'app/views'
set :session_secret, '*******'
register Sinatra::Flash
end
get "/" do
erb :welcome
end
# bunch of helper methods related to sessionsend
class ActivitiesController < ApplicationController# The line erb :'activities/index' basically means that we are rendering the 'activities/index' erb file in the /activities route.  get '/activities' do
erb :'activities/index'
end
get '/activities/new' do
erb :'/activities/new'
end
get '/activities/:id' do
erb :'activities/show'
end
get '/activities/:id/edit' do
erb :'/activities/edit'
end
post '/activities' do
activity = current_user.activities.new(title: params[:activity])
if activity.save
redirect '/activities'
else
redirect '/activities/new'
end
end
patch "/activities/:id" do
if @activity.update(params[:activity])
redirect "/activities/#{@activity.id}"
else
redirect "/activities/#{@activity.id}/edit"
end
end
delete '/activities/:id' do
@activity.destroy
redirect "/activities"
end
end

ERB stands for embedded ruby which means that we can use ruby code in our html files.

Our sessions_controller.rb file looks something like this:

class SessionsController < ApplicationController  get '/signup' do
erb :'sessions/signup'
end
post '/signup' do
user = User.new(params[:user])
if user.save
session[:user_id] = user.id
redirect '/activities'
else
redirect "/signup"
end
end
get '/login' do
erb :'/sessions/login'
end
post '/login' do
user = User.find_by_username(params[:user][:username])
if user && user.authenticate(params[:user][:password])
session[:user_id] = user.id
redirect "/activities"
else
redirect "/login"
end
end
get '/logout' do
session.clear
redirect '/login'
end
end

SessionsController is basically taking care of the signup/login/logout part of the application. It’s also authenticating the user, i.e. making sure the user is only able to see his own page and is able to log in with his password.

Now that our controllers are set, we need to get to the view part of our project. It’s usually good practice to work on your controller along with the view files but for the purpose of writing this and making it easier to understand I did both controllers and views separately.

Our views is going to have the following structure:

>views
>activities
edit.erb
index.erb
new.erb
show.erb
>sessions
login.erb
signup.erb
layout.erb
welcome.erb

Notice the relationship between the controller and the way the files have been named here. Sinatra uses this method to connect the two while rendering the erb files.

Our layout.erb is going to be the display page that contains the navbar and the footer that needs to be displayed in all of our other erb files.
It does this by using the <%= yield %> line.
The symbol <% %> is what makes us able to use ruby in our html files.
The welcome.erb page is going to be rendered while we are on the ‘localhost:9393/’ url.

The activity/new is going to render a form that will allow us to create a new activity and list the items on the same page that we wish to select.

<h5> Type in the Activity you are interested in:</h5>  <form action="/activities" method="post">
<div class="input-field col s6">
<input id="title" name="activity[title]" type="text"> <label for="title">Title</label>
</div>
<h5> The Items you would need to get started:</h5>
<p>
<% Item.all.collect do |i| %>
<label>
<input type="checkbox" name="activity[item_ids][]" value="<%= i.id%>"/>
<span><%= i.name %></span><br>
</label>
<% end %>
</p>
<input type="submit" value="Create" class="btn green lighten-3">
</form>

While creating these forms, it is necessary to correctly call on the models you established earlier. For example the following line:

class Activity < ActiveRecord::Basehas_many :activity_items
has_many :items, through: :activity_items
end

makes it easier for us to insert the “activity[item_ids][]” in the name category while listing the items. The empty array is a functionality given to us with erb. If we are dealing with nested arrays, we do not need to type in the value of each array. Instead we can use an empty array ([]) in our form view, and ERB will automagically index the array for us.

Notice the use of method=”post” in our form. That is the request that is basically creating this form.

Our edit form will be very similar to the ‘/new’ form. Except for the following lines:

<form action="/activities/<%= @activity.id %>" method="post">
<input type="hidden" name="_method" value ="patch">
---<input type="submit" value="Update">

So for our edit form, a couple of things have changed, now we want to refer to the activity selected by the user, that’s the reason why we have @activity.id in embedded ruby. We are basically only displaying the activity selected by the user. The input type=”hidden” and name=”_method” are methods we get from the “use Rack::MethodOverride” line in our config.ru file.
This basically lets us update our form. Also notice the use of “patch” in the value. This is because we are updating an already existing object of our activity model.

Our index page is just going to have basic functionality where our @activities are going to be iterated over and displayed by their title.

The show.erb is going to display the @activity.title of the activity selected along with the items listed under it.

<h4><%= @activity.title %></h4><br><ol>
Items Needed:
<% @activity.items.each do |i| %>
<li><%= i.name %> </li>
<% end %>
</ol>
<form action="/activities/<%= @activity.id %>" method="post"><input type="hidden" name="_method" value="delete"><input type="submit" value="Delete Activity" class = "btn-small">
</form>

This time; to list the items names we are going to iterate over @activity.items; again this is because of the earlier relationship we established in our models.
The delete functionality is at the bottom of the show form where we basically use the Rack::MethodOverride functionality and delete the activity. There is no specific rendering of any page in this case.

You can go ahead and create the login and signup forms similarly.
Your signup.erb should look something like this.

<h4>Create a User Account</h4>
<form action="/signup" method="post">
<div>
<input type="text" name="user[username]" id="username">
<label for="username">Username</label>
</div>
<div>
<input type="password" name="user[password]" id="password">
<label for="password">Password</label>
</div>
<input type="submit" value="Signup"></form>

Hopefully this information proves helpful to you when you go on to building an application with the help of sinatra.

--

--