The Evil calls back
Published over 6 years ago

Happy new year!

Just an update on the two middlewares I contributed to rack-contrib. In case you don’t already know what is rack-contrib, it’s a project started by Ryan Tomayko as a playground for experimental Rack middlewares from the ruby community. There are 15 middlewares there already, so do check it out!


The name says it all. It’s pure evil, as it enables the rack application to return the response from any place during while it’s serving the request by doing throw :response, [status, header, body].


app = lambda do |env|
  template = <<-TEMPLATE
  Hello Template
  <%= throw :response, [404, {'Content-Type' => 'text/html'}, 'From Template'] %>
  result =

  [200, {'Content-Type' => 'text/plain'}, "Ran the template"]

And the response reaching the client will not be “Ran the template” with 200 status. But it’ll be “From Template” with 404 status!


Now that more people are getting on board with the concept of middlewares, people have started using middlewares for pure before/after filter kind of things. And I think don’t think that’s the best way moving forward. That’s the problem Rack::Callbacks tries to alleviate.

Rack::Callbacks lets you wrap your rack application with a series of before and after callbacks.

Example :

class TimeZone
  def initialize(default)
    @default = default

  def call(env)
    env['rack.timezone'] = find_timezone(env) || @default

class CompressBody
  def call(response)
    status, headers, body = response

    compressed_body = zip_body(body)
    [status, headers, compressed_body]

app = do
  before TimeZone, "default"


  after CompressBody

The flow of execution is very apparent here. All the before callbacks are run first, then the actual application and after callbacks at last.

before Callback, arg1, arg2….argx

before() takes Callback class as the first argument. Optional arguments are passed to Callback#initialize. This is very similar to the regular rack middlewares. However before callbacks are quite different from middlewares.

Callback#call accepts a single argument : env. However, the return value of Callback#call is simply discarded.

run Application

Runs the primary rack application.

after Callback, arg1, arg2….argx

after() takes Callback class as the first argument. Optional arguments are passed to Callback#initialize. Just like before() and middlewares. However, after callbacks are substantially different from before callbacks and middlewares:

Callback#call accepts a single argument : response, which is the rack response returned by the application or other after filters. Callback#call also must return a valid rack response. Return value of Callback#call is supplied to the next after filters in the stack, and the final after filter returns the response to the client.