From e2a020ca8e027eded4486db0a52ce8441b27cb50 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 10:52:13 +1100 Subject: [PATCH 01/16] set up "working" tests for path pattern compilation Note: We can optimize the tests by compiling only once for each group of examples --- test/compile_test.rb | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/compile_test.rb diff --git a/test/compile_test.rb b/test/compile_test.rb new file mode 100644 index 00000000..bbc23dfd --- /dev/null +++ b/test/compile_test.rb @@ -0,0 +1,63 @@ +# I like coding: UTF-8 +require File.expand_path('../helper', __FILE__) + +class CompileTest < Test::Unit::TestCase + # Pattern | Current Regexp | Example | Should Be + # ---------------------|----------------------------------------------------------|-------------|---------- + [ + ["/" , %r{^/$} , "/" , [] ], + ["/foo" , %r{^/foo$} , "/foo" , [] ], + ["/:foo" , %r{^/([^/?#]+)$} , "/foo" , ["foo"] ], + ["/:foo" , %r{^/([^/?#]+)$} , "/foo?" , nil ], + ["/:foo" , %r{^/([^/?#]+)$} , "/foo/bar" , nil ], + ["/:foo" , %r{^/([^/?#]+)$} , "/foo%2Fbar" , ["foo%2Fbar"] ], + ["/:foo" , %r{^/([^/?#]+)$} , "/" , nil ], + ["/:foo" , %r{^/([^/?#]+)$} , "/foo/" , nil ], + ["/f\u00F6\u00F6" , %r{^/f%C3%B6%C3%B6$} , "/f%C3%B6%C3%B6" , [] ], + ["/:foo/:bar" , %r{^/([^/?#]+)/([^/?#]+)$} , "/foo/bar" , ["foo", "bar"] ], + ["/hello/:person" , %r{^/hello/([^/?#]+)$} , "/hello/Frank" , ["Frank"] ], + ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/hello/world" , ["hello", "world"] ], + ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/hello" , ["hello", nil] ], + ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/" , [nil, nil] ], + ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "" , [nil, nil] ], + ["/*" , %r{^/(.*?)$} , "/" , [""] ], + ["/*" , %r{^/(.*?)$} , "/foo" , ["foo"] ], + ["/*" , %r{^/(.*?)$} , "/" , [""] ], + ["/*" , %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 ], + ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/teeest/" , nil ], + ["/test(bar)/" , %r{^/test(?:\(|%28)bar(?:\)|%29)/$} , "/test(bar)/" , [] ], + ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path%20with%20spaces" , [] ], + ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path%2Bwith%2Bspaces" , [] ], + ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path+with+spaces" , [] ], + ["/foo&bar" , %r{^/foo(?:&|%26)bar$} , "/foo&bar" , [] ], + ["/: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"] ], + ].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) + end + end +end From 3a8f40f684f2f477ac322345a5d5e2e578461df9 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 11:39:14 +1100 Subject: [PATCH 02/16] 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 From ebea208657b4d107fe3f45f13b0c54ea1a6ea48b Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 11:45:03 +1100 Subject: [PATCH 03/16] clarify variable name --- lib/sinatra/base.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index fdd0ee16..5e17abac 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1302,9 +1302,9 @@ module Sinatra def compile(path) keys = [] if path.respond_to? :to_str - pad = [] + ignore = "" pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c| - pad += escaped(c) if c.match(/[\.@]/) + ignore << escaped(c).join if c.match(/[\.@]/) encoded(c) end pattern.gsub!(/((:\w+)|\*)/) do |match| @@ -1313,7 +1313,7 @@ module Sinatra "(.*?)" else keys << $2[1..-1] - "([^#{pad.join}/?#]+)" + "([^#{ignore}/?#]+)" end end [/^#{pattern}$/, keys] From 6404e3de6c20a4920443b3f44e759c82d2eda4d8 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 15:14:18 +1100 Subject: [PATCH 04/16] grouping the path pattern examples by regexp, use \A...\z instead of ^...$ --- lib/sinatra/base.rb | 2 +- test/compile_test.rb | 155 +++++++++++++++++++++++++++---------------- 2 files changed, 100 insertions(+), 57 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 5e17abac..94ebb1fe 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1316,7 +1316,7 @@ module Sinatra "([^#{ignore}/?#]+)" end end - [/^#{pattern}$/, keys] + [/\A#{pattern}\z/, keys] elsif path.respond_to?(:keys) && path.respond_to?(:match) [path, path.keys] elsif path.respond_to?(:names) && path.respond_to?(:match) diff --git a/test/compile_test.rb b/test/compile_test.rb index 38a76f08..ca5801be 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -2,66 +2,109 @@ require File.expand_path('../helper', __FILE__) class CompileTest < Test::Unit::TestCase - # Pattern | Current Regexp | Example | Should Be - # ---------------------|----------------------------------------------------------|-------------|---------- + # Pattern, Current Regexp, [Examples, Should Bes] + # [ - ["/" , %r{^/$} , "/" , [] ], - ["/foo" , %r{^/foo$} , "/foo" , [] ], - ["/:foo" , %r{^/([^/?#]+)$} , "/foo" , ["foo"] ], - ["/:foo" , %r{^/([^/?#]+)$} , "/foo?" , nil ], - ["/:foo" , %r{^/([^/?#]+)$} , "/foo/bar" , nil ], - ["/:foo" , %r{^/([^/?#]+)$} , "/foo%2Fbar" , ["foo%2Fbar"] ], - ["/:foo" , %r{^/([^/?#]+)$} , "/" , nil ], - ["/:foo" , %r{^/([^/?#]+)$} , "/foo/" , nil ], - ["/f\u00F6\u00F6" , %r{^/f%C3%B6%C3%B6$} , "/f%C3%B6%C3%B6" , [] ], - ["/:foo/:bar" , %r{^/([^/?#]+)/([^/?#]+)$} , "/foo/bar" , ["foo", "bar"] ], - ["/hello/:person" , %r{^/hello/([^/?#]+)$} , "/hello/Frank" , ["Frank"] ], - ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/hello/world" , ["hello", "world"] ], - ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/hello" , ["hello", nil] ], - ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "/" , [nil, nil] ], - ["/?:foo?/?:bar?" , %r{^/?([^/?#]+)?/?([^/?#]+)?$} , "" , [nil, nil] ], - ["/*" , %r{^/(.*?)$} , "/" , [""] ], - ["/*" , %r{^/(.*?)$} , "/foo" , ["foo"] ], - ["/*" , %r{^/(.*?)$} , "/" , [""] ], - ["/*" , %r{^/(.*?)$} , "/foo/bar" , ["foo/bar"] ], - ["/:foo/*" , %r{^/([^/?#]+)/(.*?)$} , "/foo/bar/baz" , ["foo", "bar/baz"] ], - ["/:foo/:bar" , %r{^/([^/?#]+)/([^/?#]+)$} , "/user@example.com/name" , ["user@example.com", "name"] ], - ["/test$/" , %r{^/test(?:\$|%24)/$} , "/test$/" , [] ], - ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/te+st/" , [] ], - ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/test/" , nil ], - ["/te+st/" , %r{^/te(?:\+|%2B)st/$} , "/teeest/" , nil ], - ["/test(bar)/" , %r{^/test(?:\(|%28)bar(?:\)|%29)/$} , "/test(bar)/" , [] ], - ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path%20with%20spaces" , [] ], - ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path%2Bwith%2Bspaces" , [] ], - ["/path with spaces" , %r{^/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces$} , "/path+with+spaces" , [] ], - ["/foo&bar" , %r{^/foo(?:&|%26)bar$} , "/foo&bar" , [] ], - ["/: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 ], - ["/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| + ["/", %r{\A/\z}, [ + ["/", []] + ]], + ["/foo", %r{\A/foo\z}, [ + ["/foo", []] + ]], + ["/:foo", %r{\A/([^/?#]+)\z}, [ + ["/foo" , ["foo"]], + ["/foo?" , nil], + ["/foo/bar" , nil], + ["/foo%2Fbar", ["foo%2Fbar"]], + ["/" , nil], + ["/foo/" , nil] + ]], + ["/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z}, [ + ["/f%C3%B6%C3%B6", []] + ]], + ["/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}, [ + ["/foo/bar", ["foo", "bar"]] + ]], + ["/hello/:person", %r{\A/hello/([^/?#]+)\z}, [ + ["/hello/Frank", ["Frank"]] + ]], + ["/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z}, [ + ["/hello/world", ["hello", "world"]], + ["/hello" , ["hello", nil]], + ["/" , [nil, nil]], + ["" , [nil, nil]] + ]], + ["/*", %r{\A/(.*?)\z}, [ + ["/" , [""]], + ["/foo" , ["foo"]], + ["/" , [""]], + ["/foo/bar", ["foo/bar"]] + ]], + ["/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}, [ + ["/foo/bar/baz", ["foo", "bar/baz"]] + ]], + ["/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}, [ + ["/user@example.com/name", ["user@example.com", "name"]] + ]], + ["/test$/", %r{\A/test(?:\$|%24)/\z}, [ + ["/test$/", []] + ]], + ["/te+st/", %r{\A/te(?:\+|%2B)st/\z}, [ + ["/te+st/", []], + ["/test/", nil], + ["/teeest/", nil] + ]], + ["/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}, [ + ["/test(bar)/", []] + ]], + ["/path with spaces", %r{\A/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces\z}, [ + ["/path%20with%20spaces", []], + ["/path%2Bwith%2Bspaces", []], + ["/path+with+spaces", []] + ]], + ["/foo&bar", %r{\A/foo(?:&|%26)bar\z}, [ + ["/foo&bar", []] + ]], + ["/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}, [ + ["/hello%20world/how%20are%20you", ["hello%20world", "how%20are%20you"]] + ]], + ["/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z}, [ + ["/bar/foo/bling/baz/boom", ["bar", "bling", "baz/boom"]], + ["/bar/foo/baz", nil], + ]], + ["/test.bar", %r{\A/test(?:\.|%2E)bar\z}, [ + ["/test.bar", []], + ["/test0bar", nil] + ]], + ["/:file.:ext", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)\z}, [ + ["/pony.jpg", ["pony", "jpg"]], + ["/pony%2Ejpg", ["pony", "jpg"]], + ["/.jpg", nil] + ]], + ["/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z}, [ + ["/foo", ["foo", nil]], + ["/.bar", [".bar", nil]], + ["/foo.bar", ["foo", "bar"]], + ["/foo%2Ebar", ["foo", "bar"]] + ]], + ["/:user@?:host?", %r{\A/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?\z}, [ + ["/foo@bar", ["foo", "bar"]], + ["/foo.foo@bar", ["foo.foo", "bar"]], + ["/foo@bar.bar", ["foo", "bar.bar"]] + ]] + ].each do |pattern, regexp, examples_expectations| app = nil - it "generates #{regexp.source} from #{pattern}, with #{example} succeeding" do + examples_expectations.each do |example, expected| + it "generates #{regexp.source} from #{pattern}, with #{example} succeeding" do + app ||= mock_app {} + compiled, keys = app.send(:compile, pattern) + match = compiled.match(example) + match ? assert_equal(expected, match.captures.to_a) : assert_equal(expected, match) + end + end + it "generates #{regexp.source} from #{pattern}" do app ||= mock_app {} compiled, keys = app.send(:compile, pattern) - - match = compiled.match(example) - 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 From 31d640a5b302e5632035781ee831ff980790daa3 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Fri, 23 Mar 2012 15:38:21 +1100 Subject: [PATCH 05/16] possibly fixed a path pattern compilaction bug, added a commented spec for :name(.:format)? (see https://gist.github.com/2154980#gistcomment-169469 on the second part) --- test/compile_test.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index ca5801be..13104c8e 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -83,7 +83,7 @@ class CompileTest < Test::Unit::TestCase ]], ["/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z}, [ ["/foo", ["foo", nil]], - ["/.bar", [".bar", nil]], + ["/.bar", nil], ["/foo.bar", ["foo", "bar"]], ["/foo%2Ebar", ["foo", "bar"]] ]], @@ -91,7 +91,13 @@ class CompileTest < Test::Unit::TestCase ["/foo@bar", ["foo", "bar"]], ["/foo.foo@bar", ["foo.foo", "bar"]], ["/foo@bar.bar", ["foo", "bar.bar"]] - ]] + ]], + # From https://gist.github.com/2154980#gistcomment-169469. + # + # ["/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z}, [ + # ["/foo", ["foo"]], + # ["/foo.bar", ["foo", "bar"]] + # ]] ].each do |pattern, regexp, examples_expectations| app = nil examples_expectations.each do |example, expected| From dfa8409352c9e61d4431f61b34d07c6660338e78 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Sat, 24 Mar 2012 07:06:07 +1100 Subject: [PATCH 06/16] /:foo still parses /foo.bar correctly --- test/compile_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/compile_test.rb b/test/compile_test.rb index 13104c8e..15592d6b 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -14,6 +14,7 @@ class CompileTest < Test::Unit::TestCase ["/:foo", %r{\A/([^/?#]+)\z}, [ ["/foo" , ["foo"]], ["/foo?" , nil], + ["/foo.bar" , ["foo.bar"]], ["/foo/bar" , nil], ["/foo%2Fbar", ["foo%2Fbar"]], ["/" , nil], From 540b171e25ea6bc27ac6ce66530cbcd7a36e6886 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Sun, 17 Jun 2012 22:23:26 +1000 Subject: [PATCH 07/16] test rewritten, uses DSL as described in https://github.com/sinatra/sinatra/pull/492#issuecomment-5069108 --- test/compile_test.rb | 194 +++++++++++++++++++------------------------ 1 file changed, 85 insertions(+), 109 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index 15592d6b..bbadfc9a 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -2,117 +2,93 @@ require File.expand_path('../helper', __FILE__) class CompileTest < Test::Unit::TestCase - # Pattern, Current Regexp, [Examples, Should Bes] - # - [ - ["/", %r{\A/\z}, [ - ["/", []] - ]], - ["/foo", %r{\A/foo\z}, [ - ["/foo", []] - ]], - ["/:foo", %r{\A/([^/?#]+)\z}, [ - ["/foo" , ["foo"]], - ["/foo?" , nil], - ["/foo.bar" , ["foo.bar"]], - ["/foo/bar" , nil], - ["/foo%2Fbar", ["foo%2Fbar"]], - ["/" , nil], - ["/foo/" , nil] - ]], - ["/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z}, [ - ["/f%C3%B6%C3%B6", []] - ]], - ["/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}, [ - ["/foo/bar", ["foo", "bar"]] - ]], - ["/hello/:person", %r{\A/hello/([^/?#]+)\z}, [ - ["/hello/Frank", ["Frank"]] - ]], - ["/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z}, [ - ["/hello/world", ["hello", "world"]], - ["/hello" , ["hello", nil]], - ["/" , [nil, nil]], - ["" , [nil, nil]] - ]], - ["/*", %r{\A/(.*?)\z}, [ - ["/" , [""]], - ["/foo" , ["foo"]], - ["/" , [""]], - ["/foo/bar", ["foo/bar"]] - ]], - ["/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}, [ - ["/foo/bar/baz", ["foo", "bar/baz"]] - ]], - ["/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}, [ - ["/user@example.com/name", ["user@example.com", "name"]] - ]], - ["/test$/", %r{\A/test(?:\$|%24)/\z}, [ - ["/test$/", []] - ]], - ["/te+st/", %r{\A/te(?:\+|%2B)st/\z}, [ - ["/te+st/", []], - ["/test/", nil], - ["/teeest/", nil] - ]], - ["/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}, [ - ["/test(bar)/", []] - ]], - ["/path with spaces", %r{\A/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces\z}, [ - ["/path%20with%20spaces", []], - ["/path%2Bwith%2Bspaces", []], - ["/path+with+spaces", []] - ]], - ["/foo&bar", %r{\A/foo(?:&|%26)bar\z}, [ - ["/foo&bar", []] - ]], - ["/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}, [ - ["/hello%20world/how%20are%20you", ["hello%20world", "how%20are%20you"]] - ]], - ["/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z}, [ - ["/bar/foo/bling/baz/boom", ["bar", "bling", "baz/boom"]], - ["/bar/foo/baz", nil], - ]], - ["/test.bar", %r{\A/test(?:\.|%2E)bar\z}, [ - ["/test.bar", []], - ["/test0bar", nil] - ]], - ["/:file.:ext", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)\z}, [ - ["/pony.jpg", ["pony", "jpg"]], - ["/pony%2Ejpg", ["pony", "jpg"]], - ["/.jpg", nil] - ]], - ["/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z}, [ - ["/foo", ["foo", nil]], - ["/.bar", nil], - ["/foo.bar", ["foo", "bar"]], - ["/foo%2Ebar", ["foo", "bar"]] - ]], - ["/:user@?:host?", %r{\A/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?\z}, [ - ["/foo@bar", ["foo", "bar"]], - ["/foo.foo@bar", ["foo.foo", "bar"]], - ["/foo@bar.bar", ["foo", "bar.bar"]] - ]], - # From https://gist.github.com/2154980#gistcomment-169469. - # - # ["/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z}, [ - # ["/foo", ["foo"]], - # ["/foo.bar", ["foo", "bar"]] - # ]] - ].each do |pattern, regexp, examples_expectations| - app = nil - examples_expectations.each do |example, expected| - it "generates #{regexp.source} from #{pattern}, with #{example} succeeding" do - app ||= mock_app {} - compiled, keys = app.send(:compile, pattern) - match = compiled.match(example) - match ? assert_equal(expected, match.captures.to_a) : assert_equal(expected, match) - end - end - it "generates #{regexp.source} from #{pattern}" do + + def self.parses pattern, example, expected_params + it "parses #{example} with #{pattern} into params #{expected_params}" do app ||= mock_app {} compiled, keys = app.send(:compile, pattern) - assert_equal regexp, compiled + match = compiled.match(example) + fail %Q{"#{example}" does not parse on pattern "#{pattern}".} unless match + params = Hash[keys.zip(match.captures)] + assert_equal(expected_params, params) end end + def self.fails pattern, example + it "does not parse #{example} with #{pattern}" do + app ||= mock_app {} + compiled, keys = app.send(:compile, pattern) + match = compiled.match(example) + fail unless match.nil? || match.captures.empty? + end + end + + parses "/", "/", {} + parses "/foo", "/foo", {} + + parses "/:foo", "/foo", "foo" => "foo" + parses "/:foo", "/foo.bar", "foo" => "foo.bar" + parses "/:foo", "/foo%2Fbar", "foo" => "foo%2Fbar" + fails "/:foo", "/foo?" + fails "/:foo", "/foo/bar" + fails "/:foo", "/" + fails "/:foo", "/foo/" + + fails "/f\u00F6\u00F6", "/f%C3%B6%C3%B6" + + parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar" + + parses "/hello/:person", "/hello/Frank", "person" => "Frank" + + parses "/?:foo?/?:bar?", "/hello/world", "foo" => "hello", "bar" => "world" + parses "/?:foo?/?:bar?", "/hello", "foo" => "hello", "bar" => nil + parses "/?:foo?/?:bar?", "/", "foo" => nil, "bar" => nil + parses "/?:foo?/?:bar?", "", "foo" => nil, "bar" => nil + + parses "/*", "/", "splat" => "" + parses "/*", "/foo", "splat" => "foo" + parses "/*", "/foo/bar", "splat" => "foo/bar" + + parses "/:foo/*", "/foo/bar/baz", "foo" => "foo", "splat" => "bar/baz" + + parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name" + + fails "/test$/", "/test$/" + + parses "/te+st/", "/te+st/", {} + fails "/te+st/", "/test/" + fails "/te+st/", "/teeest/" + + parses "/test(bar)/", "/test(bar)/", {} + + parses "/path with spaces", "/path%20with%20spaces", {} + parses "/path with spaces", "/path%2Bwith%2Bspaces", {} + parses "/path with spaces", "/path+with+spaces", {} + + parses "/foo&bar", "/foo&bar", {} + + parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you" + + parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => "baz/boom" # TODO + fails "/*/foo/*/*", "/bar/foo/baz" + + parses "/test.bar", "/test.bar", {} + fails "/test.bar", "/test0bar" + + parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg" + parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg" + fails "/:file.:ext", "/.jpg" + + parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil + parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar" + parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar" + fails "/:name.?:format?", "/.bar" + + parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar" + parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar" + parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar" + + # From https://gist.github.com/2154980#gistcomment-169469. + # + # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil + # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar" end From be59b2b7917709283583dbef4f551f2daf2f4c5f Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 14:56:22 +1000 Subject: [PATCH 08/16] also test if the correct regexp is generated, refactored test methods --- test/compile_test.rb | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index bbadfc9a..1a4653e3 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -3,10 +3,15 @@ require File.expand_path('../helper', __FILE__) class CompileTest < Test::Unit::TestCase + def self.converts pattern, expected_regexp + it "generates #{expected_regexp.source} from #{pattern}" do + compiled, _ = compiled pattern + assert_equal expected_regexp, compiled + end + end def self.parses pattern, example, expected_params it "parses #{example} with #{pattern} into params #{expected_params}" do - app ||= mock_app {} - compiled, keys = app.send(:compile, pattern) + compiled, keys = compiled pattern match = compiled.match(example) fail %Q{"#{example}" does not parse on pattern "#{pattern}".} unless match params = Hash[keys.zip(match.captures)] @@ -15,16 +20,24 @@ class CompileTest < Test::Unit::TestCase end def self.fails pattern, example it "does not parse #{example} with #{pattern}" do - app ||= mock_app {} - compiled, keys = app.send(:compile, pattern) + compiled, _ = compiled pattern match = compiled.match(example) fail unless match.nil? || match.captures.empty? end end + def compiled pattern + app ||= mock_app {} + compiled, keys = app.send(:compile, pattern) + [compiled, keys] + end + converts "/", %r{\A/\z} parses "/", "/", {} + + converts "/foo", %r{\A/foo\z} parses "/foo", "/foo", {} + converts "/:foo", %r{\A/([^/?#]+)\z} parses "/:foo", "/foo", "foo" => "foo" parses "/:foo", "/foo.bar", "foo" => "foo.bar" parses "/:foo", "/foo%2Fbar", "foo" => "foo%2Fbar" @@ -33,62 +46,81 @@ class CompileTest < Test::Unit::TestCase fails "/:foo", "/" fails "/:foo", "/foo/" + converts "/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z} fails "/f\u00F6\u00F6", "/f%C3%B6%C3%B6" + converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar" + converts "/hello/:person", %r{\A/hello/([^/?#]+)\z} parses "/hello/:person", "/hello/Frank", "person" => "Frank" + converts "/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z} parses "/?:foo?/?:bar?", "/hello/world", "foo" => "hello", "bar" => "world" parses "/?:foo?/?:bar?", "/hello", "foo" => "hello", "bar" => nil parses "/?:foo?/?:bar?", "/", "foo" => nil, "bar" => nil parses "/?:foo?/?:bar?", "", "foo" => nil, "bar" => nil + converts "/*", %r{\A/(.*?)\z} parses "/*", "/", "splat" => "" parses "/*", "/foo", "splat" => "foo" parses "/*", "/foo/bar", "splat" => "foo/bar" + converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z} parses "/:foo/*", "/foo/bar/baz", "foo" => "foo", "splat" => "bar/baz" + converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name" + converts "/test$/", %r{\A/test(?:\$|%24)/\z} fails "/test$/", "/test$/" + converts "/te+st/", %r{\A/te(?:\+|%2B)st/\z} parses "/te+st/", "/te+st/", {} fails "/te+st/", "/test/" fails "/te+st/", "/teeest/" + converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z} parses "/test(bar)/", "/test(bar)/", {} + converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces\z} parses "/path with spaces", "/path%20with%20spaces", {} parses "/path with spaces", "/path%2Bwith%2Bspaces", {} parses "/path with spaces", "/path+with+spaces", {} + converts "/foo&bar", %r{\A/foo(?:&|%26)bar\z} parses "/foo&bar", "/foo&bar", {} + converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z} parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you" + converts "/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z} parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => "baz/boom" # TODO fails "/*/foo/*/*", "/bar/foo/baz" + converts "/test.bar", %r{\A/test(?:\.|%2E)bar\z} parses "/test.bar", "/test.bar", {} fails "/test.bar", "/test0bar" + converts "/:file.:ext", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)\z} parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg" parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg" fails "/:file.:ext", "/.jpg" + converts "/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z} parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar" parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar" fails "/:name.?:format?", "/.bar" + converts "/:user@?:host?", %r{\A/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?\z} parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar" parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar" parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar" # From https://gist.github.com/2154980#gistcomment-169469. # + # converts "/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z} # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar" end From 6462a00d75dfc94852ab44dc33d31f88bac3cc6e Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 15:04:54 +1000 Subject: [PATCH 09/16] . --- test/compile_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index 1a4653e3..aa6a7874 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -6,7 +6,7 @@ class CompileTest < Test::Unit::TestCase def self.converts pattern, expected_regexp it "generates #{expected_regexp.source} from #{pattern}" do compiled, _ = compiled pattern - assert_equal expected_regexp, compiled + assert_equal expected_regexp.source, compiled.source end end def self.parses pattern, example, expected_params From 9f0849986b8002f7e5544f20e00025586dffe5c4 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 15:10:40 +1000 Subject: [PATCH 10/16] marked test that fails in 1.8 --- test/compile_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index aa6a7874..d2042f3b 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -6,7 +6,7 @@ class CompileTest < Test::Unit::TestCase def self.converts pattern, expected_regexp it "generates #{expected_regexp.source} from #{pattern}" do compiled, _ = compiled pattern - assert_equal expected_regexp.source, compiled.source + assert_equal expected_regexp, compiled end end def self.parses pattern, example, expected_params @@ -46,8 +46,8 @@ class CompileTest < Test::Unit::TestCase fails "/:foo", "/" fails "/:foo", "/foo/" - converts "/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z} - fails "/f\u00F6\u00F6", "/f%C3%B6%C3%B6" + # converts "/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z} # TODO Fails in Ruby 1.8 + fails "/f\u00F6\u00F6", "/f%C3%B6%C3%B6" converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar" From e3d9e012ad5eeba573f87c4d74307140e243d7f1 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 15:29:10 +1000 Subject: [PATCH 11/16] fixed test method, fixed test examples that should parse --- test/compile_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index d2042f3b..45e7e0d0 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -22,7 +22,7 @@ class CompileTest < Test::Unit::TestCase it "does not parse #{example} with #{pattern}" do compiled, _ = compiled pattern match = compiled.match(example) - fail unless match.nil? || match.captures.empty? + fail %Q{"#{pattern}" does parse "#{example}" but it should fail} unless match.nil? end end def compiled pattern @@ -32,7 +32,7 @@ class CompileTest < Test::Unit::TestCase end converts "/", %r{\A/\z} - parses "/", "/", {} + parses "/", "/", {} converts "/foo", %r{\A/foo\z} parses "/foo", "/foo", {} @@ -47,7 +47,7 @@ class CompileTest < Test::Unit::TestCase fails "/:foo", "/foo/" # converts "/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z} # TODO Fails in Ruby 1.8 - fails "/f\u00F6\u00F6", "/f%C3%B6%C3%B6" + parses "/f\u00F6\u00F6", "/f%C3%B6%C3%B6", {} converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar" @@ -73,7 +73,7 @@ class CompileTest < Test::Unit::TestCase parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name" converts "/test$/", %r{\A/test(?:\$|%24)/\z} - fails "/test$/", "/test$/" + parses "/test$/", "/test$/", {} converts "/te+st/", %r{\A/te(?:\+|%2B)st/\z} parses "/te+st/", "/te+st/", {} From de212605311b6332751246e71e6217e3e0194ae3 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 15:31:03 +1000 Subject: [PATCH 12/16] unless nil? -> if --- test/compile_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index 45e7e0d0..07045931 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -22,7 +22,7 @@ class CompileTest < Test::Unit::TestCase it "does not parse #{example} with #{pattern}" do compiled, _ = compiled pattern match = compiled.match(example) - fail %Q{"#{pattern}" does parse "#{example}" but it should fail} unless match.nil? + fail %Q{"#{pattern}" does parse "#{example}" but it should fail} if match end end def compiled pattern From 98ef21b4ccd788c9d429312318ac6506e9c00d0a Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 16:43:14 +1000 Subject: [PATCH 13/16] =?UTF-8?q?"splat"=20param=20now=20contains=20all=20?= =?UTF-8?q?matches=20in=20an=20array,=20f=C3=B6=C3=B6=20test=20now=20confo?= =?UTF-8?q?rms=20to=20the=20original=20one?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/compile_test.rb | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index 07045931..55085268 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -14,7 +14,19 @@ class CompileTest < Test::Unit::TestCase compiled, keys = compiled pattern match = compiled.match(example) fail %Q{"#{example}" does not parse on pattern "#{pattern}".} unless match - params = Hash[keys.zip(match.captures)] + + # Aggregate e.g. multiple splat values into one array. + # + params = keys.zip(match.captures).reduce({}) do |hash, mapping| + key, value = mapping + hash[key] = if existing = hash[key] + existing.respond_to?(:each) ? existing << value : [existing, value] + else + value + end + hash + end + assert_equal(expected_params, params) end end @@ -46,8 +58,8 @@ class CompileTest < Test::Unit::TestCase fails "/:foo", "/" fails "/:foo", "/foo/" - # converts "/f\u00F6\u00F6", %r{\A/f%C3%B6%C3%B6\z} # TODO Fails in Ruby 1.8 - parses "/f\u00F6\u00F6", "/f%C3%B6%C3%B6", {} + converts "/föö", %r{\A/f%C3%B6%C3%B6\z} + parses "/föö", "/f%C3%B6%C3%B6", {} converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z} parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar" @@ -95,7 +107,7 @@ class CompileTest < Test::Unit::TestCase parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you" converts "/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z} - parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => "baz/boom" # TODO + parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"] fails "/*/foo/*/*", "/bar/foo/baz" converts "/test.bar", %r{\A/test(?:\.|%2E)bar\z} From 57fc08d3dd6565214afdc909fea309c26ae79984 Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 16:51:25 +1000 Subject: [PATCH 14/16] use a method that identifies an array also in 1.8.7 --- test/compile_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_test.rb b/test/compile_test.rb index 55085268..1c13499c 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -20,7 +20,7 @@ class CompileTest < Test::Unit::TestCase params = keys.zip(match.captures).reduce({}) do |hash, mapping| key, value = mapping hash[key] = if existing = hash[key] - existing.respond_to?(:each) ? existing << value : [existing, value] + existing.respond_to?(:to_ary) ? existing << value : [existing, value] else value end From 57babc51fc6f2ffdd3b23a57e4ad77c3d53d8f1c Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Mon, 18 Jun 2012 17:05:42 +1000 Subject: [PATCH 15/16] at least it fails already --- test/compile_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/compile_test.rb b/test/compile_test.rb index 1c13499c..5c72d82d 100644 --- a/test/compile_test.rb +++ b/test/compile_test.rb @@ -135,4 +135,5 @@ class CompileTest < Test::Unit::TestCase # converts "/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z} # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar" + fails "/:name(.:format)?", "/foo." end From e59b62e3151d64b4f034d2239168948eec3082ab Mon Sep 17 00:00:00 2001 From: Florian Hanke Date: Thu, 12 Jul 2012 18:27:35 +1000 Subject: [PATCH 16/16] remove accidental indentation --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 94ebb1fe..bc237f3b 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"