Rack
...or how to build your own Ruby on Rails
by Hubert Łępicki
@hubertlepicki
What is Rack?
Rack: a Ruby Webserver Interface
Rack provides a minimal interface between webservers supporting Ruby and Ruby frameworks.
Before Rack
remember "apache proxy to cluster of mongrels"?
Different solutions
- (F)CGI
- mod_rails
- mongrel handler
- apache proxy + cluster of mongrels
- meanwhile, Python already had WSGI
Supported servers
- Apache/Passenger
- Nginx/Passenger
- Puma
- Unicorn
- Webrick ;)
- Thin
- Rainbows
- name it!
The interface
describe 'rack interface' do
it 'should respond to call and accept hash'
it 'should return [status, headers, and chunks of body]'
end
Interface as lambda
lambda do |env|
body = "Hello, World!"
[ 200,
{ "Content-Type" => "text/plain",
"Content-Length" => body.length.to_s },
[body]
]
end
Interface as an object
class RackAwesomeMiddleware
def call(env)
body = "Rack is awesome!"
[ 200,
{"Content-Type" => "text/plain",
"Content-Length" => body.length.to_s},
[body]
]
end
end
Rackup file config.ru
use Rack::Reloader, 0
use Rack::ContentLength
app = proc do |env|
[ 200, {'Content-Type' => 'text/plain'}, ["a"] ]
end
run app
$ rackup config.ru # starts the app
Interacting with layers below (above?)
class AwesomeMiddleware
def initialize app
@app = app
end
def call(env)
@app.call env
end
end
Modify the response of other layer
class AwesomeMiddleware
def initialize app
@app = app
end
def call(env)
res = @app.call(env)
res[1]["Server"] = "Awesome Server"
res
end
end
Modify environment
class AwesomeMiddleware
def initialize app
@app = app
end
def call(env)
env["RAILS_ENV"] = 'awesome'
@app.call env
end
end
Rails on Rack
require ::File.expand_path('../config/environment', __FILE__)
run Rails.application
Rails on Rack
# config/environments/staging.rb
config.middleware.insert_before(::Rack::Runtime,
"::BasicAuthMiddleware", "Staging") do |u, p|
u == 'secret' && p == 'password'
end
Configuring Rack middleware
class ConfigurableMiddleware
def initialize(app, &block)
config = OpenStruct.new
yield config
...
end
end
# confug.ru / config/environments/*.rb
config.middleware.use 'ConfigurableMiddleware' do |config|
config.foo = 'bar'
end
Examples
Rack::Lint
Rack::Cache
Rack::Logger
Rack::Static
Rack::Auth::Basic
Rack::TimeZone
Rack::Locale
Rack::Profiler
Faye
websocket-rack
Rails middleware stack
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run MyApp::Application.routes
Links and stuff
"Rails on Rack" in Rails guides
http://guides.rubyonrails.org/rails_on_rack.html
"Build your own framework with Rack"
http://isotope11.com/blog/build-your-own-web-framework-with-rack-and-ruby-part-1
"Introduction to Rack middleware"
http://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/