You are on page 1of 173

Sinatra

(Cl|S)assy Web Development

Corey Donohoe(cdonohoe@engineyard.com)

Thursday, May 14, 2009


Corey Donohoe
cdonohoe@engineyard.com / http://atmos.org

Thursday, May 14, 2009


Open Source Participant

Thursday, May 14, 2009


2001

Thursday, May 14, 2009


Writing Rails since 2004

Thursday, May 14, 2009


Engine Yard in March 2007

Thursday, May 14, 2009


ran support for a while

Thursday, May 14, 2009


“integration” team

Thursday, May 14, 2009


It’s Take Your Breath Away Awesome

Thursday, May 14, 2009


http://github.com/sinatra

Thursday, May 14, 2009


Micro Framework

Thursday, May 14, 2009


powered by rack

Thursday, May 14, 2009


history

Thursday, May 14, 2009


advances

Thursday, May 14, 2009


fulfilling

Thursday, May 14, 2009


Classic Applications
get(‘/’) { “Hello World” }

Thursday, May 14, 2009


http://sinatrarb.com

Thursday, May 14, 2009


require 'rubygems'
require 'sinatra'

helpers do
def greeting
haml :greeting
end
end

template :greeting do
"%h2 Hello World"
end

get('/') do
greeting
end

Hello World

Thursday, May 14, 2009


throw that in a file

Thursday, May 14, 2009


run it

Thursday, May 14, 2009


Where’s my config file?

Thursday, May 14, 2009


where’s my database.yml

Thursday, May 14, 2009


There isn’t one(there isn’t even a db)

Thursday, May 14, 2009


Wonderful for One Offs

Thursday, May 14, 2009


HTTP Verbs

Thursday, May 14, 2009


["/foo", "/bar", "/baz"].each do |path|
get path do
"You've reached me at #{request.path_info}"
end
end

Thursday, May 14, 2009


Block Syntax

Thursday, May 14, 2009


get '/hello/:name' do |n|
"Hello #{n}!"
end

Thursday, May 14, 2009


params hash

Thursday, May 14, 2009


templates

Thursday, May 14, 2009


template :foo do
<<-HAML
%h2 Greetings
%p Lorem Ipsum
HAML
end

Thursday, May 14, 2009


set :views, ‘views’

Thursday, May 14, 2009


views/foo.haml

Thursday, May 14, 2009


helpers do; end

Thursday, May 14, 2009


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

def localhost(suffix)
port_part = case request.scheme
when "http"
request.port == 80 ? "" : ":#{request.port}"
when "https"
request.port == 443 ? "" : ":#{request.port}"
end
"#{request.scheme}://#{request.host}#{port_part}#{suffix}"
end
end

Thursday, May 14, 2009


Configuration Options

Thursday, May 14, 2009


enable :sessions

Thursday, May 14, 2009


set :public, ‘public’

Thursday, May 14, 2009


set :foo, 'bar'
set :baz, Proc.new { "Hello " + foo }

get '/baz' do
"baz is set to " + options.baz
end

Thursday, May 14, 2009


enable/disable

Thursday, May 14, 2009


Check the FAQ

Thursday, May 14, 2009


Modular Style
class MyApp < Sinatra::Default; end

Thursday, May 14, 2009


remove global scope

Thursday, May 14, 2009


instances

Thursday, May 14, 2009


reusable instances

Thursday, May 14, 2009


module Hancock
class ConfigurationError < StandardError; end

class App < Sinatra::Default


enable :sessions

set :sreg_params, [:email, :first_name, :last_name, :internal]


set :provider_name, 'Hancock SSO Provider!'
set :do_not_reply, nil

register Sinatra::Hancock::Defaults
register Sinatra::Hancock::Sessions
register Sinatra::Hancock::Users
register Sinatra::Hancock::OpenIDServer
end
end

Thursday, May 14, 2009


global scope moved inside my class

Thursday, May 14, 2009


as class methods

Thursday, May 14, 2009


pretty much works the same

Thursday, May 14, 2009


