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 endend
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 endend
Modify environment
class AwesomeMiddleware def initialize app @app = app end
def call(env) env["RAILS_ENV"] = 'awesome' @app.call env endend
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 ... endend
# confug.ru / config/environments/*.rbconfig.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/