First Steps with Sinatra as a Rails Developer - Part 1

Photo of Grzegorz Unijewski

Grzegorz Unijewski

Apr 18, 2018 • 12 min read
max-nelson-492729-unsplash

Some time ago, I had a chance to build a very small app handling HTTP requests coming from Slack.

I decided to choose a lightweight framework, so I skipped Rails in favour of Sinatra. In this blog post, I would like to help you out with the first steps toward using something different than Rails, but from the perspective of a Rails developer.

Why? Well, a lot of Ruby developers might be called Rails developers because the only framework they have ever used is Rails. As you can see here, there are many other web frameworks for Ruby, and I’m pretty sure that most of them sound strange to you. Let me introduce Sinatra!

What’s Sinatra?

According to the website,

Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.

In other words, it’s a microframework for Ruby, which, in the very basic version, offers us some settings, routing, and a few request helpers.

How to start?

# Install the gem
gem install sinatra

# my_app.rb
require 'sinatra'

get '/' do
 'Hello world!'
end

ruby my_app.rb

The snippet above is the most basic example of an endpoint created in Sinatra, available at http://localhost:4567.

Classic vs. modular apps

In Sinatra, you will basically encounter two types of applications. Classic applications are often a single file run from the command line, and with no more than one app per process. When it comes to modular applications, they can be used for building a more complex app or a library. Each of them has different default settings.

Setting

Classic

Modular

Modular

app_file

file loading sinatra

file subclassing Sinatra::Base

file subclassing Sinatra::Application

run

$0 == app_file

false

false

logging

true

false

true

method_override

true

false

true

inline_templates

true

false

true

static

true

File.exist?(public_folder)

true

  • app_file and run indicate how we load the main app file. With the classic style, we use the built-in web server by simply running a Ruby script file.
  • logging writes requests as single lines to STDERR.
  • method_override is especially useful in form submissions to make a hack with POST requests that may look like PUT or DELETE methods.
  • inline_templates: if you want to include a view template in the same file where your endpoint is, it needs to be set as true.
  • static says whether we provide static files, usually located in the public directory.

Other settings are available here. In this blog post, we’ll follow the second style, inheriting from Sinatra::Base.

# my_app.rb
require 'sinatra/base'

class MyApp < Sinatra::Base
  get '/' do
    'Hello world!'
  end
end

Don’t forget about config.ru

When should we add a config.ru file to the app? Do it if you want to:

  • deploy with a different Rack handler (Passenger, Unicorn, Heroku etc.);
  • use more than one subclass of Sinatra::Base;
  • use Sinatra only for middleware, and not as an endpoint.

Then the file might look as follows:

require './my_app'
run MyApp

File structure and autoloading

If we want to apply some kind of a file structure or even make our app similar to a Rails app, we should be aware of one important thing: autoloading. In Rails, it happens out of the box, but here, we need to do it on our own. One of the ways might be creating an initializer at config/initializers called autoloader.rb that would iterate over specified files and require them.

# config/initializers/autoloader.rb
paths = %w[config/initializers/*.rb app/**/*.rb].map(&:freeze).freeze
paths.each do |path|
 Dir[File.join(MyApp.root, path)].each do |file|
   next if file.include?('initializers/autoloader') # skip me
   require file
 end
end

And, of course, we need to require the file manually at the end of my_app.rb.

class MyApp < Sinatra::Base
  set :root, File.dirname(__FILE__)
  …
  require File.join(root, '/config/initializers/autoloader.rb')
end

Be careful, though! If you use the inheritance, for instance, in a service, you should either use require_relative with the name of the parent class in the service or specify it directly in the autoloader. Otherwise, you’ll get an error.

Reloading

Rails offers us a lot of magic and makes us a bit lazy. In Sinatra, we need to think as if we were building the app with LEGO bricks. In this case, another brick is reloading. By default, if you introduce a change in a file, you won’t see the change until you reload your app manually. How to avoid this? There are two ways:

Sinatra::Reloader

In its repository, Sinatra contains a collection of semi-officially supported extensions called Contrib. You will find there the reloader, which reloads Ruby files upon code changes.

require 'sinatra/base'
require 'sinatra/reloader'

class MyApp < Sinatra::Base
  configure :development do
    register Sinatra::Reloader
  end
  # Your modular application code goes here...
end

So, in development environment, we have to register the extension, earlier require it and voilà! If you don’t like it, there’s an another option (see below).

Shotgun

Shotgun is an alternative to the complex reloading logic provided by web frameworks or in environments that don't support application reloading. All you need to do is:

# Gemfile
group :development do
  gem 'shotgun'
end

# Run the command
shotgun

# == Shotgun/Puma on http://127.0.0.1:9393/
# …

Okay, but which one should you use? In my opinion, Sinatra::Reloader works a bit faster, but it doesn’t load new files, only changes in existing ones, so… it’s up to you. ;)

Gems

What about gems in Sinatra apps? Basically, most gems used in Rails apps are also supported in Sinatra, so no worries. However, there’s one thing that you should be aware of when adding more and more gems. If you want to use them within the app, you should require them in the main file. In my case, after adding a few gems to my app, the beginning of the file started looking like this:

require 'app_konfig'
require 'haml'
require 'httparty'
require 'pry'
require 'puma'
require 'sidekiq'
require 'sinatra/base'
require 'sinatra/reloader'

It could look even worse, depending on how many gems you want to use. To solve this problem, there's a perfect solution provided by Bundler.

# my_app.rb
require 'bundler'
Bundler.require(:default, ENV.fetch('RACK_ENV', 'development'))

These two lines do the job and allow us to forget about requiring every gem we need explicitly.

Finally, you might have noticed that for Sinatra, I needed to require base and reloader. How to do it with the solution above? Specify them in your Gemfile:

gem 'sinatra', require: %w(sinatra/base sinatra/reloader)
gem 'sinatra-contrib'

Okay, at the moment, we’ve covered the first few steps with Sinatra. In the second part of the blog post, I’ll present how to build a full-stack Sinatra app that could be used instead of Rails. Stay tuned!

Photo by Max Nelson on Unsplash

Photo of Grzegorz Unijewski

More posts by this author

Grzegorz Unijewski

Grzegorz is a Computer Science graduate. In addition to coding, he's passionate about music, plays...
How to build products fast?  We've just answered the question in our Digital Acceleration Editorial  Sign up to get access

We're Netguru!

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency
Let's talk business!

Trusted by:

  • Vector-5
  • Babbel logo
  • Merc logo
  • Ikea logo
  • Volkswagen logo
  • UBS_Home