Fix middleware not running when app run as middleware [#161]

The app's middleware pipeline was ignored when the app itself was
run as middleware. This was due to the separate call paths for
middleware vs. endpoint apps. This change makes it so that both
endpoint and middleware apps are invoked via the same instance
level #call method.

One potentially confusing aspect of this change is that Base.new now
returns the head of the app's middleware pipeline. If no middleware
is used by the app, this will be an instance of the Base class;
however, if middleware is used, Base.new will return the head of
the middleware chain leading to the Base instance.
This commit is contained in:
Ryan Tomayko 2009-02-26 22:02:36 -08:00
parent 98427cefa7
commit d7eabb9739
2 changed files with 53 additions and 40 deletions

View File

@ -545,8 +545,8 @@ module Sinatra
@conditions = []
@templates = {}
@middleware = []
@callsite = nil
@errors = {}
@prototype = nil
class << self
attr_accessor :routes, :filters, :conditions, :templates,
@ -745,7 +745,7 @@ module Sinatra
end
def use(middleware, *args, &block)
reset_middleware
@prototype = nil
@middleware << [middleware, args, block]
end
@ -766,22 +766,61 @@ module Sinatra
puts "== Someone is already performing on port #{port}!"
end
# The prototype instance used to process requests.
def prototype
@prototype ||= new
end
# Create a new instance of the class fronted by its middleware
# pipeline. The object is guaranteed to respond to #call but may not be
# an instance of the class new was called on.
def new(*args, &bk)
builder = Rack::Builder.new
builder.use Rack::Session::Cookie if sessions?
builder.use Rack::CommonLogger if logging?
builder.use Rack::MethodOverride if methodoverride?
@middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
builder.run super
builder.to_app
end
def call(env)
synchronize do
reload! if reload?
construct_middleware if @callsite.nil?
@callsite.call(env)
prototype.call(env)
end
end
def reloading?
@reloading
end
def reload!
@reloading = true
superclass.send :reset!, self
reset!
$LOADED_FEATURES.delete("sinatra.rb")
::Kernel.load app_file
@reloading = false
end
def reset!(base=superclass)
@routes = base.dupe_routes
@templates = base.templates.dup
@conditions = []
@filters = base.filters.dup
@errors = base.errors.dup
@middleware = base.middleware.dup
@prototype = nil
end
protected
def dupe_routes
routes.inject({}) do |hash,(request_method,routes)|
hash[request_method] = routes.dup
hash
end
end
private
def detect_rack_handler
servers = Array(self.server)
@ -795,38 +834,11 @@ module Sinatra
fail "Server handler (#{servers.join(',')}) not found."
end
def construct_middleware(builder=Rack::Builder.new)
builder.use Rack::Session::Cookie if sessions?
builder.use Rack::CommonLogger if logging?
builder.use Rack::MethodOverride if methodoverride?
@middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
builder.run new
@callsite = builder.to_app
end
def reset_middleware
@callsite = nil
end
def reset!(subclass = self)
subclass.routes = dupe_routes
subclass.templates = templates.dup
subclass.conditions = []
subclass.filters = filters.dup
subclass.errors = errors.dup
subclass.middleware = middleware.dup
subclass.send :reset_middleware
end
def inherited(subclass)
reset!(subclass)
subclass.reset! self
super
end
def reloading?
@reloading ||= false
end
@@mutex = Mutex.new
def synchronize(&block)
if lock?
@ -836,13 +848,6 @@ module Sinatra
end
end
def dupe_routes
routes.inject({}) do |hash,(request_method,routes)|
hash[request_method] = routes.dup
hash
end
end
def metadef(message, &block)
(class << self; self; end).
send :define_method, message, &block

View File

@ -57,4 +57,12 @@ describe "Middleware" do
assert_equal '/foo', body
assert_equal "UpcaseMiddleware, DowncaseMiddleware", response['X-Tests']
end
it "works when app is used as middleware" do
@app.use UpcaseMiddleware
@app = @app.new
get '/Foo'
assert_equal "/FOO", body
assert_equal "UpcaseMiddleware", response['X-Tests']
end
end