diff options
author | Jeremy Evans <code@jeremyevans.net> | 2020-01-30 15:16:29 -0800 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2020-01-30 15:16:49 -0800 |
commit | f2d2df4016a906beec755b63b4edfcc07b58ee05 (patch) | |
tree | bd909618b35c514c90d5dd4a3bd9eb5445e3abc4 | |
parent | e9f26e74913d7c2ef31f4fa81ee8c0e8d568ffb7 (diff) | |
download | rack-f2d2df4016a906beec755b63b4edfcc07b58ee05.tar.gz |
Add documentation to BodyProxy and Builder
-rw-r--r-- | lib/rack/body_proxy.rb | 11 | ||||
-rw-r--r-- | lib/rack/builder.rb | 77 |
2 files changed, 82 insertions, 6 deletions
diff --git a/lib/rack/body_proxy.rb b/lib/rack/body_proxy.rb index ebb7db39..cfc0796a 100644 --- a/lib/rack/body_proxy.rb +++ b/lib/rack/body_proxy.rb @@ -1,17 +1,25 @@ # frozen_string_literal: true module Rack + # Proxy for response bodies allowing calling a block when + # the response body is closed (after the response has been fully + # sent to the client). class BodyProxy + # Set the response body to wrap, and the block to call when the + # response has been fully sent. def initialize(body, &block) @body = body @block = block @closed = false end + # Return whether the wrapped body responds to the method. def respond_to_missing?(method_name, include_all = false) super or @body.respond_to?(method_name, include_all) end + # If not already closed, close the wrapped body and + # then call the block the proxy was initialized with. def close return if @closed @closed = true @@ -22,10 +30,13 @@ module Rack end end + # Whether the proxy is closed. The proxy starts as not closed, + # and becomes closed on the first call to close. def closed? @closed end + # Delegate missing methods to the wrapped body. def method_missing(method_name, *args, &block) @body.__send__(method_name, *args, &block) end diff --git a/lib/rack/builder.rb b/lib/rack/builder.rb index ebfa1f11..c9c45048 100644 --- a/lib/rack/builder.rb +++ b/lib/rack/builder.rb @@ -35,6 +35,32 @@ module Rack # https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom UTF_8_BOM = '\xef\xbb\xbf' + # 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 def self.parse_file(config, opts = Server::Options.new) if config.end_with?('.ru') return self.load_file(config, opts) @@ -45,6 +71,25 @@ module Rack end end + # 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 def self.load_file(path, opts = Server::Options.new) options = {} @@ -61,16 +106,23 @@ module Rack return app, options end + # Evaluate the given +builder_script+ string in the context of + # a Rack::Builder block, returning a Rack application. def self.new_from_string(builder_script, file = "(rackup)") eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app", TOPLEVEL_BINDING, file, 0 end + # 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. def initialize(default_app = nil, &block) @use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false instance_eval(&block) if block_given? end + # Create a new Rack::Builder instance and return the Rack application + # generated from it. def self.app(default_app = nil, &block) self.new(default_app, &block).to_app end @@ -121,7 +173,8 @@ module Rack @run = app end - # Takes a lambda or block that is used to warm-up the application. + # 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) @@ -134,25 +187,31 @@ module Rack @warmup = prc || block end - # Creates a route within the application. + # 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 '/' do + # map '/heartbeat' do # run Heartbeat # end + # run App # end # - # The +use+ method can also be used here to specify middleware to run under a specific path: + # The +use+ method can also be used inside the block to specify middleware to run under a specific path: # # Rack::Builder.app do - # map '/' do + # map '/heartbeat' do # use Middleware # run Heartbeat # end + # run App # end # - # This example includes a piece of middleware which will run before requests hit +Heartbeat+. + # 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. def map(path, &block) @map ||= {} @map[path] = block @@ -164,6 +223,7 @@ module Rack @freeze_app = true end + # Return the Rack application generated by this instance. def to_app app = @map ? generate_map(@run, @map) : @run fail "missing run or map statement" unless app @@ -173,12 +233,17 @@ module Rack app end + # 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. def call(env) to_app.call(env) end private + # Generate a URLMap instance by generating new Rack applications for each + # map block in this instance. 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 } |