class Rack::Builder

Rack::Builder implements a small DSL to iteratively construct Rack applications.

Example:

require 'rack/lobster'
app = Rack::Builder.new do
  use Rack::CommonLogger
  use Rack::ShowExceptions
  map "/lobster" do
    use Rack::Lint
    run Rack::Lobster.new
  end
end

run app

Or

app = Rack::Builder.app do
  use Rack::CommonLogger
  run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end

run app

use adds middleware to the stack, run dispatches to an application. You can use map to construct a Rack::URLMap in a convenient way.

Constants

UTF_8_BOM

stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom

Public Class Methods

app(default_app = nil, &block) click to toggle source

Create a new Rack::Builder instance and return the Rack application generated from it.

# File lib/rack/builder.rb, line 129
def self.app(default_app = nil, &block)
  self.new(default_app, &block).to_app
end
load_file(path, opts = Server::Options.new) click to toggle source

Load the given file as a rackup file, treating the contents as if specified inside a Rack::Builder block.

Treats the first comment at the beginning of a line that starts with a backslash as options similar to options passed on a rackup command line.

Ignores content in the file after +__END__+, so that use of +__END__+ will not result in a syntax error.

Example config.ru file:

$ cat config.ru

#\ -p 9393

use Rack::ContentLength
require './app.rb'
run App
# File lib/rack/builder.rb, line 92
def self.load_file(path, opts = Server::Options.new)
  options = {}

  cfgfile = ::File.read(path)
  cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8

  if cfgfile[/^#\(.*)/] && opts
    warn "Parsing options from the first comment line is deprecated!"
    options = opts.parse! $1.split(/\s+/)
  end

  cfgfile.sub!(/^__END__\n.*\Z/m, '')
  app = new_from_string cfgfile, path

  return app, options
end
new(default_app = nil, &block) click to toggle source

Initialize a new Rack::Builder instance. default_app specifies the default application if run is not called later. If a block is given, it is evaluted in the context of the instance.

# File lib/rack/builder.rb, line 122
def initialize(default_app = nil, &block)
  @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
  instance_eval(&block) if block_given?
end
new_from_string(builder_script, file = "(rackup)") click to toggle source

Evaluate the given builder_script string in the context of a Rack::Builder block, returning a Rack application.

# File lib/rack/builder.rb, line 111
def self.new_from_string(builder_script, file = "(rackup)")
  # We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
  # We cannot use instance_eval(String) as that would resolve constants differently.
  binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
  eval builder_script, binding, file
  builder.to_app
end
parse_file(config, opts = Server::Options.new) click to toggle source

Parse the given config file to get a Rack application.

If the config file ends in .ru, it is treated as a rackup file and the contents will be treated as if specified inside a Rack::Builder block, using the given options.

If the config file does not end in .ru, it is required and Rack will use the basename of the file to guess which constant will be the Rack application to run. The options given will be ignored in this case.

Examples:

Rack::Builder.parse_file('config.ru')
# Rack application built using Rack::Builder.new

Rack::Builder.parse_file('app.rb')
# requires app.rb, which can be anywhere in Ruby's
# load path. After requiring, assumes App constant
# contains Rack application

Rack::Builder.parse_file('./my_app.rb')
# requires ./my_app.rb, which should be in the
# process's current directory.  After requiring,
# assumes MyApp constant contains Rack application
# File lib/rack/builder.rb, line 63
def self.parse_file(config, opts = Server::Options.new)
  if config.end_with?('.ru')
    return self.load_file(config, opts)
  else
    require config
    app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
    return app, {}
  end
end

Public Instance Methods

call(env) click to toggle source

Call the Rack application generated by this builder instance. Note that this rebuilds the Rack application and runs the warmup code (if any) every time it is called, so it should not be used if performance is important.

# File lib/rack/builder.rb, line 242
def call(env)
  to_app.call(env)
end
freeze_app() click to toggle source

Freeze the app (set using run) and all middleware instances when building the application in to_app.

# File lib/rack/builder.rb, line 225
def freeze_app
  @freeze_app = true
end
map(path, &block) click to toggle source

Creates a route within the application. Routes under the mapped path will be sent to the Rack application specified by run inside the block. Other requests will be sent to the default application specified by run outside the block.

Rack::Builder.app do
  map '/heartbeat' do
    run Heartbeat
  end
  run App
end

The use method can also be used inside the block to specify middleware to run under a specific path:

Rack::Builder.app do
  map '/heartbeat' do
    use Middleware
    run Heartbeat
  end
  run App
end

This example includes a piece of middleware which will run before /heartbeat requests hit Heartbeat.

Note that providing a path of / will ignore any default application given in a run statement outside the block.

# File lib/rack/builder.rb, line 218
def map(path, &block)
  @map ||= {}
  @map[path] = block
end
run(app) click to toggle source

Takes an argument that is an object that responds to call and returns a Rack response. The simplest form of this is a lambda object:

run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }

However this could also be a class:

class Heartbeat
  def self.call(env)
   [200, { "Content-Type" => "text/plain" }, ["OK"]]
  end
end

run Heartbeat
# File lib/rack/builder.rb, line 175
def run(app)
  @run = app
end
to_app() click to toggle source

Return the Rack application generated by this instance.

# File lib/rack/builder.rb, line 230
def to_app
  app = @map ? generate_map(@run, @map) : @run
  fail "missing run or map statement" unless app
  app.freeze if @freeze_app
  app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
  @warmup.call(app) if @warmup
  app
end
use(middleware, *args, &block) click to toggle source

Specifies middleware to use in a stack.

class Middleware
  def initialize(app)
    @app = app
  end

  def call(env)
    env["rack.some_header"] = "setting an example"
    @app.call(env)
  end
end

use Middleware
run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }

All requests through to this application will first be processed by the middleware class. The call method in this example sets an additional environment key which then can be referenced in the application if required.

# File lib/rack/builder.rb, line 152
def use(middleware, *args, &block)
  if @map
    mapping, @map = @map, nil
    @use << proc { |app| generate_map(app, mapping) }
  end
  @use << proc { |app| middleware.new(app, *args, &block) }
end
warmup(prc = nil, &block) click to toggle source

Takes a lambda or block that is used to warm-up the application. This block is called before the Rack application is returned by to_app.

warmup do |app|
  client = Rack::MockRequest.new(app)
  client.get('/')
end

use SomeMiddleware
run MyApp
# File lib/rack/builder.rb, line 189
def warmup(prc = nil, &block)
  @warmup = prc || block
end

Private Instance Methods

generate_map(default_app, mapping) click to toggle source

Generate a URLMap instance by generating new Rack applications for each map block in this instance.

# File lib/rack/builder.rb, line 250
def generate_map(default_app, mapping)
  mapped = default_app ? { '/' => default_app } : {}
  mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
  URLMap.new(mapped)
end