What Do You Use Sinatra For

What do you use Sinatra for?

Sinatra is not Rails. It is a micro-framework used for simple websites where you may need to just define a few actions. You can make a Sinatra application as complex as you want to, but you'll hit a point where you code has become a crazy mess sooner than with Rails.

While not 100% accurate, Sinatra fits mostly into the Page Controller architectural pattern, and Rails is a clear MVC implementation.

To answer your questions specifically:

  • It is not intended to replace Rails
  • It can run side by side
  • You could create a twitter clone in Sinatra

Benefits of Sinatra for a web service like controller

The simplest useful Ruby web application you could build would be a Rack application. Sinatra is a lightweight DSL which sits on top of Rack to make coding the controllers and views more convenient. You can build more sophisticated applications by including more add-ons like ActiveRecord or Rack::Oauth, etc

Rails 2 is a more feature rich framework which includes a plethora of additional features already in the framework. Some applications don't need all that so some developers prefer things like Sinatra which is minimal.

However the distinction between Rails and Sinatra has blurred quite a bit since Rails 3. The new version allows everything from just Rack to full Rails in the stack to you can tailor it to suit your needs. The raison d'etre for some of the intermediate frameworks such as Sinatra is weaker than it used to be.

So take a look at Rails 3, start minimally and grow to suit your needs.

Using Sinatra for larger projects via multiple files

