From 3a8f40f684f2f477ac322345a5d5e2e578461df9 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 11:39:14 +1100 Subject: [PATCH] all but one example of the path pattern compilation tests work --- lib/sinatra/base.rb | 16 ++++++++++++---- test/compile_test.rb | 33 +++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index c7c4316a..fdd0ee16 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1,4 +1,4 @@ -# external dependencies + # external dependencies require 'rack' require 'tilt' require "rack/protection" @@ -1302,14 +1302,18 @@ module Sinatra def compile(path) keys = [] if path.respond_to? :to_str - pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) } + pad = [] + pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c| + pad += escaped(c) if c.match(/[\.@]/) + encoded(c) + end pattern.gsub!(/((:\w+)|\*)/) do |match| if match == "*" keys << 'splat' "(.*?)" else keys << $2[1..-1] - "([^/?#]+)" + "([^#{pad.join}/?#]+)" end end [/^#{pattern}$/, keys] @@ -1328,10 +1332,14 @@ module Sinatra def encoded(char) enc = URI.escape(char) - enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char + enc = "(?:#{escaped(char, enc).join('|')})" if enc == char enc = "(?:#{enc}|#{encoded('+')})" if char == " " enc end + + def escaped(char, enc = URI.escape(char)) + [Regexp.escape(enc), URI.escape(char, /./)] + end public # Makes the methods defined in the block and in the Modules given diff --git a/test/compile_test.rb b/test/compile_test.rb index bbc23dfd..38a76f08 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -26,11 +26,6 @@ class CompileTest < Test::Unit::TestCase ["/*" , %r{^/(.*?)$} , "/foo/bar" , ["foo/bar"] ], ["/:foo/*" , %r{^/([^/?#]+)/(.*?)$} , "/foo/bar/baz" , ["foo", "bar/baz"] ], ["/:foo/:bar" , %r{^/([^/?#]+)/([^/?#]+)$} , "/user@example.com/name" , ["user@example.com", "name"] ], - ["/:file.:ext" , %r{^/([^/?#]+)(?:\.|%2E)([^/?#]+)$} , "/pony.jpg" , ["pony", "jpg"] ], - ["/:file.:ext" , %r{^/([^/?#]+)(?:\.|%2E)([^/?#]+)$} , "/pony%2Ejpg" , ["pony", "jpg"] ], - ["/:file.:ext" , %r{^/([^/?#]+)(?:\.|%2E)([^/?#]+)$} , "/.jpg" , nil ], - ["/test.bar" , %r{^/test(?:\.|%2E)bar$} , "/test.bar" , [] ], - ["/test.bar" , %r{^/test(?:\.|%2E)bar$} , "/test0bar" , nil ], ["/test$/" , %r{^/test(?:\$|%24)/$} , "/test$/" , [] ], ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/te+st/" , [] ], ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/test/" , nil ], @@ -43,21 +38,31 @@ class CompileTest < Test::Unit::TestCase ["/:foo/*" , %r{^/([^/?#]+)/(.*?)$} , "/hello%20world/how%20are%20you" , ["hello%20world", "how%20are%20you"]], ["/*/foo/*/*" , %r{^/(.*?)/foo/(.*?)/(.*?)$} , "/bar/foo/bling/baz/boom" , ["bar", "bling", "baz/boom"] ], ["/*/foo/*/*" , %r{^/(.*?)/foo/(.*?)/(.*?)$} , "/bar/foo/baz" , nil ], - ["/:name.?:format?" , %r{^/([^/?#]+)(?:\.|%2E)?([^/?#]+)?$} , "/foo" , ["foo", nil] ], - ["/:name.?:format?" , %r{^/([^/?#]+)(?:\.|%2E)?([^/?#]+)?$} , "/.bar" , [".bar", nil] ], - ["/:name.?:format?" , %r{^/([^/?#]+)(?:\.|%2E)?([^/?#]+)?$} , "/foo.bar" , ["foo", "bar"] ], - ["/:name.?:format?" , %r{^/([^/?#]+)(?:\.|%2E)?([^/?#]+)?$} , "/foo%2Ebar" , ["foo", "bar"] ], - ["/:user@?:host?" , %r{^/([^/?#]+)(?:@|%40)?([^/?#]+)?$} , "/foo@bar" , ["foo", "bar"] ], - ["/:user@?:host?" , %r{^/([^/?#]+)(?:@|%40)?([^/?#]+)?$} , "/foo.foo@bar" , ["foo.foo", "bar"] ], - ["/:user@?:host?" , %r{^/([^/?#]+)(?:@|%40)?([^/?#]+)?$} , "/foo@bar.bar" , ["foo", "bar.bar"] ], + ["/test.bar" , %r{^/test(?:\.|%2E)bar$} , "/test.bar" , [] ], + ["/test.bar" , %r{^/test(?:\.|%2E)bar$} , "/test0bar" , nil ], + ["/:file.:ext" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)$} , "/pony.jpg" , ["pony", "jpg"] ], + ["/:file.:ext" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)$} , "/pony%2Ejpg" , ["pony", "jpg"] ], + ["/:file.:ext" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)$} , "/.jpg" , nil ], + ["/:name.?:format?" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?$} , "/foo" , ["foo", nil] ], + ["/:name.?:format?" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?$} , "/.bar" , [".bar", nil] ], + ["/:name.?:format?" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?$} , "/foo.bar" , ["foo", "bar"] ], + ["/:name.?:format?" , %r{^/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?$} , "/foo%2Ebar" , ["foo", "bar"] ], + ["/:user@?:host?" , %r{^/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?$} , "/foo@bar" , ["foo", "bar"] ], + ["/:user@?:host?" , %r{^/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?$} , "/foo.foo@bar" , ["foo.foo", "bar"] ], + ["/:user@?:host?" , %r{^/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?$} , "/foo@bar.bar" , ["foo", "bar.bar"] ], ].each do |pattern, regexp, example, expected| app = nil it "generates #{regexp.source} from #{pattern}, with #{example} succeeding" do app ||= mock_app {} compiled, keys = app.send(:compile, pattern) - assert_equal regexp.source, compiled.source + match = compiled.match(example) - match ? assert_equal(expected, match.captures.to_a) : assert_nil(expected) + match ? assert_equal(expected, match.captures.to_a) : assert_equal(expected, match) + + # Compare the regexp last, to see that the captures work, + # but the regexp might not the expected one. + # + assert_equal regexp, compiled end end end