RackMiddleware
\m/
NEED
CHOICE
Jon Crosby
http://joncrosby.me
CloudKithttp://getcloudkit.com
rack-contrib
http://github.com/rack/rack-contrib
Engine Yard Solo
“The platform foron-demand management of your
Ruby on Rails applicationin the cloud.”
Free during HackFest
Discount for Sign Up
CGI
app.cgi
WARNING
Contains Perl
old skool perl cgi
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
if ($cgi->param(‘action’) eq ‘all’) { my $sql = “select * from customer”; my $rows = $dbh->selectall_arrayref($sql); if (@$rows) { print “<table border=1>” . “<th>name</th>” ....
Monolith
:-(
Rails
MerbSinatraMack
RamazeWaves
Authentication
Single Sign-On
Caching
Authentication Example:OpenID + OAuth
Install Auth Plugin(s)
Install Auth Plugin(s)Generate Controllers
Install Auth Plugin(s)Generate ControllersGenerate Models
Install Auth Plugin(s)Generate ControllersGenerate ModelsGenerate Migrations
Install Auth Plugin(s)Generate ControllersGenerate ModelsGenerate MigrationsModify Existing Controllers
Install Auth Plugin(s)Generate ControllersGenerate ModelsGenerate MigrationsModify Existing ControllersMonkey Patch Rails
:-(
The Web
HTTP
HTTP
Intermediaries
HTTP
Intermediaries App
HTTP
Intermediaries App
Rack
HTTP
HTTP
Intermediaries
HTTP
Middleware
HTTP
Middleware App
Rack is the Web
The Web is Rack
WSGI
SPEC
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
run lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
config.ru
$ rackup config.ru
$ curl http://localhost:9292
Hello
class App def call(env) [200, {...}, [...]] endend
SPEC
$ rake SPEC
Rack::Lint
lambda { |env| [ 200, { ‘Content-Type’ => ‘text/plain’, ‘Content-Length’ => ‘5’ }, [‘Hello’] ]}
env
REQUEST_METHOD
env[‘REQUEST_METHOD’]
GETPUTPOSTDELETEHEADOPTIONSTRACE
PATH_INFO
/items/123
HTTP_*
HTTP_ACCEPT
application/json
rack.*
rack.input
(the input stream)
#gets#each#read#rewind
yournamespace.*
request = Rack::Request.new(env)
request.post?
request.params[‘id’]
request[‘HTTP_IF_MATCH’]
\m/
HTTP
Middleware App
use MiddlewareAuse MiddlewareBuse MiddlewareCrun app
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
rack-contrib
http://github.com/rack/rack-contrib
Rack::Profiler
Rack::MailExceptions
Rack::JSONP
Rack::CSSHTTPRequest
Rack::Cache
http://github.com/rtomayko/rack-cache
Rack::NotFound
404
HTTP
Middleware App
use MiddlewareAuse MiddlewareBuse MiddlewareCrun app
class App def call(env) [200, {...}, [...]] endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
class GoSlower def initialize(app) @app = app end
def call(env) sleep(1) @app.call(env) endend
use MiddlewareAuse MiddlewareBuse MiddlewareCrun app
HTTP
Middleware App
HTTP
Middleware App
Cooperative Middleware
URI Space
/*
/just-what-it-needs
CloudKit
Open WebJSON
Appliance
expose :notes, :todos
expose :notes, :todos
contain :notes, :todos
use Rack::Pool::Sessionuse CloudKit::OAuthFilteruse CloudKit::OpenIDFilteruse CloudKit::Service, :collections => [:notes, :todos](run DefaultApp)
CloudKit::OAuthFilter
/oauth/*
CloudKit::OpenIDFilter
/login/logout/openid_complete
CloudKit::Service
/notes/*/todos/*
?
OAuth OpenID Service
Browser
OAuth OpenID Service
Browser
OAuth OpenID Service
Browser
{...}
OAuth OpenID Service
Browser
OAuth OpenID Service
Browser
Login
{...}
OAuth OpenID Service
Browser
OAuth OpenID Service
Browser
OAuth OpenID Service
Browser
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
{...}
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
Login
{...}
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
OAuth OpenID Service
Service or Desktop App
AnnouncingMiddlewarePresence
HTTP
Via
Via: 1.0 ricky, 1.1 ethel, 1.1 fred
Via: 1.0 ricky, 1.1 ethel, 1.1 fred
Via: 1.0 ricky, 1.1 ethel, 1.1 fred
Via: 1.0 ricky, 1.1 ethel, 1.1 fred
env[‘cloudkit.auth’] = 1
env[‘cloudkit.via’] << ‘cloudkit.filter.oauth’
env[‘cloudkit.via’] << ‘cloudkit.filter.openid’
env[‘cloudkit.user’] = ‘http://joncrosby.me’
Alternative Stacks
Rack::Map
map “/” do run Blog::Publicend
map “/db” do run Blog::DBAdminend
Rack::Map + Sinatra
require ‘sinatra/base’
module Blog class Public < Sinatra::Base get ‘/’ do erb :index end endend
require ‘sinatra/base’
module Blog class Public < Sinatra::Base get ‘/’ do erb :index end endend
require ‘sinatra/base’
module Blog class Public < Sinatra::Base get ‘/’ do erb :index end endend
require ‘sinatra’
for “apps”
/* URI space
require ‘sinatra/base’
MyClass < Sinatra::Base
Minimal Sinatra (routing, rendering, etc.)
\m/
use MySinatraApprun SomeOtherApp
Rack::Cascade
app1 = lambda { ... }app2 = lambda { ... }run Rack::Cascade.new([app1, app2])
Sinatra as Middlewarein Rails
class X < Sinatra::Base get ‘/what’ do ‘what’ endend
Rails::Initializer.run do |config| config.use.middleware ‘X’end
CloudKit in Rails
Rails::Initializer.run do |config| config.use.middleware ‘CloudKit::Service’, :collections => [:notes, :todos]end
HTTP
Middleware App
HTTP
Middleware App
Rails
HTTP
Middleware App
Rails Merb
HTTP
Middleware App
Rails Merb *
New Unit of Composition
\m/