Permalink
Browse files

Add Rack::ErrorPages with tests, update readme and authors too

  • Loading branch information...
1 parent ee269bd commit db6e5e8499fd79c21adcf7cfab22e98dcad5c710 @matth committed Nov 4, 2010
Showing with 112 additions and 0 deletions.
  1. +1 −0 AUTHORS
  2. +1 −0 README.rdoc
  3. +1 −0 lib/rack/contrib.rb
  4. +57 −0 lib/rack/contrib/error_pages.rb
  5. +52 −0 test/spec_error_pages.rb
View
@@ -24,3 +24,4 @@ TJ Holowaychuk <tj@vision-media.ca>
anupom syam <anupom.syam@gmail.com>
ichverstehe <ichverstehe@gmail.com>
kubicek <jiri@kubicek.cz>
+Matt Haynes <matt@matthaynes.net>
View
@@ -48,6 +48,7 @@ interface:
* 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
+* Rack::ErrorPages - Serves static HTML pages for any status code, just create a page name :status.html, e.g 500.html
=== Use
View
@@ -13,6 +13,7 @@ def self.release
autoload :Cookies, "rack/contrib/cookies"
autoload :CSSHTTPRequest, "rack/contrib/csshttprequest"
autoload :Deflect, "rack/contrib/deflect"
+ autoload :ErrorPages, "rack/contrib/error_pages"
autoload :ExpectationCascade, "rack/contrib/expectation_cascade"
autoload :GarbageCollector, "rack/contrib/garbagecollector"
autoload :JSONP, "rack/contrib/jsonp"
@@ -0,0 +1,57 @@
+module Rack
+
+ #
+ # Serve static error pages based on status code if they exist.
+ #
+ # If your app returns a status code with a matching :status.html file
+ # in the static_dir then this file will be served
+ #
+ # EG: Your app returns a 500 status, this middleware serves the file
+ # found at "#{static_dir}/500.html"
+ #
+ # Implements some memoization so files are read only once per process
+ # and only when required
+ #
+ # Matt Haynes 2010
+ #
+
+ class ErrorPages
+
+ F = ::File
+
+ def initialize(app, static_dir = 'public')
+
+ @app = app
+
+ @pages = Hash.new do |hash, key|
+
+ path = F.expand_path("#{static_dir}/#{key}.html")
+
+ if F.exists? path
+ content = F.read(path)
+ length = content.size.to_s
+ hash[key] = [content, length]
+ else
+ hash[key] = nil
+ end
+
+ end
+
+ end
+
+ def call(env)
+
+ status, headers, body = @app.call(env)
+
+ if @pages[status].nil?
+ [status, headers, body]
+ else
+ [status, headers.merge({ 'Content-Type' => 'text/html', 'Content-Length' => @pages[status][1] }), @pages[status][0]]
+ end
+
+ end
+
+ end
+
+end
+
View
@@ -0,0 +1,52 @@
+require 'test/spec'
+
+require 'rack'
+require 'rack/contrib/error_pages'
+
+describe "Rack::ErrorPages" do
+
+ context 'when the HTML file cannot be found' do
+ it 'should return the original status, body and headers from the app' do
+
+ middleware = Rack::ErrorPages.new(lambda {|env| [200, {'Content-Type' => 'text/plain'}, 'hello rack'] })
+
+ status, headers, body = middleware.call({})
+
+ status.should == 200
+ headers.should == {'Content-Type' => 'text/plain'}
+ body.should == "hello rack"
+
+ end
+ end
+
+ context 'when the HTML file can be found' do
+
+ app = lambda {|env| [404, {'Foo' => 'Bar', 'Content-Length' => '10', 'Content-Type' => 'text/plain'}, 'hello rack'] }
+
+ middleware = Rack::ErrorPages.new(app, 'test')
+
+ status, headers, body = middleware.call({})
+
+ it 'should return the status of the original app' do
+ status.should == 404
+ end
+
+ it 'should return the headers of the original app' do
+ headers['Foo'].should == 'Bar'
+ end
+
+ it 'should set the content-length header to the length of the HTML file' do
+ headers['Content-Length'].should == File.read('test/404.html').size.to_s
+ end
+
+ it 'should set the content-type header to text/html' do
+ headers['Content-Type'].should == 'text/html'
+ end
+
+ it 'should set the body to the body contents HTML file' do
+ body.should == File.read('test/404.html')
+ end
+
+ end
+
+end

0 comments on commit db6e5e8

Please sign in to comment.