Permalink
Browse files

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.
  • Loading branch information...
1 parent 98427ce commit d7eabb9739ae53d0795ad462bcd6a3e1284a5684 @rtomayko rtomayko committed Feb 27, 2009
Showing with 53 additions and 40 deletions.
  1. +45 −40 lib/sinatra/base.rb
  2. +8 −0 test/middleware_test.rb
View
@@ -545,8 +545,8 @@ def clean_backtrace(trace)
@conditions = []
@templates = {}
@middleware = []
- @callsite = nil
@errors = {}
+ @prototype = nil
class << self
attr_accessor :routes, :filters, :conditions, :templates,
@@ -745,7 +745,7 @@ def configure(*envs, &block)
end
def use(middleware, *args, &block)
- reset_middleware
+ @prototype = nil
@middleware << [middleware, args, block]
end
@@ -766,22 +766,61 @@ def run!(options={})
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 @@ def detect_rack_handler
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 @@ def synchronize(&block)
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
@@ -57,4 +57,12 @@ def call(env)
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

0 comments on commit d7eabb9

Please sign in to comment.