Class: Rack::Builder
- Inherits:
-
Object
- Object
- Rack::Builder
- Defined in:
- lib/rack/builder.rb
Overview
Rack::Builder provides a domain-specific language (DSL) to construct Rack applications. It is primarily used to parse config.ru
files which instantiate several middleware and a final application which are hosted by a Rack-compatible web server.
Example:
app = Rack::Builder.new do
use Rack::CommonLogger
map "/ok" do
run lambda { |env| [200, {'content-type' => 'text/plain'}, ['OK']] }
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
-
.app(default_app = nil, &block) ⇒ Object
Create a new Rack::Builder instance and return the Rack application generated from it.
-
.load_file(path) ⇒ Object
Load the given file as a rackup file, treating the contents as if specified inside a Rack::Builder block.
-
.new_from_string(builder_script, file = "(rackup)") ⇒ Object
Evaluate the given
builder_script
string in the context of a Rack::Builder block, returning a Rack application. -
.parse_file(path) ⇒ Object
Parse the given config file to get a Rack application.
Instance Method Summary collapse
-
#call(env) ⇒ Object
Call the Rack application generated by this builder instance.
-
#freeze_app ⇒ Object
Freeze the app (set using run) and all middleware instances when building the application in to_app.
-
#initialize(default_app = nil, &block) ⇒ Builder
constructor
Initialize a new Rack::Builder instance.
-
#map(path, &block) ⇒ Object
Creates a route within the application.
-
#run(app = nil, &block) ⇒ Object
Takes a block or argument that is an object that responds to #call and returns a Rack response.
-
#to_app ⇒ Object
Return the Rack application generated by this instance.
-
#use(middleware, *args, &block) ⇒ Object
Specifies middleware to use in a stack.
-
#warmup(prc = nil, &block) ⇒ Object
Takes a lambda or block that is used to warm-up the application.
Constructor Details
#initialize(default_app = nil, &block) ⇒ Builder
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 evaluated in the context of the instance.
111 112 113 114 115 116 117 118 119 |
# File 'lib/rack/builder.rb', line 111 def initialize(default_app = nil, &block) @use = [] @map = nil @run = default_app @warmup = nil @freeze_app = false instance_eval(&block) if block_given? end |
Class Method Details
.app(default_app = nil, &block) ⇒ Object
Create a new Rack::Builder instance and return the Rack application generated from it.
123 124 125 |
# File 'lib/rack/builder.rb', line 123 def self.app(default_app = nil, &block) self.new(default_app, &block).to_app end |
.load_file(path) ⇒ Object
Load the given file as a rackup file, treating the contents as if specified inside a Rack::Builder block.
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
use Rack::ContentLength
require './app.rb'
run App
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rack/builder.rb', line 84 def self.load_file(path) config = ::File.read(path) config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8 if config[/^#\\(.*)/] fail "Parsing options from the first comment line is no longer supported: #{path}" end config.sub!(/^__END__\n.*\Z/m, '') return new_from_string(config, path) end |
.new_from_string(builder_script, file = "(rackup)") ⇒ Object
Evaluate the given builder_script
string in the context of a Rack::Builder block, returning a Rack application.
99 100 101 102 103 104 105 106 |
# File 'lib/rack/builder.rb', line 99 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 return builder.to_app end |
.parse_file(path) ⇒ Object
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.
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.
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
# is a 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 is a Rack application
62 63 64 65 66 67 68 69 |
# File 'lib/rack/builder.rb', line 62 def self.parse_file(path) if path.end_with?('.ru') return self.load_file(path) else require path return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join('')) end end |
Instance Method Details
#call(env) ⇒ Object
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.
263 264 265 |
# File 'lib/rack/builder.rb', line 263 def call(env) to_app.call(env) end |
#freeze_app ⇒ Object
Freeze the app (set using run) and all middleware instances when building the application in to_app.
246 247 248 |
# File 'lib/rack/builder.rb', line 246 def freeze_app @freeze_app = true end |
#map(path, &block) ⇒ Object
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.
class App
def call(env)
[200, {'content-type' => 'text/plain'}, ["Hello World"]]
end
end
class Heartbeat
def call(env)
[200, { "content-type" => "text/plain" }, ["OK"]]
end
end
app = Rack::Builder.app do
map '/heartbeat' do
run Heartbeat.new
end
run App.new
end
run app
The use
method can also be used inside the block to specify middleware to run under a specific path:
app = Rack::Builder.app do
map '/heartbeat' do
use Middleware
run Heartbeat.new
end
run App.new
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.
239 240 241 242 |
# File 'lib/rack/builder.rb', line 239 def map(path, &block) @map ||= {} @map[path] = block end |
#run(app = nil, &block) ⇒ Object
Takes a block or argument that is an object that responds to #call and returns a Rack response.
You can use a block:
run do |env|
[200, { "content-type" => "text/plain" }, ["Hello World!"]]
end
You can also provide a lambda:
run lambda { |env| [200, { "content-type" => "text/plain" }, ["OK"]] }
You can also provide a class instance:
class Heartbeat
def call(env)
[200, { "content-type" => "text/plain" }, ["OK"]]
end
end
run Heartbeat.new
180 181 182 183 184 |
# File 'lib/rack/builder.rb', line 180 def run(app = nil, &block) raise ArgumentError, "Both app and block given!" if app && block_given? @run = app || block end |
#to_app ⇒ Object
Return the Rack application generated by this instance.
251 252 253 254 255 256 257 258 |
# File 'lib/rack/builder.rb', line 251 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.
146 147 148 149 150 151 152 |
# File 'lib/rack/builder.rb', line 146 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. 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
196 197 198 |
# File 'lib/rack/builder.rb', line 196 def warmup(prc = nil, &block) @warmup = prc || block end |