Skip to content

Commit

Permalink
Rack::TryStatic middleware
Browse files Browse the repository at this point in the history
- try match request to a static file
  • Loading branch information
gmarik committed Sep 19, 2010
1 parent a8f77fb commit 0702201
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.rdoc
Expand Up @@ -47,6 +47,7 @@ interface:
* Rack::Access - Limits access based on IP address
* Rack::ResponseHeaders - Manipulates response headers object at runtime
* Rack::SimpleEndpoint - Creates simple endpoints with routing rules, similar to Sinatra actions
* Rack::TryStatic - Tries to match request to a static file

=== Use

Expand Down
36 changes: 36 additions & 0 deletions lib/rack/contrib/try_static.rb
@@ -0,0 +1,36 @@
module Rack

# The Rack::TryStatic middleware delegates requests to Rack::Static middleware
# trying to match a static file
#
# Examples
#
# use Rack::TryStatic,
# :root => "public", # static files root dir
# :urls => %w[/], # match all requests
# :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially
#
# uses same options as Rack::Static with extra :try option which is an array
# of postfixes to find desired file

class TryStatic

def initialize(app, options)
@app = app
@try = ['', *options.delete(:try)]
@static = ::Rack::Static.new(
lambda { [404, {}, []] },
options)
end

def call(env)
orig_path = env['PATH_INFO']
found = nil
@try.each do |path|
resp = @static.call(env.merge!({'PATH_INFO' => orig_path + path}))
break if 404 != resp[0] && found = resp
end
found or @app.call(env.merge!('PATH_INFO' => orig_path))
end
end
end
1 change: 1 addition & 0 deletions test/documents/existing.html
@@ -0,0 +1 @@
existing.html
1 change: 1 addition & 0 deletions test/documents/index.htm
@@ -0,0 +1 @@
index.htm
1 change: 1 addition & 0 deletions test/documents/index.html
@@ -0,0 +1 @@
index.html
44 changes: 44 additions & 0 deletions test/spec_rack_try_static.rb
@@ -0,0 +1,44 @@
require 'test/spec'

require 'rack'
require 'rack/contrib/try_static'
require 'rack/mock'

def request(options = {})
options.merge!({
:urls => %w[/],
:root => ::File.expand_path(::File.dirname(__FILE__)),
})

@request =
Rack::MockRequest.new(
Rack::TryStatic.new(
lambda {[200, {}, ["Hello World"]]},
options))
end

describe "Rack::TryStatic" do
context 'when file cannot be found' do
it 'should call call app' do
res = request(:try => ['html']).get('/documents')
res.should.be.ok
res.body.should == "Hello World"
end
end

context 'when file can be found' do
it 'should serve first found' do
res = request(:try => ['.html', '/index.html', '/index.htm']).get('/documents')
res.should.be.ok
res.body.strip.should == "index.html"
end
end

context 'when path_info maps directly to file' do
it 'should serve existing' do
res = request(:try => ['/index.html']).get('/documents/existing.html')
res.should.be.ok
res.body.strip.should == "existing.html"
end
end
end

0 comments on commit 0702201

Please sign in to comment.