Ruby Rack Middleware Tutorial
So you discovered Ruby's Rack 'web framework framework', and would like to know how to extend it, this is done using Rack middleware.
Basic Rack Application
The tiny example below shows that a Rack app is simply any object that responds to the call method, which returns an http status code, headers, and body. Middleware can be utilized via the use method which, in turn autoload's a Ruby file containing the middleware. Autoloading of middleware is simply a Rack convention, but helps to keep things clean
use Rack::ContentLength
app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, 'Hello World'] }
run appRunning The Application
Save the code above in a file named 'basic_rack.ru', the *.ru extension corresponds to Rack's rackup executable. These files are regular Ruby files.
Start the app using the following command, then visit http://0.0.0.0:3000/
$ rackup basic_rack.ru -p 3000Middleware Ordering
Your application is called, and then middleware is invoked in the order that you specify. These middlewares call each other, acting as a set of 'filters' for the response, so it is important to note that the ordering to which you 'use' them can be important, and have an effect on the results.
The following example illustrates that the body contents can be altered by several middleware's:
module Rack
class Upcase
def initialize app
@app = app
end
def call env
puts 'upcase'
p @app
puts
status, headers, body = @app.call env
[status, headers, [body.first.upcase]]
end
end
end
module Rack
class Reverse
def initialize app
@app = app
end
def call env
puts 'reverse'
p @app
puts
status, headers, body = @app.call env
[status, headers, [body.first.reverse]]
end
end
end
use Rack::Upcase
use Rack::Reverse
use Rack::ContentLength
app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, 'Hello World'] }
run appAs you can see in the terminal output, our Upcase object's application is actually the Reverse middleware, and Reverse's application is the next one in line which happens to be ContentLength.
upcase
#<Rack::Reverse:0x5574e0 @app=#<Rack::ContentLength:0x557620 @app=#<Proc:0x00561210@rack.ru:38>>>
reverse
#<Rack::ContentLength:0x557620 @app=#<Proc:0x00561210@rack.ru:38>>Realworld Example of JSON Middleware
Below is a very simple realworld example which translates the body results to json when the response Content-Type header is that of json (application/json). This sort of middleware can really clean up your app, help others, and well really this is where this sort of functionality belongs!
require 'json'
module Rack
class JSON
def initialize app
@app = app
end
def call env
@status, @headers, @body = @app.call env
@body = ::JSON.generate @body if json_response?
[@status, @headers, @body]
end
def json_response?
@headers['Content-Type'] == 'application/json'
end
end
end
use Rack::ContentLength
use Rack::JSON
app = lambda { |env| [200, { 'Content-Type' => 'application/json' }, { :some => 'json', :stuff => ['here'] } ] }
run appAdditional Information
This article has some good examples of URL mapping with Rack, as well as explicitly using the adapter of your choice instead of using rackup: http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder
Delicious
Digg
StumbleUpon
Reddit
Facebook
Comments
Thanks for the tutorial. As newbie to Rack, very hard to figure out without this sort of page.
There's a Rack coding contest starting now if you want to enter. It's at http://www.coderack.org
Some great prizes and it promises to be a lot of fun.
Nice explanation!