register class method
nice modular pieces of sinatra apps

Thursday, May 14, 2009


module Sinatra
module Hancock
module Sessions
def self.sessions_template(file)
template = File.expand_path(File.dirname(__FILE__)+'/views/sessions')
File.read("#{template}/#{file}.haml")
end

module Helpers
def session_user
session['user_id'].nil? ? nil : ::Hancock::User.get(session['user_id'])
end
end

def self.registered(app)
app.helpers(Sinatra::Hancock::Sessions::Helpers)
app.template(:unauthenticated) { sessions_template ('unauthenticated') }
app.get '/sso/login' do
ensure_authenticated
end
app.post '/sso/login' do
@user = ::Hancock::User.authenticate(params['email'], params['password'])
if @user
session['user_id'] = @user.id
end
ensure_authenticated
redirect session['return_to'] || '/'
end

app.get '/sso/logout' do
session.clear
redirect '/'
end
end
end
end
end

Thursday, May 14, 2009


Sinatra::Hancock::Sessions

Thursday, May 14, 2009


lib/sinatra/hancock/sessions.rb

Thursday, May 14, 2009


Thursday, May 14, 2009
auto register by requiring

Thursday, May 14, 2009


no one is really using this

Thursday, May 14, 2009


module Hancock
class ConfigurationError < StandardError; end

class App < Sinatra::Default


enable :sessions

set :sreg_params, [:email, :first_name, :last_name, :internal]


set :provider_name, 'Hancock SSO Provider!'
set :do_not_reply, nil

register Sinatra::Hancock::Defaults
register Sinatra::Hancock::Sessions
register Sinatra::Hancock::Users
register Sinatra::Hancock::OpenIDServer
end
end

OMG LOOK WHAT I MADE!


you can share it with your buddies

Thursday, May 14, 2009


be responsible

Thursday, May 14, 2009


under the hood
yo dawg, i heard you were into rack

Thursday, May 14, 2009


http://github.com/chneukirchen/rack

Thursday, May 14, 2009


bacon

Thursday, May 14, 2009


i don’t use it

Thursday, May 14, 2009


made up the tumblelog

Thursday, May 14, 2009


rackup

Thursday, May 14, 2009


specifies the app to run

Thursday, May 14, 2009


configurable stuff, ports, hostnames etc

Thursday, May 14, 2009


middleware to run also

Thursday, May 14, 2009


use

Thursday, May 14, 2009


specific middleware

Thursday, May 14, 2009


initialize method

Thursday, May 14, 2009


block syntax

Thursday, May 14, 2009


#!/usr/bin/env rackup

use EySso do |sso|


sso.only_staff!
end

# vim:filetype=ruby

Thursday, May 14, 2009


use in Sinatra

Thursday, May 14, 2009


initialize arity

Thursday, May 14, 2009


first parameter is the app

Thursday, May 14, 2009


it’s implied

Thursday, May 14, 2009


use Rack::HoptoadNotifier,‘10c4cc2f6aa6b9e1104a921c5fdae4b0846d4475’

Thursday, May 14, 2009


Rack::HoptoadNotifier.new(app,‘10c4cc2f6aa6b9e1104a921c5fdae4b0846d4475’)

Thursday, May 14, 2009


initialize w/ a block

Thursday, May 14, 2009


use in Rails

Thursday, May 14, 2009


config.middleware.use

Thursday, May 14, 2009


it’s a string

Thursday, May 14, 2009


script/generate metal

Thursday, May 14, 2009


works with Sinatra::Default

Thursday, May 14, 2009


just inherit from them

Thursday, May 14, 2009


use in merb

Thursday, May 14, 2009


no rack.session

Thursday, May 14, 2009


config/rack.rb

Thursday, May 14, 2009


who still writes merb apps?

Thursday, May 14, 2009


use in ramaze

Thursday, May 14, 2009


no rack.session

Thursday, May 14, 2009


not becoming rails

Thursday, May 14, 2009


map

Thursday, May 14, 2009


