sinatra 1.4.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -47
  3. data/CONTRIBUTING.md +1 -1
  4. data/Gemfile +41 -49
  5. data/LICENSE +4 -1
  6. data/MAINTENANCE.md +42 -0
  7. data/README.de.md +644 -436
  8. data/README.es.md +6 -6
  9. data/README.fr.md +9 -9
  10. data/README.hu.md +37 -3
  11. data/README.ja.md +103 -45
  12. data/README.ko.md +8 -8
  13. data/README.md +471 -363
  14. data/README.pt-br.md +3 -3
  15. data/README.pt-pt.md +2 -2
  16. data/README.ru.md +42 -64
  17. data/README.zh.md +8 -8
  18. data/Rakefile +72 -49
  19. data/SECURITY.md +35 -0
  20. data/lib/sinatra/base.rb +137 -195
  21. data/lib/sinatra/indifferent_hash.rb +150 -0
  22. data/lib/sinatra/main.rb +1 -0
  23. data/lib/sinatra/show_exceptions.rb +63 -55
  24. data/lib/sinatra/version.rb +1 -1
  25. data/sinatra.gemspec +19 -7
  26. metadata +30 -164
  27. data/lib/sinatra/ext.rb +0 -17
  28. data/test/asciidoctor_test.rb +0 -72
  29. data/test/base_test.rb +0 -167
  30. data/test/builder_test.rb +0 -91
  31. data/test/coffee_test.rb +0 -96
  32. data/test/compile_test.rb +0 -183
  33. data/test/contest.rb +0 -91
  34. data/test/creole_test.rb +0 -65
  35. data/test/delegator_test.rb +0 -160
  36. data/test/encoding_test.rb +0 -20
  37. data/test/erb_test.rb +0 -116
  38. data/test/extensions_test.rb +0 -98
  39. data/test/filter_test.rb +0 -487
  40. data/test/haml_test.rb +0 -109
  41. data/test/helper.rb +0 -132
  42. data/test/helpers_test.rb +0 -1917
  43. data/test/integration/app.rb +0 -79
  44. data/test/integration_helper.rb +0 -236
  45. data/test/integration_test.rb +0 -104
  46. data/test/less_test.rb +0 -69
  47. data/test/liquid_test.rb +0 -77
  48. data/test/mapped_error_test.rb +0 -285
  49. data/test/markaby_test.rb +0 -80
  50. data/test/markdown_test.rb +0 -85
  51. data/test/mediawiki_test.rb +0 -68
  52. data/test/middleware_test.rb +0 -68
  53. data/test/nokogiri_test.rb +0 -67
  54. data/test/public/favicon.ico +0 -0
  55. data/test/public/hello+world.txt +0 -1
  56. data/test/rabl_test.rb +0 -89
  57. data/test/rack_test.rb +0 -45
  58. data/test/radius_test.rb +0 -59
  59. data/test/rdoc_test.rb +0 -66
  60. data/test/readme_test.rb +0 -130
  61. data/test/request_test.rb +0 -100
  62. data/test/response_test.rb +0 -63
  63. data/test/result_test.rb +0 -76
  64. data/test/route_added_hook_test.rb +0 -59
  65. data/test/routing_test.rb +0 -1456
  66. data/test/sass_test.rb +0 -115
  67. data/test/scss_test.rb +0 -88
  68. data/test/server_test.rb +0 -56
  69. data/test/settings_test.rb +0 -582
  70. data/test/sinatra_test.rb +0 -12
  71. data/test/slim_test.rb +0 -102
  72. data/test/static_test.rb +0 -266
  73. data/test/streaming_test.rb +0 -149
  74. data/test/stylus_test.rb +0 -90
  75. data/test/templates_test.rb +0 -382
  76. data/test/textile_test.rb +0 -65
  77. data/test/views/a/in_a.str +0 -1
  78. data/test/views/ascii.erb +0 -2
  79. data/test/views/b/in_b.str +0 -1
  80. data/test/views/calc.html.erb +0 -1
  81. data/test/views/error.builder +0 -3
  82. data/test/views/error.erb +0 -3
  83. data/test/views/error.haml +0 -3
  84. data/test/views/error.sass +0 -2
  85. data/test/views/explicitly_nested.str +0 -1
  86. data/test/views/foo/hello.test +0 -1
  87. data/test/views/hello.asciidoc +0 -1
  88. data/test/views/hello.builder +0 -1
  89. data/test/views/hello.coffee +0 -1
  90. data/test/views/hello.creole +0 -1
  91. data/test/views/hello.erb +0 -1
  92. data/test/views/hello.haml +0 -1
  93. data/test/views/hello.less +0 -5
  94. data/test/views/hello.liquid +0 -1
  95. data/test/views/hello.mab +0 -1
  96. data/test/views/hello.md +0 -1
  97. data/test/views/hello.mediawiki +0 -1
  98. data/test/views/hello.nokogiri +0 -1
  99. data/test/views/hello.rabl +0 -2
  100. data/test/views/hello.radius +0 -1
  101. data/test/views/hello.rdoc +0 -1
  102. data/test/views/hello.sass +0 -2
  103. data/test/views/hello.scss +0 -3
  104. data/test/views/hello.slim +0 -1
  105. data/test/views/hello.str +0 -1
  106. data/test/views/hello.styl +0 -2
  107. data/test/views/hello.test +0 -1
  108. data/test/views/hello.textile +0 -1
  109. data/test/views/hello.wlang +0 -1
  110. data/test/views/hello.yajl +0 -1
  111. data/test/views/layout2.builder +0 -3
  112. data/test/views/layout2.erb +0 -2
  113. data/test/views/layout2.haml +0 -2
  114. data/test/views/layout2.liquid +0 -2
  115. data/test/views/layout2.mab +0 -2
  116. data/test/views/layout2.nokogiri +0 -3
  117. data/test/views/layout2.rabl +0 -3
  118. data/test/views/layout2.radius +0 -2
  119. data/test/views/layout2.slim +0 -3
  120. data/test/views/layout2.str +0 -2
  121. data/test/views/layout2.test +0 -1
  122. data/test/views/layout2.wlang +0 -2
  123. data/test/views/nested.str +0 -1
  124. data/test/views/utf8.erb +0 -2
  125. data/test/wlang_test.rb +0 -87
  126. data/test/yajl_test.rb +0 -86
