Permalink
Browse files

specs added, comments, readme updated

  • Loading branch information...
1 parent b8e7f9a commit 088813ea7b4fdd670f3ae01b6da24054636fe8af Daniel Mendler committed Mar 4, 2009
Showing with 123 additions and 8 deletions.
  1. +4 −1 README.rdoc
  2. +11 −3 lib/rack/contrib/rewrite.rb
  3. +9 −4 lib/rack/contrib/tidy.rb
  4. +7 −0 test/invalid-output.html
  5. +8 −0 test/invalid.html
  6. +35 −0 test/spec_rack_rewrite.rb
  7. +38 −0 test/spec_rack_tidy.rb
  8. +11 −0 test/valid.html
View
@@ -35,12 +35,15 @@ interface:
CSS for cross-site AJAX-style data loading
* Rack::Deflect - Helps protect against DoS attacks.
* Rack::ResponseCache - Caches responses to requests without query strings
- to Disk or a user provider Ruby object. Similar to Rails' page caching.
+ to Disk or a user provider Ruby object. Similar to Rails' page caching. (Better use Rack::Cache by rtomayko)
* Rack::RelativeRedirect - Transforms relative paths in redirects to
absolute URLs.
* Rack::Backstage - Returns content of specified file if it exists, which makes
it convenient for putting up maintenance pages.
* Rack::Format - Adds a format extension at the end of the URI when there is none, corresponding to the mime-type given in the Accept HTTP header.
+* Rack::Tidy - Validate and cleanup XML output with xmllint or tidy
+* Rack::Rewrite - Rewrite url bases. If you have an application which wants to be installed in / you can installed it e.g.
+ in /~user/app by using this middleware's base rewriting.
=== Use
@@ -1,21 +1,29 @@
+require 'cgi'
+
module Rack
+ # Rack::Rewrite rewrites absolute paths to another base.
+ # If you have an application for / which you want to install under /~user/app
+ # then you can use this middleware.
+ # You have to specify the base as option.
+
class Rewrite
def initialize(app, options={})
+ raise ArgumentError, 'Base option is missing' if !options[:base]
@app = app
@base = options[:base]
@base.gsub!(%r{^/+|/+$}, '')
end
def call(env)
if env['PATH_INFO'] =~ %r{^/#{@base}$|^/#{@base}/}
- env['PATH_INFO'] = env['PATH_INFO'].sub(%r{^/#{@base}/?}, '/')
- env['REQUEST_URI'] = env['REQUEST_URI'].sub(%r{^/#{@base}/?}, '/')
+ env['PATH_INFO'] = env['PATH_INFO'].to_s.sub(%r{^/#{@base}/?}, '/')
+ env['REQUEST_URI'] = env['REQUEST_URI'].to_s.sub(%r{^/#{@base}/?}, '/')
status, header, body = @app.call(env)
if [301, 302, 303, 307].include?(status)
- header['Location'] = '/' + @base + header['Location']
+ header['Location'] = '/' + @base + header['Location'] if header['Location'][0..0] == '/'
elsif ![204, 304].include?(status) && html?(header)
tmp = ''
body.each {|data| tmp << data}
View
@@ -1,14 +1,19 @@
require 'open3'
module Rack
+ # Rack::Tidy cleans markup and validates it.
+ # It adds a comment with the errors at the end.
+ #
+ # You can specify tidy/xmllint via the mode options.
+
class Tidy
TIDY_CMD = 'tidy -i -xml -access 3 -quiet'
XMLLINT_CMD = 'xmllint --format --valid -'
def initialize(app, options={})
@app = app
- mode = options[:mode] || :tidy
- @cmd = mode == :tidy ? TIDY_CMD : XMLLINT_CMD
+ mode = options[:mode] || 'tidy'
+ @cmd = mode.to_s == 'tidy' ? TIDY_CMD : XMLLINT_CMD
end
def call(env)
@@ -19,7 +24,7 @@ def call(env)
stdin.close
body = stdout.read
errors = stderr.read.strip
- body += "<!--\n#{errors}\n-->\n" if !errors.blank?
+ body += "<!--\n#{errors}\n-->\n" if !errors.empty?
}
header['Content-Length'] = body.length.to_s
[status, header, body]
@@ -28,7 +33,7 @@ def call(env)
private
def html?(header)
- %w(application/xhtml+xml text/html).any? do |type|
+ %w(application/xhtml+xml text/html text/xml).any? do |type|
header['Content-Type'].to_s.include?(type)
end
end
View
@@ -0,0 +1,7 @@
+<!--
+line 6 column 14 - Error: unexpected </div> in <p>
+line 7 column 3 - Error: unexpected </body> in <p>
+line 8 column 1 - Error: unexpected </html> in <p>
+line 1 column 1 - Access: [3.2.1.1]: <doctype> missing.
+line 1 column 1 - Access: [3.3.1.1]: use style sheets to control presentation.
+-->
View
@@ -0,0 +1,8 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Titel</title>
+ </head>
+ <body>
+ Blabla<p></div>
+ </body>
+</html>
View
@@ -0,0 +1,35 @@
+require 'test/spec'
+require 'rack/mock'
+require 'rack/contrib/rewrite'
+
+context "Rack::Rewrite" do
+
+ def action(id, expect)
+ response = Rack::MockRequest.new(@app).get("/~user/app/action#{id}")
+ response.body.should.equal(expect)
+ response.status.should.equal(200)
+ end
+
+ specify "should rewrite absolute paths" do
+ @app = Rack::Builder.new do
+ use Rack::Lint
+ use Rack::Rewrite, :base => '/~user/app'
+ run Rack::URLMap.new({
+ '/action1' => lambda { [200, {"Content-Type" => "text/html"}, ['<a href="/action">']] },
+ '/action2' => lambda { [200, {"Content-Type" => "text/html"}, ['<a href=\'/action\'>']] },
+ '/action3' => lambda { [200, {"Content-Type" => "text/html"}, ['<a href="http://xy">']] },
+ '/action4' => lambda { [200, {"Content-Type" => "text/html"}, ['<a href="relative">']] },
+ '/action5' => lambda { [200, {"Content-Type" => "text/html"}, ['<img title="image" src="/img"/>']] },
+ '/action6' => lambda { [200, {"Content-Type" => "text/html"}, ['<link rel="stylesheet" href="/style.css" type="text/css">']] },
+ })
+ end
+
+ action(1, '<a href="/~user/app/action">')
+ action(2, '<a href=\'/~user/app/action\'>')
+ action(3, '<a href="http://xy">')
+ action(4, '<a href="relative">')
+ action(5, '<img title="image" src="/~user/app/img"/>')
+ action(6, '<link rel="stylesheet" href="/~user/app/style.css" type="text/css">')
+ end
+
+end
View
@@ -0,0 +1,38 @@
+require 'test/spec'
+require 'rack/mock'
+require 'rack/contrib/tidy'
+
+context "Rack::Tidy" do
+
+ specify "should run tidy on xml" do
+ app = Rack::Builder.new do
+ use Rack::Lint
+ use Rack::Tidy
+ run Rack::URLMap.new({
+ "/valid" => lambda { [200, {"Content-Type" => "text/html"}, File.open('test/valid.html')] },
+ "/invalid" => lambda { [200, {"Content-Type" => "text/html"}, File.open('test/invalid.html')] },
+ })
+ end
+ response = Rack::MockRequest.new(app).get('/valid')
+ response.status.should.equal(200)
+ response.body.should.equal(File.read('test/valid.html'))
+
+ response = Rack::MockRequest.new(app).get('/invalid')
+ response.status.should.equal(200)
+ response.body.should.equal(File.read('test/invalid-output.html'))
+ end
+
+ specify "should not run tidy on non-xml" do
+ app = Rack::Builder.new do
+ use Rack::Lint
+ use Rack::Tidy
+ run Rack::URLMap.new({
+ "/image" => lambda { [200, {"Content-Type" => "image/png", "Content-Length" => "9"}, ['>no html<']] },
+ })
+ end
+ response = Rack::MockRequest.new(app).get('/image')
+ response.status.should.equal(200)
+ response.body.should.equal('>no html<')
+ end
+
+end
View
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Titel</title>
+ <link rel="stylesheet" type="text/css" href="style.css" />
+ </head>
+ <body>
+ <div>Content</div>
+ </body>
+</html>

0 comments on commit 088813e

Please sign in to comment.