russian doll

Thursday, May 14, 2009


#!/usr/bin/env rackup

require File.dirname(__FILE__) + '/lib/setup'


require 'gateway/app'
require 'migration/app'

use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"

map "/gateway/" do
use EySso
run Gateway::App
end

map "/migration/" do
use Rack::ShowExceptions
if ENV["RACK_ENV"] == "production"
use EySso do |sso|
sso.only_staff!
end
end
run Migration::App
end

map "/" do
app = lambda do |env|
[404, {"Content-Type" => "text/plain", "Content-Length" => "9"}, ["Not found"]]
end
run app
end

Thursday, May 14, 2009


with a url helper hack

Thursday, May 14, 2009


helpers do
def url(path)
request.script_name + path
end
end

Thursday, May 14, 2009


#!/usr/bin/env rackup

require File.dirname(__FILE__) + '/lib/setup'


require 'gateway/app'
require 'migration/app'

use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"

map "/gateway/" do
use EySso
run Gateway::App
end

map "/migration/" do
use Rack::ShowExceptions
if ENV["RACK_ENV"] == "production"
use EySso do |sso|
sso.only_staff!
end
end
run Migration::App
end

map "/" do
app = lambda do |env|
[404, {"Content-Type" => "text/plain", "Content-Length" => "9"}, ["Not found"]]
end
run app
end

Thursday, May 14, 2009


every day kinda stuff

Thursday, May 14, 2009


so back to sinatra

Thursday, May 14, 2009


Sinatra::Base

Thursday, May 14, 2009


this is middleware

Thursday, May 14, 2009


you should really read the docs

Thursday, May 14, 2009


straight rack, no sinatra

Thursday, May 14, 2009


major frameworks integrate ok

Thursday, May 14, 2009


Sinatra::Default

Thursday, May 14, 2009


this is a sinatra application

Thursday, May 14, 2009


these are FUN

Thursday, May 14, 2009


your rackup files invoke these

Thursday, May 14, 2009


so you got all that, right?

Thursday, May 14, 2009


Deployment

Thursday, May 14, 2009


easy

Thursday, May 14, 2009


Passenger

Thursday, May 14, 2009


nginx

Thursday, May 14, 2009


nginx config
http {
...
server {
listen 80;
server_name hancock.atmos.org
root /data/hancock;
passenger_enabled on; # <--- These lines
passenger_base_uri /hancock; # <--- added.
}
...
}

Thursday, May 14, 2009


apache

Thursday, May 14, 2009


Apache 2 Vhost Config
<VirtualHost *:80>
ServerName hancock.atmos.org
DocumentRoot /data/hancock/current/public
RackEnv 'production'
<Directory /data/hancock/current/public>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

Thursday, May 14, 2009


script/

Thursday, May 14, 2009


script/console
#!/usr/bin/env ruby
require "irb"
require 'rubygems'
require 'twitrsvp'

DataMapper.setup(:default, "mysql://atmos:s3cr3t@localhost/twitrsvp_production")

ENV['TWIT_RSVP_READKEY'] = '4the'
ENV['TWIT_RSVP_READSECRET'] = 'lulz'

include ::TwitRSVP

if __FILE__ == $0
IRB.start(__FILE__)
else
# check -e option
if /^-e$/ =~ $0
IRB.start(__FILE__)
else
IRB.setup(__FILE__)
end
end

Thursday, May 14, 2009


crons, whatever

Thursday, May 14, 2009


gnu screen

Thursday, May 14, 2009


Testing
Fleshing things out

Thursday, May 14, 2009


i’m a huge fan

Thursday, May 14, 2009


TATFT

Thursday, May 14, 2009


acceptance testing

Thursday, May 14, 2009


TDD as a design tool

Thursday, May 14, 2009


Sinatra::Test

Thursday, May 14, 2009


is Deprecated

Thursday, May 14, 2009


Sticky Sessions Inside Your Tests

Thursday, May 14, 2009


rack-test
http://github.com/brynary/rack-test/tree/master