@@ -1,72 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
-
3
- begin
4
- require 'asciidoctor'
5
-
6
- class AsciidoctorTest < Minitest::Test
7
- def asciidoc_app(&block)
8
- mock_app do
9
- set :views, File.dirname(__FILE__) + '/views'
10
- get('/', &block)
11
- end
12
- get '/'
13
- end
14
-
15
- it 'renders inline AsciiDoc strings' do
16
- asciidoc_app { asciidoc '== Hiya' }
17
- assert ok?
18
- assert_match %r{<h2.*?>Hiya</h2>}, body
19
- end
20
-
21
- it 'uses the correct engine' do
22
- engine = Tilt::AsciidoctorTemplate
23
- assert_equal engine, Tilt[:ad]
24
- assert_equal engine, Tilt[:adoc]
25
- assert_equal engine, Tilt[:asciidoc]
26
- end
27
-
28
- it 'renders .asciidoc files in views path' do
29
- asciidoc_app { asciidoc :hello }
30
- assert ok?
31
- assert_match %r{<h2.*?>Hello from AsciiDoc</h2>}, body
32
- end
33
-
34
- it 'raises error if template not found' do
35
- mock_app { get('/') { asciidoc :no_such_template } }
36
- assert_raises(Errno::ENOENT) { get('/') }
37
- end
38
-
39
- it 'renders with inline layouts' do
40
- mock_app do
41
- layout { 'THIS. IS. #{yield.upcase}!' }
42
- get('/') { asciidoc 'Sparta', :layout_engine => :str }
43
- end
44
- get '/'
45
- assert ok?
46
- assert_include body, 'THIS. IS.'
47
- assert_include body, '<P>SPARTA</P>'
48
- end
49
-
50
- it 'renders with file layouts' do
51
- asciidoc_app do
52
- asciidoc 'Hello World', :layout => :layout2, :layout_engine => :erb
53
- end
54
- assert ok?
55
- assert_include body, 'ERB Layout!'
56
- assert_include body, '<p>Hello World</p>'
57
- end
58
-
59
- it 'can be used in a nested fashion for partials and whatnot' do
60
- mock_app do
61
- template(:inner) { 'hi' }
62
- template(:outer) { '<outer><%= asciidoc :inner %></outer>' }
63
- get('/') { erb :outer }
64
- end
65
- get '/'
66
- assert ok?
67
- assert_match %r{<outer>.*<p.*?>hi</p>.*</outer>}m, body
68
- end
69
- end
70
- rescue LoadError
71
- warn "#{$!.to_s}: skipping asciidoc tests"
72
- end
data/test/base_test.rb DELETED
@@ -1,167 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
-
3
- class BaseTest < Minitest::Test
4
- describe 'Sinatra::Base subclasses' do
5
- class TestApp < Sinatra::Base
6
- get('/') { 'Hello World' }
7
- end
8
-
9
- it 'include Rack::Utils' do
10
- assert TestApp.included_modules.include?(Rack::Utils)
11
- end
12
-
13
- it 'processes requests with #call' do
14
- assert TestApp.respond_to?(:call)
15
-
16
- request = Rack::MockRequest.new(TestApp)
17
- response = request.get('/')
18
- assert response.ok?
19
- assert_equal 'Hello World', response.body
20
- end
21
-
22
- class TestApp < Sinatra::Base
23
- get '/state' do
24
- @foo ||= "new"
25
- body = "Foo: #{@foo}"
26
- @foo = 'discard'
27
- body
28
- end
29
- end
30
-
31
- it 'does not maintain state between requests' do
32
- request = Rack::MockRequest.new(TestApp)
33
- 2.times do
34
- response = request.get('/state')
35
- assert response.ok?
36
- assert_equal 'Foo: new', response.body
37
- end
38
- end
39
-
40
- it "passes the subclass to configure blocks" do
41
- ref = nil
42
- TestApp.configure { |app| ref = app }
43
- assert_equal TestApp, ref
44
- end
45
-
46
- it "allows the configure block arg to be omitted and does not change context" do
47
- context = nil
48
- TestApp.configure { context = self }
49
- assert_equal self, context
50
- end
51
- end
52
-
53
- describe "Sinatra::Base#new" do
54
- it 'returns a wrapper' do
55
- assert_equal Sinatra::Wrapper, Sinatra::Base.new.class
56
- end
57
-
58
- it 'implements a nice inspect' do
59
- assert_equal '#<Sinatra::Base app_file=nil>', Sinatra::Base.new.inspect
60
- end
61
-
62
- it 'exposes settings' do
63
- assert_equal Sinatra::Base.settings, Sinatra::Base.new.settings
64
- end
65
-
66
- it 'exposes helpers' do
67
- assert_equal 'image/jpeg', Sinatra::Base.new.helpers.mime_type(:jpg)
68
- end
69
- end
70
-
71
- describe "Sinatra::Base as Rack middleware" do
72
- app = lambda { |env|
73
- headers = {'X-Downstream' => 'true'}
74
- headers['X-Route-Missing'] = env['sinatra.route-missing'] || ''
75
- [210, headers, ['Hello from downstream']] }
76
-
77
- class TestMiddleware < Sinatra::Base
78
- end
79
-
80
- it 'creates a middleware that responds to #call with .new' do
81
- middleware = TestMiddleware.new(app)
82
- assert middleware.respond_to?(:call)
83
- end
84
-
85
- it 'exposes the downstream app' do
86
- middleware = TestMiddleware.new!(app)
87
- assert_same app, middleware.app
88
- end
89
-
90
- class TestMiddleware < Sinatra::Base
91
- def route_missing
92
- env['sinatra.route-missing'] = '1'
93
- super
94
- end
95
-
96
- get('/') { 'Hello from middleware' }
97
- end
98
-
99
- middleware = TestMiddleware.new(app)
100
- request = Rack::MockRequest.new(middleware)
101
-
102
- it 'intercepts requests' do
103
- response = request.get('/')
104
- assert response.ok?
105
- assert_equal 'Hello from middleware', response.body
106
- end
107
-
108
- it 'automatically forwards requests downstream when no matching route found' do
109
- response = request.get('/missing')
110
- assert_equal 210, response.status
111
- assert_equal 'Hello from downstream', response.body
112
- end
113
-
114
- it 'calls #route_missing before forwarding downstream' do
115
- response = request.get('/missing')
116
- assert_equal '1', response['X-Route-Missing']
117
- end
118
-
119
- class TestMiddleware < Sinatra::Base
120
- get('/low-level-forward') { app.call(env) }
121
- end
122
-
123
- it 'can call the downstream app directly and return result' do
124
- response = request.get('/low-level-forward')
125
- assert_equal 210, response.status
126
- assert_equal 'true', response['X-Downstream']
127
- assert_equal 'Hello from downstream', response.body
128
- end
129
-
130
- class TestMiddleware < Sinatra::Base
131
- get '/explicit-forward' do
132
- response['X-Middleware'] = 'true'
133
- res = forward
134
- assert_nil res
135
- assert_equal 210, response.status
136
- assert_equal 'true', response['X-Downstream']
137
- assert_equal ['Hello from downstream'], response.body
138
- 'Hello after explicit forward'
139
- end
140
- end
141
-
142
- it 'forwards the request downstream and integrates the response into the current context' do
143
- response = request.get('/explicit-forward')
144
- assert_equal 210, response.status
145
- assert_equal 'true', response['X-Downstream']
146
- assert_equal 'Hello after explicit forward', response.body
147
- assert_equal '28', response['Content-Length']
148
- end
149
-
150
- app_content_length = lambda {|env|
151
- [200, {'Content-Length' => '16'}, 'From downstream!']}
152
-
153
- class TestMiddlewareContentLength < Sinatra::Base
154
- get '/forward' do
155
- 'From after explicit forward!'
156
- end
157
- end
158
-
159
- middleware_content_length = TestMiddlewareContentLength.new(app_content_length)
160
- request_content_length = Rack::MockRequest.new(middleware_content_length)
161
-
162
- it "sets content length for last response" do
163
- response = request_content_length.get('/forward')
164
- assert_equal '28', response['Content-Length']
165
- end
166
- end
167
- end
data/test/builder_test.rb DELETED
@@ -1,91 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
-
3
- begin
4
- require 'builder'
5
-
6
- class BuilderTest < Minitest::Test
7
- def builder_app(options = {}, &block)
8
- mock_app do
9
- set :views, File.dirname(__FILE__) + '/views'
10
- set options
11
- get('/', &block)
12
- end
13
- get '/'
14
- end
15
-
16
- it 'renders inline Builder strings' do
17
- builder_app { builder 'xml.instruct!' }
18
- assert ok?
19
- assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body
20
- end
21
-
22
- it 'defaults content type to xml' do
23
- builder_app { builder 'xml.instruct!' }
24
- assert ok?
25
- assert_equal "application/xml;charset=utf-8", response['Content-Type']
26
- end
27
-
28
- it 'defaults allows setting content type per route' do
29
- builder_app do
30
- content_type :html
31
- builder 'xml.instruct!'
32
- end
33
- assert ok?
34
- assert_equal "text/html;charset=utf-8", response['Content-Type']
35
- end
36
-
37
- it 'defaults allows setting content type globally' do
38
- builder_app(:builder => { :content_type => 'html' }) do
39
- builder 'xml.instruct!'
40
- end
41
- assert ok?
42
- assert_equal "text/html;charset=utf-8", response['Content-Type']
43
- end
44
-
45
- it 'renders inline blocks' do
46
- builder_app do
47
- @name = "Frank & Mary"
48
- builder { |xml| xml.couple @name }
49
- end
50
- assert ok?
51
- assert_equal "<couple>Frank &amp; Mary</couple>\n", body
52
- end
53
-
54
- it 'renders .builder files in views path' do
55
- builder_app do
56
- @name = "Blue"
57
- builder :hello
58
- end
59
- assert ok?
60
- assert_equal %(<exclaim>You're my boy, Blue!</exclaim>\n), body
61
- end
62
-
63
- it "renders with inline layouts" do
64
- mock_app do
65
- layout { %(xml.layout { xml << yield }) }
66
- get('/') { builder %(xml.em 'Hello World') }
67
- end
68
- get '/'
69
- assert ok?
70
- assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
71
- end
72
-
73
- it "renders with file layouts" do
74
- builder_app do
75
- builder %(xml.em 'Hello World'), :layout => :layout2
76
- end
77
- assert ok?
78
- assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
79
- end
80
-
81
- it "raises error if template not found" do
82
- mock_app do
83
- get('/') { builder :no_such_template }
84
- end
85
- assert_raises(Errno::ENOENT) { get('/') }
86
- end
87
- end
88
-
89
- rescue LoadError
90
- warn "#{$!.to_s}: skipping builder tests"
91
- end
data/test/coffee_test.rb DELETED
@@ -1,96 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
-
3
- begin
4
- require 'coffee-script'
5
- require 'execjs'
6
-
7
- begin
8
- ExecJS.compile '1'
9
- rescue Exception
10
- raise LoadError, 'unable to execute JavaScript'
11
- end
12
-
13
- class CoffeeTest < Minitest::Test
14
- def coffee_app(options = {}, &block)
15
- mock_app do
16
- set :views, File.dirname(__FILE__) + '/views'
17
- set(options)
18
- get('/', &block)
19
- end
20
- get '/'
21
- end
22
-
23
- it 'renders inline Coffee strings' do
24
- coffee_app { coffee "alert 'Aye!'\n" }
25
- assert ok?
26
- assert body.include?("alert('Aye!');")
27
- end
28
-
29
- it 'defaults content type to javascript' do
30
- coffee_app { coffee "alert 'Aye!'\n" }
31
- assert ok?
32
- assert_equal "application/javascript;charset=utf-8", response['Content-Type']
33
- end
34
-
35
- it 'defaults allows setting content type per route' do
36
- coffee_app do
37
- content_type :html
38
- coffee "alert 'Aye!'\n"
39
- end
40
- assert ok?
41
- assert_equal "text/html;charset=utf-8", response['Content-Type']
42
- end
43
-
44
- it 'defaults allows setting content type globally' do
45
- coffee_app(:coffee => { :content_type => 'html' }) do
46
- coffee "alert 'Aye!'\n"
47
- end
48
- assert ok?
49
- assert_equal "text/html;charset=utf-8", response['Content-Type']
50
- end
51
-
52
- it 'renders .coffee files in views path' do
53
- coffee_app { coffee :hello }
54
- assert ok?
55
- assert_include body, "alert(\"Aye!\");"
56
- end
57
-
58
- it 'ignores the layout option' do
59
- coffee_app { coffee :hello, :layout => :layout2 }
60
- assert ok?
61
- assert_include body, "alert(\"Aye!\");"
62
- end
63
-
64
- it "raises error if template not found" do
65
- mock_app {
66
- get('/') { coffee :no_such_template }
67
- }
68
- assert_raises(Errno::ENOENT) { get('/') }
69
- end
70
-
71
- it "passes coffee options to the coffee engine" do
72
- coffee_app { coffee "alert 'Aye!'\n", :no_wrap => true }
73
- assert ok?
74
- assert_body "alert('Aye!');"
75
- end
76
-
77
- it "passes default coffee options to the coffee engine" do
78
- mock_app do
79
- set :coffee, :no_wrap => true # default coffee style is :nested
80
- get('/') { coffee "alert 'Aye!'\n" }
81
- end
82
- get '/'
83
- assert ok?
84
- assert_body "alert('Aye!');"
85
- end
86
- end
87
-
88
- rescue LoadError
89
- warn "#{$!.to_s}: skipping coffee tests"
90
- rescue
91
- if $!.class.name == 'ExecJS::RuntimeUnavailable'
92
- warn "#{$!.to_s}: skipping coffee tests"
93
- else
94
- raise
95
- end
96
- end
data/test/compile_test.rb DELETED
@@ -1,183 +0,0 @@
1
- # I like coding: UTF-8
2
- require File.expand_path('../helper', __FILE__)
3
-
4
- class CompileTest < Minitest::Test
5
-
6
- def self.converts pattern, expected_regexp
7
- it "generates #{expected_regexp.source} from #{pattern}" do
8
- compiled, _ = compiled pattern
9
- assert_equal expected_regexp, compiled, "Pattern #{pattern} is not compiled into #{expected_regexp.source}. Was #{compiled.source}."
10
- end
11
- end
12
- def self.parses pattern, example, expected_params
13
- it "parses #{example} with #{pattern} into params #{expected_params}" do
14
- compiled, keys = compiled pattern
15
- match = compiled.match(example)
16
- fail %Q{"#{example}" does not parse on pattern "#{pattern}" (compiled pattern is #{compiled.source}).} unless match
17
-
18
- # Aggregate e.g. multiple splat values into one array.
19
- #
20
- params = keys.zip(match.captures).reduce({}) do |hash, mapping|
21
- key, value = mapping
22
- hash[key] = if existing = hash[key]
23
- existing.respond_to?(:to_ary) ? existing << value : [existing, value]
24
- else
25
- value
26
- end
27
- hash
28
- end
29
-
30
- assert_equal expected_params, params, "Pattern #{pattern} does not match path #{example}."
31
- end
32
- end
33
- def self.fails pattern, example
34
- it "does not parse #{example} with #{pattern}" do
35
- compiled, _ = compiled pattern
36
- match = compiled.match(example)
37
- fail %Q{"#{pattern}" does parse "#{example}" but it should fail} if match
38
- end
39
- end
40
- def compiled pattern
41
- app ||= mock_app {}
42
- compiled, keys = app.send(:compile, pattern)
43
- [compiled, keys]
44
- end
45
-
46
- converts "/", %r{\A/\z}
47
- parses "/", "/", {}
48
-
49
- converts "/foo", %r{\A/foo\z}
50
- parses "/foo", "/foo", {}
51
-
52
- converts "/:foo", %r{\A/([^/?#]+)\z}
53
- parses "/:foo", "/foo", "foo" => "foo"
54
- parses "/:foo", "/foo.bar", "foo" => "foo.bar"
55
- parses "/:foo", "/foo%2Fbar", "foo" => "foo%2Fbar"
56
- parses "/:foo", "/%0Afoo", "foo" => "%0Afoo"
57
- fails "/:foo", "/foo?"
58
- fails "/:foo", "/foo/bar"
59
- fails "/:foo", "/"
60
- fails "/:foo", "/foo/"
61
-
62
- converts "/föö", %r{\A/f%[Cc]3%[Bb]6%[Cc]3%[Bb]6\z}
63
- parses "/föö", "/f%C3%B6%C3%B6", {}
64
-
65
- converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
66
- parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar"
67
-
68
- converts "/hello/:person", %r{\A/hello/([^/?#]+)\z}
69
- parses "/hello/:person", "/hello/Frank", "person" => "Frank"
70
-
71
- converts "/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z}
72
- parses "/?:foo?/?:bar?", "/hello/world", "foo" => "hello", "bar" => "world"
73
- parses "/?:foo?/?:bar?", "/hello", "foo" => "hello", "bar" => nil
74
- parses "/?:foo?/?:bar?", "/", "foo" => nil, "bar" => nil
75
- parses "/?:foo?/?:bar?", "", "foo" => nil, "bar" => nil
76
-
77
- converts "/*", %r{\A/(.*?)\z}
78
- parses "/*", "/", "splat" => ""
79
- parses "/*", "/foo", "splat" => "foo"
80
- parses "/*", "/foo/bar", "splat" => "foo/bar"
81
-
82
- converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
83
- parses "/:foo/*", "/foo/bar/baz", "foo" => "foo", "splat" => "bar/baz"
84
-
85
- converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
86
- parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name"
87
-
88
- converts "/test$/", %r{\A/test(?:\$|%24)/\z}
89
- parses "/test$/", "/test$/", {}
90
-
91
- converts "/te+st/", %r{\A/te(?:\+|%2[Bb])st/\z}
92
- parses "/te+st/", "/te+st/", {}
93
- fails "/te+st/", "/test/"
94
- fails "/te+st/", "/teeest/"
95
-
96
- converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}
97
- parses "/test(bar)/", "/test(bar)/", {}
98
-
99
- converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2[Bb]))with(?:%20|(?:\+|%2[Bb]))spaces\z}
100
- parses "/path with spaces", "/path%20with%20spaces", {}
101
- parses "/path with spaces", "/path%2Bwith%2Bspaces", {}
102
- parses "/path with spaces", "/path+with+spaces", {}
103
-
104
- converts "/foo&bar", %r{\A/foo(?:&|%26)bar\z}
105
- parses "/foo&bar", "/foo&bar", {}
106
-
107
- converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
108
- parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you"
109
-
110
- converts "/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z}
111
- parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"]
112
- fails "/*/foo/*/*", "/bar/foo/baz"
113
-
114
- converts "/test.bar", %r{\A/test(?:\.|%2[Ee])bar\z}
115
- parses "/test.bar", "/test.bar", {}
116
- fails "/test.bar", "/test0bar"
117
-
118
- converts "/:file.:ext", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])((?:[^/?#%]|(?:%[^2].|%[2][^Ee]))+)\z}
119
- parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg"
120
- parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg"
121
- fails "/:file.:ext", "/.jpg"
122
-
123
- converts "/:name.?:format?", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])?((?:[^/?#%]|(?:%[^2].|%[2][^Ee]))+)?\z}
124
- parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil
125
- parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar"
126
- parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar"
127
- parses "/:name?.?:format", "/.bar", "name" => nil, "format" => "bar"
128
- parses "/:name?.?:format?", "/.bar", "name" => nil, "format" => "bar"
129
- parses "/:name?.:format?", "/.bar", "name" => nil, "format" => "bar"
130
- fails "/:name.:format", "/.bar"
131
- fails "/:name.?:format?", "/.bar"
132
-
133
- converts "/:user@?:host?", %r{\A/((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)(?:@|%40)?((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)?\z}
134
- parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar"
135
- parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar"
136
- parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar"
137
-
138
- # From https://gist.github.com/2154980#gistcomment-169469.
139
- #
140
- # converts "/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z}
141
- # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil
142
- # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar"
143
- fails "/:name(.:format)?", "/foo."
144
-
145
- parses "/:id/test.bar", "/3/test.bar", {"id" => "3"}
146
- parses "/:id/test.bar", "/2/test.bar", {"id" => "2"}
147
- parses "/:id/test.bar", "/2E/test.bar", {"id" => "2E"}
148
- parses "/:id/test.bar", "/2e/test.bar", {"id" => "2e"}
149
- parses "/:id/test.bar", "/%2E/test.bar", {"id" => "%2E"}
150
-
151
- parses '/10/:id', '/10/test', "id" => "test"
152
- parses '/10/:id', '/10/te.st', "id" => "te.st"
153
-
154
- parses '/10.1/:id', '/10.1/test', "id" => "test"
155
- parses '/10.1/:id', '/10.1/te.st', "id" => "te.st"
156
- parses '/:foo/:id', '/10.1/te.st', "foo" => "10.1", "id" => "te.st"
157
- parses '/:foo/:id', '/10.1.2/te.st', "foo" => "10.1.2", "id" => "te.st"
158
- parses '/:foo.:bar/:id', '/10.1/te.st', "foo" => "10", "bar" => "1", "id" => "te.st"
159
- fails '/:foo.:bar/:id', '/10.1.2/te.st' # We don't do crazy.
160
-
161
- parses '/:a/:b.?:c?', '/a/b', "a" => "a", "b" => "b", "c" => nil
162
- parses '/:a/:b.?:c?', '/a/b.c', "a" => "a", "b" => "b", "c" => "c"
163
- parses '/:a/:b.?:c?', '/a.b/c', "a" => "a.b", "b" => "c", "c" => nil
164
- parses '/:a/:b.?:c?', '/a.b/c.d', "a" => "a.b", "b" => "c", "c" => "d"
165
- fails '/:a/:b.?:c?', '/a.b/c.d/e'
166
-
167
- parses "/:file.:ext", "/pony%2ejpg", "file" => "pony", "ext" => "jpg"
168
- parses "/:file.:ext", "/pony%E6%AD%A3%2Ejpg", "file" => "pony%E6%AD%A3", "ext" => "jpg"
169
- parses "/:file.:ext", "/pony%e6%ad%a3%2ejpg", "file" => "pony%e6%ad%a3", "ext" => "jpg"
170
- parses "/:file.:ext", "/pony正%2Ejpg", "file" => "pony正", "ext" => "jpg"
171
- parses "/:file.:ext", "/pony正%2ejpg", "file" => "pony正", "ext" => "jpg"
172
- parses "/:file.:ext", "/pony正..jpg", "file" => "pony正", "ext" => ".jpg"
173
- fails "/:file.:ext", "/pony正.%2ejpg"
174
-
175
- converts "/:name.:format", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])((?:[^/?#%]|(?:%[^2].|%[2][^Ee]))+)\z}
176
- parses "/:name.:format", "/file.tar.gz", "name" => "file", "format" => "tar.gz"
177
- parses "/:name.:format1.:format2", "/file.tar.gz", "name" => "file", "format1" => "tar", "format2" => "gz"
178
- parses "/:name.:format1.:format2", "/file.temp.tar.gz", "name" => "file", "format1" => "temp", "format2" => "tar.gz"
179
-
180
- # From issue #688.
181
- #
182
- parses "/articles/10.1103/:doi", "/articles/10.1103/PhysRevLett.110.026401", "doi" => "PhysRevLett.110.026401"
183
- end