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 projectmonk add riblits git://github.com/Phrogz/riblits.git# Inside your empty project directorymonk init -s riblits
File Layout:
config.ruapp.rbhelpers/ init.rb partials.rbmodels/ init.rb user.rbroutes/ init.rb login.rb main.rbviews/ layout.haml login.haml main.haml
config.ru
root = ::File.dirname(__FILE__)require ::File.join( root, 'app' )run MyApp.new
app.rb
# encoding: utf-8require '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 endendrequire_relative 'models/init'require_relative 'helpers/init'require_relative 'routes/init'
helpers/init.rb
# encoding: utf-8require_relative 'partials'MyApp.helpers PartialPartialsrequire_relative 'nicebytes'MyApp.helpers NiceBytes
helpers/partials.rb
# encoding: utf-8module 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 endend
helpers/nicebytes.rb
# encoding: utf-8module 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 Sinatraend
models/init.rb
# encoding: utf-8require '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-8class User < Sequel::Model # ...end
routes/init.rb
# encoding: utf-8require_relative 'login'require_relative 'main'
routes/login.rb
# encoding: utf-8class 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 '/' endend
routes/main.rb
# encoding: utf-8class MyApp < Sinatra::Application get "/" do @title = "Welcome to MyApp" haml :main endend
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
Absolutely. To see an example of this I recommend downloading the Monk gem, described here:
https://github.com/monkrb/monk
You can 'gem install' it via rubygems.org. Once you have the gem, generate a sample app using the instructions linked above.
Note that you don't have to use Monk for your actual development unless you want to (in fact I think it may not be current). The point is to see how you can easily structure your app in the MVC style (with separate controller-like route files) if you want to.
It's pretty simple if you look at how Monk handles it, mostly a matter of requiring files in separate directories, something like (you'll have to define root_path):
Dir[root_path("app/**/*.rb")].each do |file| require fileend
Do a Google search for "Sinatra boilerplate" to get some ideas for how others are laying out their Sinatra applications. From that you can probably find one that suits your needs or simply make your own. It's not too hard to do. As you develop more Sinatra apps, you can add to your boilerplate.
Here's what I made and use for all of my projects: