Skip to content
This repository has been archived by the owner on May 26, 2021. It is now read-only.

Commit

Permalink
Fix middleware not running when app run as middleware [sinatra#161]
Browse files Browse the repository at this point in the history
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
rtomayko committed Feb 27, 2009
1 parent 98427ce commit d7eabb9
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 40 deletions.
85 changes: 45 additions & 40 deletions lib/sinatra/base.rb
Expand Up @@ -545,8 +545,8 @@ def clean_backtrace(trace)
@conditions = []
@templates = {}
@middleware = []
@callsite = nil
@errors = {}
@prototype = nil

class << self
attr_accessor :routes, :filters, :conditions, :templates,
Expand Down Expand Up @@ -745,7 +745,7 @@ def configure(*envs, &block)
end

def use(middleware, *args, &block)
reset_middleware
@prototype = nil
@middleware << [middleware, args, block]
end

Expand All @@ -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)
Expand All @@ -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?
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions test/middleware_test.rb
Expand Up @@ -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.