Class: Rack::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/builder.rb

Overview

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.

Constant Summary collapse

UTF_8_BOM =
'\xef\xbb\xbf'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(default_app = nil, &block) ⇒ Builder

Returns a new instance of Builder.



69
70
71
72
# File 'lib/rack/builder.rb', line 69

def initialize(default_app = nil, &block)
  @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
  instance_eval(&block) if block_given?
end

Class Method Details

.app(default_app = nil, &block) ⇒ Object



74
75
76
# File 'lib/rack/builder.rb', line 74

def self.app(default_app = nil, &block)
  self.new(default_app, &block).to_app
end

.load_file(path, opts = Server::Options.new) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rack/builder.rb', line 48

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
    options = opts.parse! $1.split(/\s+/)
  end

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

  return app, options
end

.new_from_string(builder_script, file = "(rackup)") ⇒ Object



64
65
66
67
# File 'lib/rack/builder.rb', line 64

def self.new_from_string(builder_script, file = "(rackup)")
  eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
    TOPLEVEL_BINDING, file, 0
end

.parse_file(config, opts = Server::Options.new) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/rack/builder.rb', line 38

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

Instance Method Details

#call(env) ⇒ Object



176
177
178
# File 'lib/rack/builder.rb', line 176

def call(env)
  to_app.call(env)
end

#freeze_appObject

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



163
164
165
# File 'lib/rack/builder.rb', line 163

def freeze_app
  @freeze_app = true
end

#map(path, &block) ⇒ Object

Creates a route within the application.

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

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

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

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



156
157
158
159
# File 'lib/rack/builder.rb', line 156

def map(path, &block)
  @map ||= {}
  @map[path] = block
end

#run(app) ⇒ Object

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


120
121
122
# File 'lib/rack/builder.rb', line 120

def run(app)
  @run = app
end

#to_appObject



167
168
169
170
171
172
173
174
# File 'lib/rack/builder.rb', line 167

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) ⇒ Object

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.



97
98
99
100
101
102
103
# File 'lib/rack/builder.rb', line 97

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) ⇒ Object

Takes a lambda or block that is used to warm-up the application.

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

use SomeMiddleware
run MyApp


133
134
135
# File 'lib/rack/builder.rb', line 133

def warmup(prc = nil, &block)
  @warmup = prc || block
end