Here is a basic template for Sinatra apps that I use. (My larger apps have 200+ files broken out like this, not counting vendor'd gems, covering 75-100 explicit routes. Some of these routes are Regexp routes covering an additional 50+ route patterns.) When using Thin, you run an app like this using:

thin -R config.ru start

Edit: I'm now maintaining my own Monk skeleton based on the below called Riblits. To use it to copy my template as the basis for your own projects:

# Before creating your project
monk add riblits git://github.com/Phrogz/riblits.git

# Inside your empty project directory
monk init -s riblits

File Layout:


config.ru
app.rb
helpers/
init.rb
partials.rb
models/
init.rb
user.rb
routes/
init.rb
login.rb
main.rb
views/
layout.haml
login.haml
main.haml

 

config.ru

root = ::File.dirname(__FILE__)
require ::File.join( root, 'app' )
run MyApp.new

 

app.rb

# encoding: utf-8
require 'sinatra'
require 'haml'

class MyApp < Sinatra::Application
enable :sessions

configure :production do
set :haml, { :ugly=>true }
set :clean_trace, true
end

configure :development do
# ...
end

helpers do
include Rack::Utils
alias_method :h, :escape_html
end
end

require_relative 'models/init'
require_relative 'helpers/init'
require_relative 'routes/init'

 

helpers/init.rb

# encoding: utf-8
require_relative 'partials'
MyApp.helpers PartialPartials

require_relative 'nicebytes'
MyApp.helpers NiceBytes

 

helpers/partials.rb

# encoding: utf-8
module PartialPartials
def spoof_request(uri,env_modifications={})
call(env.merge("PATH_INFO" => uri).merge(env_modifications)).last.join
end

def partial( page, variables={} )
haml page, {layout:false}, variables
end
end

 

helpers/nicebytes.rb

# encoding: utf-8
module NiceBytes
K = 2.0**10
M = 2.0**20
G = 2.0**30
T = 2.0**40
def nice_bytes( bytes, max_digits=3 )
value, suffix, precision = case bytes
when 0...K
[ bytes, 'B', 0 ]
else
value, suffix = case bytes
when K...M then [ bytes / K, 'kiB' ]
when M...G then [ bytes / M, 'MiB' ]
when G...T then [ bytes / G, 'GiB' ]
else [ bytes / T, 'TiB' ]
end
used_digits = case value
when 0...10 then 1
when 10...100 then 2
when 100...1000 then 3
else 4
end
leftover_digits = max_digits - used_digits
[ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
end
"%.#{precision}f#{suffix}" % value
end
module_function :nice_bytes # Allow NiceBytes.nice_bytes outside of Sinatra
end

 

models/init.rb

# encoding: utf-8
require 'sequel'
DB = Sequel.postgres 'dbname', user:'bduser', password:'dbpass', host:'localhost'
DB << "SET CLIENT_ENCODING TO 'UTF8';"

require_relative 'users'

 

models/user.rb

# encoding: utf-8
class User < Sequel::Model
# ...
end

 

routes/init.rb

# encoding: utf-8
require_relative 'login'
require_relative 'main'

 

routes/login.rb

# encoding: utf-8
class MyApp < Sinatra::Application
get "/login" do
@title = "Login"
haml :login
end

post "/login" do
# Define your own check_login
if user = check_login
session[ :user ] = user.pk
redirect '/'
else
redirect '/login'
end
end

get "/logout" do
session[:user] = session[:pass] = nil
redirect '/'
end
end

 

routes/main.rb

# encoding: utf-8
class MyApp < Sinatra::Application
get "/" do
@title = "Welcome to MyApp"
haml :main
end
end

 

views/layout.haml

!!! XML
!!! 1.1
%html(xmlns="http://www.w3.org/1999/xhtml")
%head
%title= @title
%link(rel="icon" type="image/png" href="/favicon.png")
%meta(http-equiv="X-UA-Compatible" content="IE=8")
%meta(http-equiv="Content-Script-Type" content="text/javascript" )
%meta(http-equiv="Content-Style-Type" content="text/css" )
%meta(http-equiv="Content-Type" content="text/html; charset=utf-8" )
%meta(http-equiv="expires" content="0" )
%meta(name="author" content="MeWho")
%body{id:@action}
%h1= @title
#content= yield

Use Sinatra to serve php files

You could use rack-legacy, which allows Sinatra to serve PHP files. It simply uses php-cgi to run the scripts. For example, put phpMyAdmin under directory admin and put something along these lines to config.ru:

require 'app'

map "/admin" do
use Rack::Legacy::Php, 'admin'
use Rack::Static, :urls => ['/'], :root => 'admin'
run lambda{|env| [200, {'Content-type' => 'text/plain'}, 'OK']}
end

map "/" do
run Sinatra::Application
end

(If you're not familiar with using config.ru with your Sinatra app, see this part of Sinatra docs).

I'd suggest to configure Apache instead if possible. It strikes me as a cleaner solution and it would be also more efficient, but that's probably not a problem if you're only using it for phpMyAdmin.

Sinatra, where to place the require statments

I recommend:

  • Require your main app file only from your config.ru.
  • Require Sinatra and views gems in your main app
  • Create individual init.rb files for each of your helpers, models, and routes, and require those in your main app.
  • Require DB-related gems in models/init.rb

Here's an example of the layout I use:

Using Sinatra for larger projects via multiple files

Note that by loading DB-related gems and setting up your DB in your models/init.rb you can (from IRB) load just that file and have your full model stack available for poking at.

How do I use Sinatra and Sequel to get an array from a checkbox HTML form?

You don't actually need Sequel to receive an item from a post request. Sequel is a database, so you would save the returned item to the database after receiving it from your post. So, you don't actually need it in order to return an array in this case.

Also, forms and checkboxes are not specific to Sinatra but are specific to HTML.

When you submit the form as a post request, it is sent back to the server and Sinatra where it can be handled. Sinatra provides a params method which contains a hash of the values of the form that was submitted.

In the example below, I use a special syntax (item[]) to provide the values named item as an array within the params hash. For example, this is what the params hash returns {"item"=>["Bacon", "Eggs (dozen)"]} with both checkboxes checked.

require 'sinatra'

get '/results' do
erb :results
end
post '/results' do
params[:item].inspect
end

__END__

@@ results
<form action="/results" method="post" name="checkbox_form">
<input type="checkbox" name="item[]" value="Bacon">Bacon (1 lb., sliced)<br/>
<input type="checkbox" name="item[]" value="Eggs (dozen)">Eggs (1 dozen)<br/>
<input type="submit" value="Calculate"/>
</form>

The get block receives a get request from a user's browser and handles it. In this example, it returns an HTML form to the user.

The post block receives a post request from a user's browser after they have submitted the form. The user is shown the response from the server which in this case is an array of the checkbox values.

Architecture for a modular, component-based Sinatra Application

This is similar to include's proposal, but it doesn't require access to the rackup file.

Write your various Handlers like:

class FoodHandler < Sinatra::Base
get '/chunky/:food' do
"Chunky #{params[:food]}!"
end
end

Then in your main application file:

require './lib/handlers/food_handler.rb'

class Main < Sinatra::Base
enable :sessions
... bla bla bla
use FoodHandler
end

I've used this kind of structure to build some fairly complex Sinatra apps. It scales just as well as Rails.

EDIT

To have your config file define the routes, you could do something like this:

class PlacesHandler < Sinatra::Base
# Given your example, this would define 'places/paris' and 'places/losangeles'
CONFIG['components'].select { |c| c['compontent_type'] == 'Mapper' }.each do |c|
get c['route'] do
@latitude = c['component_settings']['latitude']
@longitude = c['component_settings']['longitude']
end
end
end

How can I use a side sinatra app when deploying?

Here is an article from Thoughtbot, which shows two approaches on how to use Sinatra application inside your Rails app.

The simpler method is to use Rails router:

require 'my_sinatra_app'

MyRailsApp::Application.routes.draw do
mount MySinatraApp.new => '/sinatra'
end

It's a preferable approach unless you need a custom middleware for each app.



Related Topics



Leave a reply



Submit