Sinatra App Template

A quick and dirty template for Sinatra apps with many of the features you can find in Rails.

Supports:

  • Slim views
  • Sass stylesheets
  • Coffee script
  • Compass
  • Susy (susy.oddbird.net)
  • Asset pipeline (Sprockets)
  • View helpers
  • A console
  • I18n
  • Logging
  • ActiveRecord+migrations
  • Debugging with Pry
  • Flash messages
  • Class reloading in development mode
  • Security features such as CSRF, Cross Site Scripting, etc. All provided by Rack::Protection and Sinatra.

Create app.rb:

require 'slim'
require 'i18n'
require 'mysql2'
require 'sprockets'
require 'sprockets-sass'
require 'compass'
require 'susy'
require 'sinatra/base'
require 'sinatra/cookies'
require 'sinatra/content_for'
require 'sinatra/activerecord'
require 'sinatra/partial'
require 'rack-flash'

class App < Sinatra::Base
  enable :sessions, :logging
  register Sinatra::Partial

  use Rack::MethodOverride
  use Rack::Flash, :accessorize => [:info, :error, :success], :sweep => true
  use Rack::Protection::AuthenticityToken # HTML forms now require: input name="authenticity_token" value=session[:csrf] type="hidden"
  set :public_folder, File.dirname(__FILE__) + '/public'
  set :views, File.dirname(__FILE__) + '/app/views'
  set :slim, :layout_engine => :slim, :layout => :'layouts/default', :use_html_safe => true, :pretty => App.environment == :development
  set :partial_template_engine, :slim
  set :session_secret, "25729f31a6bc7c57f8575db9b79ee468...." # SecureRandom.hex(128)
  set :cookie_options, { path: '/'}

  def self.sprockets
    project_root = File.expand_path(File.dirname(__FILE__))
    assets = Sprockets::Environment.new(project_root)
    assets.append_path('app/js')
    assets.append_path('app/css')
    # Twitter Bootstrap...
    assets.append_path('lib/bootstrap/js')
    assets.append_path('lib/bootstrap/css')
    assets
  end

  helpers Sinatra::Cookies
  helpers Sinatra::ContentFor
  helpers do
    def t(*args)
      ::I18n::t(*args)
    end
    def authenticity_token
      session[:csrf] = SecureRandom.hex(128) unless session.has_key?(:csrf)
      %Q{<input type="hidden" name="authenticity_token" value="#{session[:csrf]}"/>}
    end
  end

  configure do
    # Configure logging, WTF
    set :logging, false
    class ::Logger; alias_method :write, :<<; end
    logfile = File.join(App.root, 'log', "#{App.environment}.log")
    # Send STDs to log file
    $stdout.reopen(logfile)
    $stderr.reopen(logfile)
    $stderr.sync = true
    $stdout.sync = true
    # Weekly roll
    log  = Logger.new(logfile, 'weekly')
    log.level = Logger::DEBUG
    # use Rack::CommonLogger, log
    set :log, log

    Compass.add_project_configuration(File.join(Sinatra::Application.root, 'config', 'compass.rb'))
  end

  configure :development do
    require "sinatra/reloader"
    register Sinatra::Reloader
    also_reload 'app/**/*.rb'
    also_reload 'lib/**/*.rb'
    also_reload 'conf/**/*.rb'
    set :raise_errors, true
  end

  [:error, :info, :success].each do |key|
    class_eval "
    def flash_#{key}(key, now=true)
      message(key, :#{key}, now)
    end
    "
  end

  def message(key, type=:notice, now=true)
    hash = now ? flash.now : flash
    hash[type] = I18n.t(key)
  end

  # Set view variables, e.g.:
  # - meta :title, "Page title"
  # Retrieve view variables, e.g.:
  # = meta :title
  def meta(key, value = nil)
    value ? content_for(key) { value } : yield_content(key)
  end

  # Helper method for creating HTML tags:
  # content_tag :a, 'Contact us', href: 'mailto:xxx@xxx.com'
  def content_tag(name, content, attributes = nil)
    name = html_escape(name) unless name.html_safe?
    content = html_escape(content) unless content.html_safe?
    attributes = attributes.map do |name, value|
      value = html_escape(value) unless value.html_safe?
      %Q{#{name}="#{value}"}
    end if attributes && attributes.any?
    start = [name, attributes.join(" ")].reject(&:nil?).join(' ')
    "<#{start}>#{content}</#{name}>"
  end

  def debug_something_with_pry
    Kernel.binding.pry
  end

  error do
    slim :'errors/500'
  end

  not_found do
    slim :'errors/404'
  end

  error ActiveRecord::RecordNotFound do
    slim :'errors/404'
  end

  before do
    I18n.locale = params[:locale] || I18n.default_locale
  end
end

# Require attr_accessible...
ActiveRecord::Base.send(:attr_accessible, nil)

# Move to config/init/db.rb if you like
OpenStruct.new(YAML::load(File.open('config/database.yml'))[App.environment.to_s].symbolize_keys).tap do |config|
  ActiveRecord::Base.establish_connection(
    host: config.host,
    adapter: config.adapter,
    database: config.database,
    username: config.username,
    password: config.password
  )
end

%w(models controllers concerns).each do |name|
  Dir[File.join('app', name, '**/*.rb')].each do |file|
    require_relative file
  end
end

# Move to config/init/i18n.rb if you like
Dir[File.join(App.root, 'config', 'locales', '*.yml')].each do |file|
  I18n.backend.load_translations(file)
end
I18n.default_locale = :en

# Move to app/controllers/root_controller.rb
class RootController < App
  get '/' do
    flash_alert "Thanks for nothing"
    slim :'index'
  end
end

Create Gemfile:

source :rubygems
gem 'thin'
gem 'slim'
gem 'sass'
gem 'sinatra'
gem 'sprockets'
gem 'sprockets-sass'
gem 'compass'
gem 'susy'

gem 'sinatra-partial'
gem 'coffee-script'
gem 'therubyracer'
group :development do
  gem 'sinatra-reloader'
  gem 'sinatra-activerecord'
  gem 'pry'
end
gem 'rack-flash3'
gem 'awesome_print'
gem 'mysql2'

Create Rakefile:

require 'bundler/setup'
require 'sinatra/activerecord/rake'
require 'pry'
require './app'

Dir[File.join('lib', 'tasks', '**', '*.rake')].each do |file|
  import file
end

task :console do
  binding.pry
end

# Asset pipeline (Sprockets)
namespace :assets do
  task :precompile do
    App.sprockets['application.js'].write_to('public/assets/application.js')
    App.sprockets['application.css'].write_to('public/assets/application.css')
  end
end

Create config/database.yml:

development:
  adapter: mysql2
  database: xxx_development
  username: root
  password: 
  host: localhost

Create config.ru:

require './app'
map "/" do
  run RootController
end

map "/assets" do
 run App.sprockets
end

Run:

mkdir -p app/controllers app/models app/concerns app/views/layouts app/css app/js lib tmp log public/img config/init db/migrate lib/tasks
bundle
bundle exec thin start -p 3000

Updated 868 days ago