Thursday, May 14, 2009


require "rack/test"

class HomepageTest < Test::Unit::TestCase


include Rack::Test::Methods

def app
MyApp.new
end

def test_redirect_logged_in_users_to_dashboard
authorize "bryan", "secret"
get "/"
follow_redirect!

assert_equal "http://example.org/redirected", last_request.url


assert last_response.ok?
end
end

Thursday, May 14, 2009


Webrat Matchers

Thursday, May 14, 2009


What You Should Be Doing

Thursday, May 14, 2009


Webrat
http://github.com/brynary/webrat

Thursday, May 14, 2009


have_selector

Thursday, May 14, 2009


have_selector("#zendesk_organization_2 .name:contains('Zondervan’)”)

Thursday, May 14, 2009


Cucumber
http://github.com/aslakhellesoy/cucumber/tree/master

Thursday, May 14, 2009


Combination of the previous two

Thursday, May 14, 2009


rack-test verbs instead of visit

Thursday, May 14, 2009


webrat selectors for validations

Thursday, May 14, 2009


drive browsers

Thursday, May 14, 2009


safariwatir and selenium-rc

Thursday, May 14, 2009


acceptance testing is wonderful

Thursday, May 14, 2009


reflection

Thursday, May 14, 2009


simple

Thursday, May 14, 2009


reusable

Thursday, May 14, 2009


nice performance

Thursday, May 14, 2009


not fighting my framework

Thursday, May 14, 2009


leverage all the ruby libs

Thursday, May 14, 2009


develop like a pro

Thursday, May 14, 2009


not overwhelmed with choices

Thursday, May 14, 2009


future is known

Thursday, May 14, 2009


merb

Thursday, May 14, 2009


rails 3

Thursday, May 14, 2009


think UNIX

Thursday, May 14, 2009


FUN

Thursday, May 14, 2009


creative juices flowing

Thursday, May 14, 2009


hack out something fun

Thursday, May 14, 2009


solve something annoying

Thursday, May 14, 2009


That’s it! :D
✤ http://github.com/sinatra/sinatra

✤ http://github.com/chneukirchen/rack

✤ http://github.com/atmos/hancock

✤ http://github.com/atmos/hancock-client

✤ http://github.com/atmos/hancock-client-rails

✤ http://github.com/brynary/rack-bug

✤ http://github.com/foca/integrity

Thursday, May 14, 2009


Hancock

Thursday, May 14, 2009


Like Your John Hancock

Thursday, May 14, 2009


Single Sign On

Thursday, May 14, 2009


gem 'hancock', '~>0.0.1'
require 'hancock'

DataMapper.setup(:default, "sqlite3:///#{Dir.pwd}/development.db")

Hancock::App.set :views, 'views'


Hancock::App.set :public, 'public'
Hancock::App.set :environment, :production

Hancock::App.set :provider_name, 'Example SSO Provider'


Hancock::App.set :do_not_reply, 'sso@example.com'

Sinatra::Mailer.config = {
:host => 'smtp.example.com',
:port => '25',
:user => 'sso',
:pass => 'lolerskates',
:auth => :plain # :plain, :login, :cram_md5, the default is no auth
:domain => "example.com" # the HELO domain provided by the client to the server
}

run Hancock::App

config.ru

Thursday, May 14, 2009


Example

Thursday, May 14, 2009


Hancock-Client

Thursday, May 14, 2009


Sinatra App

Thursday, May 14, 2009


Rack App

Thursday, May 14, 2009


Hancock-Client-Rails

Thursday, May 14, 2009


metal

Thursday, May 14, 2009


\m/

Thursday, May 14, 2009


Rails 2.3.x

Thursday, May 14, 2009


config/environment.rb
require File.join(File.dirname(__FILE__), 'boot')
require 'hancock'

Rails::Initializer.run do |config|
config.gem 'hancock', :lib => 'hancock'

config.middleware.use 'Hancock::Client'
# all your other normal stuff
end

Thursday, May 14, 2009

You might also like