The Basics of Sinatra à la MVC — Configuration & Routes & Forms
Tags: ruby, sinatra, beginners
Published: 2018-07-24 04:29:37

---
title: The Basics of Sinatra à la MVC — Configuration & Routes & Forms
published: true
description: A mini-tutorial on getting started with Sinatra
tags: ruby, sinatra, beginners
---

Sinatra is a DSL that lets you easily get your application up and running on its own web server which can respond to HTTP requests and handle URI routing. It is actually built on [Rack](https://rack.github.io/), a webserver interface for Ruby apps. Compared to Rack, Sinatra is much easier to use.

This blog post goes over basic set for Sinatra using the MVC model and getting started with basic routes using the example of a simple recipe app.

## File Structure and Initial Setup/Configuration
The following is one of the basic ways to organize an app in a Model-View-Controller setup. Notably, all the main app files are organized inside an app folder with subfolders corresponding to each component of MVC: `/models` for your object model files, `/views` for your html template files, and `/controllers` for your controller files.

CSS stylesheets and Javascript often go into the public folder under their own subfolders. The spec folder is for spec tests, if you have them.

```
├── Gemfile
├── README.md
├── app
│   ├── controllers
│   │   └── application_controller.rb
│   ├── models
│   │   └── model.rb
│   └── views
│       └── index.erb
├── config
│   └── environment.rb
├── config.ru
├── public
│   └── stylesheets
│   └── javascript
└── spec
    ├── controllers
    ├── features
    ├── models
    └── spec_helper.rb
```

*Note that for smaller applications, sometimes a single application controller is all that is need and may be put directly in the root folder. The models and views folders may also be put directly in the root instead of an app folder.

## Gemfile

First, you’ll need the Sinatra gem and I’d recommend the Shotgun gem as well, so you don’t need to restart the Sinatra server during testing/development every single time you make a change. Make sure your Gemfile includes the following:

```ruby
# Gemfile

source "https://rubygems.org"
gem 'sinatra'
gem 'shotgun'
```

After the Gemfile is ready, make sure you run `bundle install` in the command line.


## Config.ru
Sinatra and Rack-based apps need a `config.ru` that loads the environment and other requirements of your app, specifies which controllers should be used via the `use` and `run` keywords, and starts the application server when `run` is called.

In the simple case below, the `application_controller.rb` file is our only controller and environment is loaded via the `config/environment.rb` file.

```ruby
# config.ru

require_relative './config/environment'
run ApplicationController
```

## config/environment.rb
This file gets our application code connected to the appropriate gems. It loads Bundler (and thus all the gems in the Gemfile) and all the files (models, views, and controllers) in the app directory.

```ruby
ENV['SINATRA_ENV'] ||= "development"
ENV['RACK_ENV'] ||= "development"

require 'bundler/setup'
Bundler.require(:default, ENV['SINATRA_ENV'])

require_all 'app'
```

(To use the handy require_all keyword, add gem `'require_all'` to the Gemfile. Check it out on [Github here](https://github.com/jarmo/require_all/).)


## Application Controller
Okay, let’s finally look at a basic controller for Sinatra. The controller’s job is to handle all the incoming requests, responses, and routing.

In the `application_controller.rb` file, let’s create a class for the application that inherits from `Sinatra::Base`. `Sinatra::Base` gives our app a Rack-compatible interface that can be used via Sinatra’s framework.

```ruby
# application_controller.rb
 
class ApplicationController < Sinatra::Base
  # code for the controller here...
end
```

To get the controller set up for the file structure above, add some configuration code that tells Sinatra where to find the `/views` folder (Sinatra looks for it in the root by default) and the `/public` folder using a `configure` block. After the configuration code, we can write our routes (I’ll go over some basics below).

```ruby
# application_controller.rb
 
class ApplicationController < Sinatra::Base
  configure do
    set :views, "app/views"
    set :public_dir, "public"
  end

  get '/' do
    "Hello World!"
  end

  # and more routes...
end
```

For a small application, a single controller will be enough and here we can define all the URLs and how they will respond to requests like ‘get’ and ‘post.’

Note that the application controller class can be named anything you like; just make sure it’s mounted with the appropriate name via `run` in the `config.ru` file. “Mounting” is just telling Rack which part of your application is defining the controller that handles web requests.

## Routes

### Basic routes
Routes are what connect requests from a browser to the specific method in your app (in the Controller) that can handle dealing with the request and sending a response. For example, on the simple side, a route might just show, or render, a basic HTML view. Or, another route might receive data submitted via a form, say a recipe title and its ingredients and steps, process that data, and then show the completed post–a new recipe, which is just another HTML view.

Some basic GET request could look like this:

```ruby
# application_controller.rb

get '/' do
  erb :index
end

get '/about' do
  erb :about
end
```

The `get '/' do` and `get '/about' do` lines correspond to the URLs in the browser. So, if the domain is `tastybites.com`,  `get '/' do` refers to that root domain.  `get '/about' do` would refer to `tastybites.com/about`.

The `erb :index` and  `erb :about` lines tell the controller which view file, in this case an embedded ruby file, in the views folder to get and show. So we would need to have a `index.erb` and an `about.erb` in a views folder for this to work.

As you can see, the view file is represented by a symbol of the same name in the route. Sinatra assumes that the view templates are all directly under the `/views` folder, so if the view happens to be nested in a folder in `/views`, say `/views/recipes/index`, we will need to refer to it as so: `erb :'recipes/index'` or `erb 'recipes/index'.to_sym`. For example:

```ruby
# application_controller.rb

get '/recipes/index' do
  erb :'recipes/show'
end

# Or the other way:
get '/recipes/index' do
  erb 'recipes/show'.to_sym
end
```

### Dynamic Routes
Dynamic routes can handle a HTTP request based on attributes in the URL. These attributes are represented by represented by symbols coded directly in the route and their values are easily accessible through the automatically generated `params` hash, so they can be used to look up or process data.

Let’s look at an example. Let’s say that in our recipe site at `tastybites.com` you want to be able to get individual recipes via their id in the URL (e.g. `tastybites.com/recipes/27`, with 27 representing the id). Obviously, we wouldn’t want to write out a route for every single recipe and its id. A symbol is used instead: `get '/recipes/:id'` do, where the `:id` could be any number. The value of `:id` is then accessible through `params[:id]`. Let see how this could be used to grab the proper recipe:

```ruby
# application_controller.rb

get '/recipes/:id' do
  # The :id is passed through the URL,
  # which is accessible in the params hash.
  # Use it to assign a recipe to an instance variable
  @recipe = Recipe.find(params[:id])
  erb :'recipes/show'
end
```

Notice how the params hash was used to look up a recipe from the database. That recipe was then assigned to an instance variable. Instance variables in Sinatra are super special because we can used them to pass data to our views! Which brings to me the next section…

### Passing data to view templates through instance variables
Whenever you create an instance variable with in a controller route, that variable is available within the corresponding view file. Note that the instance variables will not be available within other routes in the controller; only the view specified within a single route.

Let’s go back to the example above:

```ruby
# controllers/application_controller.rb

get '/recipes/:id' do
  @recipe = Recipe.find(params[:id])
  erb :'recipes/show'
end
```

This assumes you also have a recipe model with in your `app/models` folder, that could look something like this:

```ruby
# models/recipe.rb

class Recipe
  attr_accessor :title, :description, :ingredients, :method
 
  # The rest of your Recipe class code...
end
```

The recipe has a few attributes like a title, descriptions, etc., so once the recipe object has been assigned to `@recipe` in the controller route, we can weave all those attributes right into our ERB template:

```erb
# views/recipes/show.erb

<h1><%= @recipe.title %></h1>
<p><%= @recipe.description %></p>

<h2>Ingredients</h2>
<ul>
<% @recipe.ingredients.each do |ingredient| %>
  <li><%= ingredient %></li>
<% end %>
</ul>

<h2>How to Cook</h2>
<p><%= @recipe.method %></p>
```

We can even iterate through data in views. For example, let’s say in we want to show all the recipes in the recipe index page. First, we might assign all the recipes into an instance variable in the `recipes/index` route:

```ruby
# controllers/application_controller.rb

get '/recipes/' do
  @recipes = Recipe.all
  erb :'recipes/index'
end
```

Then, iterate over the recipes in the `recipes/index.erb` template:


```erb
# views/recipes/index.erb

<h1>All Recipes</h1>
<% @recipes.each.do |recipe| %>
  <h2>
    <a href="../recipes/<%= recipe.id %>"><%= recipe.title %></a>
  </h2>
<% end %>
```

Notice how we also linked to the recipe page using the recipe id, which will be processed by the `get '/recipes/:id'` route. Pretty cool.

Note that these instance variables do not need to be objects from models; they can be any variable that you want to assign and use in the view.

Now that we know how to get and use data from the URL, let’s take a look at processing data that gets sent through forms via the POST method.

###Passing data with forms and catching it with ‘post’ routes

Receiving user-input data from forms is the key to building web apps. We just need to correctly hook up your forms to our controllers. Let’s keep going with our recipe example–this time we’re going to create a recipe, so the first thing we’ll need is a basic form and route that will render it.

Setting up the route is as simple as connecting the URL for a new recipe post to the proper view, which is going to contain our form. In this case, let’s say we want the url to be `tastybitees.com/recipes/new`:

```ruby
# app/controllers/application_controller.rb

get '/recipes/new' do
  erb :'recipes/new'
end
```

Next, inside the `views/recipes/new.erb` file, we’ll set up a basic form:

```erb
# views/recipes/new.erb

<form method='POST' action='/recipes'> 
  <label for="title">Title</label>
  <input type="text" name="title">

  <label for="description">Description</label>
  <textarea name="description"></textarea>

  <label for="ingredients">Ingredients</label>
  <textarea name="ingredients"></textarea>

  <label for="method">How to Cook</label>
  <textarea name="method"></textarea>
  
  <input type="submit" value="Submit">
</form>
```

The `<form method='POST' action='/recipes'>` line is very important for setting up the route. The `action` attribute tells the controller what part of the code (that is, which route) should handle the form. Think of it like an address. The `method` attribute is simply how it’s going to get there, in this case via `POST`.

The other important part is the `name` attribute on each input tag, as this is what sets up our `params` hash. Above we have `name="title"`, `name="description"`, `name="ingredients"`, and `name="method"`, which correspond to the attributes in our Recipe model. This will produce a hash that will look something like this, depending on what the user submitted:


```ruby
{ 
  "title" => "Apple Pie",
  "description" => "A recipe that I learned from my granny.",
  "ingredients" => "4 apples, 1 cup sugar, ...",
  "method" => "Preheat oven to 350 degrees. Cut the apples in small pieces..." 
}
```

When the form is submitted, this data is now available in this handy-dandy `params` hash! So let’s set up the post route and use the params to make a new recipe:

```ruby
# app/controllers/application_controller.rb

# This is responsible for PROCESSING a newly submitted recipe form
post '/recipes' do
  @recipe = Recipe.new

  # get data from params
  @recipe.title = params[:title]
  @recipe.descriptions = params[:descriptions]
  @recipe.ingredients = params[:ingredients]
  @recipe.method = params[:method]
  @recipe.save
end
```

The only problem with this is that once the form has been submitted, the user is going to be shown a blank page. Here, `POST` just sends the information, it doesn’t display it afterwards. After the recipe data has been processed and completed, it makes sense to show the finished recipe to the user. Conveniently, Sinatra has a nice `redirect` method that will take the user to another page, in this case the recipe show page. Add it to the above route to get this:

```ruby
# app/controllers/application_controller.rb

post '/recipes' do
  @recipe = Recipe.new
  @recipe.title = params[:title]
  @recipe.descriptions = params[:descriptions]
  @recipe.ingredients = params[:ingredients]
  @recipe.method = params[:method]
  @recipe.save

  # show post after completion
  redirect "/recipes/#{@recipe.id}"
end
```

The `redirect "/recipes/#{@recipe.id}"` is going to send it to our recipe show route, `get '/recipes/:id'`, so the user can be proud of their new creation.

And there we have, the very basics of setting up a simple Sinatra-based app using MVC concepts! There are a million things you can do with routes and a good place to get more info is right on Sinatra’s [README page](http://sinatrarb.com/intro.html).