Skip to content

Commit

Permalink
Add support for specifying options to builder and thus config.ru. (r…
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix authored Jul 19, 2023
1 parent af9e278 commit 444dc8a
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 8 deletions.
29 changes: 21 additions & 8 deletions lib/rack/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

require_relative 'urlmap'

module Rack; end
Rack::BUILDER_TOPLEVEL_BINDING = ->(builder){builder.instance_eval{binding}}

module Rack
# Rack::Builder provides a domain-specific language (DSL) to construct Rack
# applications. It is primarily used to parse +config.ru+ files which
Expand Down Expand Up @@ -59,9 +62,9 @@ class Builder
# # requires ./my_app.rb, which should be in the
# # process's current directory. After requiring,
# # assumes MyApp constant is a Rack application
def self.parse_file(path)
def self.parse_file(path, **options)
if path.end_with?('.ru')
return self.load_file(path)
return self.load_file(path, **options)
else
require path
return Object.const_get(::File.basename(path, '.rb').split('_').map(&:capitalize).join(''))
Expand All @@ -81,7 +84,7 @@ def self.parse_file(path)
# use Rack::ContentLength
# require './app.rb'
# run App
def self.load_file(path)
def self.load_file(path, **options)
config = ::File.read(path)
config.slice!(/\A#{UTF_8_BOM}/) if config.encoding == Encoding::UTF_8

Expand All @@ -91,33 +94,43 @@ def self.load_file(path)

config.sub!(/^__END__\n.*\Z/m, '')

return new_from_string(config, path)
return new_from_string(config, path, **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)")
def self.new_from_string(builder_script, path = "(rackup)", **options)
builder = self.new(**options)

# 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
binding = BUILDER_TOPLEVEL_BINDING.call(builder)
eval(builder_script, binding, path)

return builder.to_app
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 evaluated in the context of the instance.
def initialize(default_app = nil, &block)
def initialize(default_app = nil, **options, &block)
@use = []
@map = nil
@run = default_app
@warmup = nil
@freeze_app = false
@options = options

instance_eval(&block) if block_given?
end

# Any options provided to the Rack::Builder instance at initialization.
# These options can be server-specific. Some general options are:
#
# * +:isolation+: One of +process+, +thread+ or +fiber+. The execution
# isolation model to use.
attr :options

# Create a new Rack::Builder instance and return the Rack application
# generated from it.
def self.app(default_app = nil, &block)
Expand Down
5 changes: 5 additions & 0 deletions test/spec_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ def builder_to_app(&block)
Rack::Lint.new Rack::Builder.new(&block).to_app
end

it "can provide options" do
builder = Rack::Builder.new(foo: :bar)
builder.options[:foo].must_equal :bar
end

it "supports run with block" do
app = builder_to_app do
run {|env| [200, { "content-type" => "text/plain" }, ["OK"]]}
Expand Down

0 comments on commit 444dc8a

Please sign in to comment.