roda 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/README.rdoc +10 -0
  4. data/doc/release_notes/3.29.0.txt +15 -0
  5. data/lib/roda.rb +1 -0
  6. data/lib/roda/plugins/caching.rb +2 -0
  7. data/lib/roda/plugins/common_logger.rb +1 -1
  8. data/lib/roda/plugins/exception_page.rb +7 -1
  9. data/lib/roda/plugins/indifferent_params.rb +2 -0
  10. data/lib/roda/version.rb +1 -1
  11. metadata +4 -214
  12. data/Rakefile +0 -108
  13. data/doc/release_notes/1.0.0.txt +0 -329
  14. data/doc/release_notes/1.1.0.txt +0 -226
  15. data/doc/release_notes/1.2.0.txt +0 -406
  16. data/doc/release_notes/1.3.0.txt +0 -109
  17. data/doc/release_notes/2.0.0.txt +0 -75
  18. data/doc/release_notes/2.1.0.txt +0 -124
  19. data/doc/release_notes/2.10.0.txt +0 -27
  20. data/doc/release_notes/2.11.0.txt +0 -70
  21. data/doc/release_notes/2.12.0.txt +0 -40
  22. data/doc/release_notes/2.13.0.txt +0 -10
  23. data/doc/release_notes/2.14.0.txt +0 -44
  24. data/doc/release_notes/2.15.0.txt +0 -53
  25. data/doc/release_notes/2.16.0.txt +0 -48
  26. data/doc/release_notes/2.17.0.txt +0 -62
  27. data/doc/release_notes/2.18.0.txt +0 -69
  28. data/doc/release_notes/2.19.0.txt +0 -30
  29. data/doc/release_notes/2.2.0.txt +0 -97
  30. data/doc/release_notes/2.20.0.txt +0 -5
  31. data/doc/release_notes/2.21.0.txt +0 -17
  32. data/doc/release_notes/2.22.0.txt +0 -41
  33. data/doc/release_notes/2.23.0.txt +0 -29
  34. data/doc/release_notes/2.24.0.txt +0 -65
  35. data/doc/release_notes/2.25.0.txt +0 -14
  36. data/doc/release_notes/2.26.0.txt +0 -13
  37. data/doc/release_notes/2.27.0.txt +0 -56
  38. data/doc/release_notes/2.28.0.txt +0 -17
  39. data/doc/release_notes/2.29.0.txt +0 -156
  40. data/doc/release_notes/2.3.0.txt +0 -109
  41. data/doc/release_notes/2.4.0.txt +0 -55
  42. data/doc/release_notes/2.5.0.txt +0 -23
  43. data/doc/release_notes/2.5.1.txt +0 -4
  44. data/doc/release_notes/2.6.0.txt +0 -21
  45. data/doc/release_notes/2.7.0.txt +0 -75
  46. data/doc/release_notes/2.8.0.txt +0 -44
  47. data/doc/release_notes/2.9.0.txt +0 -6
  48. data/spec/all.rb +0 -1
  49. data/spec/assets/css/app.scss +0 -1
  50. data/spec/assets/css/no_access.css +0 -1
  51. data/spec/assets/css/raw.css +0 -1
  52. data/spec/assets/js/head/app.js +0 -1
  53. data/spec/composition_spec.rb +0 -31
  54. data/spec/define_roda_method_spec.rb +0 -274
  55. data/spec/env_spec.rb +0 -11
  56. data/spec/freeze_spec.rb +0 -37
  57. data/spec/integration_spec.rb +0 -209
  58. data/spec/matchers_spec.rb +0 -832
  59. data/spec/opts_spec.rb +0 -42
  60. data/spec/plugin/_after_hook_spec.rb +0 -19
  61. data/spec/plugin/all_verbs_spec.rb +0 -29
  62. data/spec/plugin/assets_preloading_spec.rb +0 -98
  63. data/spec/plugin/assets_spec.rb +0 -745
  64. data/spec/plugin/backtracking_array_spec.rb +0 -42
  65. data/spec/plugin/branch_locals_spec.rb +0 -106
  66. data/spec/plugin/caching_spec.rb +0 -337
  67. data/spec/plugin/chunked_spec.rb +0 -201
  68. data/spec/plugin/class_level_routing_spec.rb +0 -164
  69. data/spec/plugin/class_matchers_spec.rb +0 -40
  70. data/spec/plugin/common_logger_spec.rb +0 -85
  71. data/spec/plugin/content_for_spec.rb +0 -162
  72. data/spec/plugin/content_security_policy_spec.rb +0 -175
  73. data/spec/plugin/cookies_spec.rb +0 -51
  74. data/spec/plugin/csrf_spec.rb +0 -111
  75. data/spec/plugin/default_headers_spec.rb +0 -82
  76. data/spec/plugin/default_status_spec.rb +0 -95
  77. data/spec/plugin/delay_build_spec.rb +0 -23
  78. data/spec/plugin/delegate_spec.rb +0 -23
  79. data/spec/plugin/delete_empty_headers_spec.rb +0 -27
  80. data/spec/plugin/direct_call_spec.rb +0 -28
  81. data/spec/plugin/disallow_file_uploads_spec.rb +0 -25
  82. data/spec/plugin/drop_body_spec.rb +0 -24
  83. data/spec/plugin/early_hints_spec.rb +0 -19
  84. data/spec/plugin/empty_root_spec.rb +0 -14
  85. data/spec/plugin/environments_spec.rb +0 -42
  86. data/spec/plugin/error_email_spec.rb +0 -97
  87. data/spec/plugin/error_handler_spec.rb +0 -216
  88. data/spec/plugin/error_mail_spec.rb +0 -93
  89. data/spec/plugin/exception_page_spec.rb +0 -168
  90. data/spec/plugin/flash_spec.rb +0 -121
  91. data/spec/plugin/h_spec.rb +0 -11
  92. data/spec/plugin/halt_spec.rb +0 -119
  93. data/spec/plugin/hash_matcher_spec.rb +0 -27
  94. data/spec/plugin/hash_routes_spec.rb +0 -535
  95. data/spec/plugin/head_spec.rb +0 -52
  96. data/spec/plugin/header_matchers_spec.rb +0 -98
  97. data/spec/plugin/heartbeat_spec.rb +0 -74
  98. data/spec/plugin/hooks_spec.rb +0 -152
  99. data/spec/plugin/indifferent_params_spec.rb +0 -14
  100. data/spec/plugin/json_parser_spec.rb +0 -141
  101. data/spec/plugin/json_spec.rb +0 -83
  102. data/spec/plugin/mail_processor_spec.rb +0 -451
  103. data/spec/plugin/mailer_spec.rb +0 -282
  104. data/spec/plugin/match_affix_spec.rb +0 -43
  105. data/spec/plugin/match_hook_spec.rb +0 -79
  106. data/spec/plugin/middleware_spec.rb +0 -237
  107. data/spec/plugin/middleware_stack_spec.rb +0 -81
  108. data/spec/plugin/module_include_spec.rb +0 -48
  109. data/spec/plugin/multi_route_spec.rb +0 -268
  110. data/spec/plugin/multi_run_spec.rb +0 -87
  111. data/spec/plugin/multi_view_spec.rb +0 -50
  112. data/spec/plugin/multibyte_string_matcher_spec.rb +0 -44
  113. data/spec/plugin/named_templates_spec.rb +0 -96
  114. data/spec/plugin/not_allowed_spec.rb +0 -69
  115. data/spec/plugin/not_found_spec.rb +0 -128
  116. data/spec/plugin/optimized_string_matchers_spec.rb +0 -43
  117. data/spec/plugin/padrino_render_spec.rb +0 -34
  118. data/spec/plugin/param_matchers_spec.rb +0 -69
  119. data/spec/plugin/params_capturing_spec.rb +0 -33
  120. data/spec/plugin/partials_spec.rb +0 -43
  121. data/spec/plugin/pass_spec.rb +0 -29
  122. data/spec/plugin/path_matchers_spec.rb +0 -42
  123. data/spec/plugin/path_rewriter_spec.rb +0 -45
  124. data/spec/plugin/path_spec.rb +0 -222
  125. data/spec/plugin/placeholder_string_matchers_spec.rb +0 -126
  126. data/spec/plugin/precompile_templates_spec.rb +0 -61
  127. data/spec/plugin/public_spec.rb +0 -85
  128. data/spec/plugin/render_each_spec.rb +0 -82
  129. data/spec/plugin/render_locals_spec.rb +0 -114
  130. data/spec/plugin/render_spec.rb +0 -912
  131. data/spec/plugin/request_aref_spec.rb +0 -51
  132. data/spec/plugin/request_headers_spec.rb +0 -39
  133. data/spec/plugin/response_request_spec.rb +0 -43
  134. data/spec/plugin/route_block_args_spec.rb +0 -86
  135. data/spec/plugin/route_csrf_spec.rb +0 -305
  136. data/spec/plugin/run_append_slash_spec.rb +0 -77
  137. data/spec/plugin/run_handler_spec.rb +0 -53
  138. data/spec/plugin/sessions_spec.rb +0 -452
  139. data/spec/plugin/shared_vars_spec.rb +0 -45
  140. data/spec/plugin/sinatra_helpers_spec.rb +0 -537
  141. data/spec/plugin/slash_path_empty_spec.rb +0 -22
  142. data/spec/plugin/static_routing_spec.rb +0 -192
  143. data/spec/plugin/static_spec.rb +0 -30
  144. data/spec/plugin/status_303_spec.rb +0 -28
  145. data/spec/plugin/status_handler_spec.rb +0 -158
  146. data/spec/plugin/streaming_spec.rb +0 -246
  147. data/spec/plugin/strip_path_prefix_spec.rb +0 -24
  148. data/spec/plugin/symbol_matchers_spec.rb +0 -51
  149. data/spec/plugin/symbol_status_spec.rb +0 -25
  150. data/spec/plugin/symbol_views_spec.rb +0 -32
  151. data/spec/plugin/timestamp_public_spec.rb +0 -85
  152. data/spec/plugin/type_routing_spec.rb +0 -348
  153. data/spec/plugin/typecast_params_spec.rb +0 -1370
  154. data/spec/plugin/unescape_path_spec.rb +0 -22
  155. data/spec/plugin/view_options_spec.rb +0 -170
  156. data/spec/plugin_spec.rb +0 -71
  157. data/spec/redirect_spec.rb +0 -41
  158. data/spec/request_spec.rb +0 -97
  159. data/spec/response_spec.rb +0 -199
  160. data/spec/route_spec.rb +0 -39
  161. data/spec/session_middleware_spec.rb +0 -129
  162. data/spec/session_spec.rb +0 -37
  163. data/spec/spec_helper.rb +0 -137
  164. data/spec/version_spec.rb +0 -14
  165. data/spec/views/_test.erb +0 -1
  166. data/spec/views/a.erb +0 -1
  167. data/spec/views/a.rdoc +0 -2
  168. data/spec/views/about.erb +0 -1
  169. data/spec/views/about.str +0 -1
  170. data/spec/views/about/_test.css.gz +0 -0
  171. data/spec/views/about/_test.erb +0 -1
  172. data/spec/views/about/_test.erb.gz +0 -0
  173. data/spec/views/about/comp_test.erb +0 -1
  174. data/spec/views/b.erb +0 -1
  175. data/spec/views/c.erb +0 -1
  176. data/spec/views/comp_layout.erb +0 -1
  177. data/spec/views/comp_test.erb +0 -1
  178. data/spec/views/content-yield.erb +0 -1
  179. data/spec/views/each.str +0 -1
  180. data/spec/views/home.erb +0 -2
  181. data/spec/views/home.str +0 -2
  182. data/spec/views/iv.erb +0 -1
  183. data/spec/views/layout-alternative.erb +0 -2
  184. data/spec/views/layout-yield.erb +0 -3
  185. data/spec/views/layout.erb +0 -2
  186. data/spec/views/layout.str +0 -2
  187. data/spec/views/multiple-layout.erb +0 -1
  188. data/spec/views/multiple.erb +0 -1
@@ -1,11 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "Roda#env" do
4
- it "should return the environment" do
5
- app do |r|
6
- env['PATH_INFO']
7
- end
8
-
9
- body("/foo").must_equal "/foo"
10
- end
11
- end
@@ -1,37 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "Roda.freeze" do
4
- before do
5
- app{'a'}.freeze
6
- end
7
-
8
- it "should result in a working application" do
9
- body.must_equal 'a'
10
- end
11
-
12
- it "should not break if called more than once" do
13
- app.freeze
14
- body.must_equal 'a'
15
- end
16
-
17
- it "should make opts not be modifiable after calling finalize!" do
18
- proc{app.opts[:foo] = 'bar'}.must_raise
19
- end
20
-
21
- it "should make use and route raise errors" do
22
- proc{app.use Class.new}.must_raise
23
- proc{app.route{}}.must_raise
24
- end
25
-
26
- it "should make plugin raise errors" do
27
- proc{app.plugin Module.new}.must_raise
28
- end
29
-
30
- it "should make subclassing raise errors" do
31
- proc{Class.new(app)}.must_raise
32
- end
33
-
34
- it "should freeze app" do
35
- app.frozen?.must_equal true
36
- end
37
- end
@@ -1,209 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "integration" do
4
- before do
5
- @c = Class.new do
6
- def initialize(app, first, second, &block)
7
- @app, @first, @second, @block = app, first, second, block
8
- end
9
-
10
- def call(env)
11
- env["m.first"] = @first
12
- env["m.second"] = @second
13
- env["m.block"] = @block.call
14
-
15
- @app.call(env)
16
- end
17
- end
18
-
19
- end
20
-
21
- it "should setup middleware using use" do
22
- c = @c
23
- app(:bare) do
24
- use c, "First", "Second" do
25
- "Block"
26
- end
27
-
28
- route do |r|
29
- r.get "hello" do
30
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
31
- end
32
- end
33
- end
34
-
35
- body('/hello').must_equal 'D First Second Block'
36
- end
37
-
38
- it "should clear middleware when clear_middleware! is called" do
39
- c = @c
40
- app(:bare) do
41
- use c, "First", "Second" do
42
- "Block"
43
- end
44
-
45
- route do |r|
46
- r.get "hello" do
47
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
48
- end
49
- end
50
-
51
- clear_middleware!
52
- end
53
-
54
- body('/hello').must_equal 'D '
55
- end
56
-
57
- it "should freeze middleware if opts[:freeze_middleware] is true" do
58
- c = Class.new do
59
- def initialize(app) @app = app end
60
- def call(env) @a = 1; @app.call(env) end
61
- end
62
-
63
- app do
64
- "D"
65
- end
66
-
67
- body.must_equal 'D'
68
-
69
- app.use c
70
- body.must_equal 'D'
71
-
72
- app.clear_middleware!
73
- app.opts[:freeze_middleware] = true
74
- app.use c
75
- proc{body}.must_raise RuntimeError, TypeError
76
- end
77
-
78
- it "should support adding middleware using use after route block setup" do
79
- c = @c
80
- app(:bare) do
81
- route do |r|
82
- r.get "hello" do
83
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
84
- end
85
- end
86
-
87
- use c, "First", "Second" do
88
- "Block"
89
- end
90
- end
91
-
92
- body('/hello').must_equal 'D First Second Block'
93
- end
94
-
95
- it "should inherit middleware in subclass" do
96
- c = @c
97
- @app = Class.new(app(:bare){use(c, '1', '2'){"3"}})
98
- @app.route do |r|
99
- r.get "hello" do
100
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
101
- end
102
- end
103
-
104
- body('/hello').must_equal 'D 1 2 3'
105
- end
106
-
107
- it "should not inherit middleware in subclass if inhert_middleware = false" do
108
- c = @c
109
- app(:bare){use(c, '1', '2'){"3"}}
110
- @app.inherit_middleware = false
111
- @app = Class.new(@app)
112
- @app.route do |r|
113
- r.get "hello" do
114
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
115
- end
116
- end
117
-
118
- body('/hello').must_equal 'D '
119
- end
120
-
121
- it "should inherit route in subclass" do
122
- c = @c
123
- app(:bare) do
124
- use(c, '1', '2'){"3"}
125
- route do |r|
126
- r.get "hello" do
127
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
128
- end
129
- end
130
- end
131
- @app = Class.new(app)
132
-
133
- body('/hello').must_equal 'D 1 2 3'
134
- end
135
-
136
- it "should use instance of subclass when inheriting routes" do
137
- c = @c
138
- obj = nil
139
- app(:bare) do
140
- use(c, '1', '2'){"3"}
141
- route do |r|
142
- r.get "hello" do
143
- obj = self
144
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
145
- end
146
- end
147
- end
148
- @app = Class.new(app)
149
-
150
- body('/hello').must_equal 'D 1 2 3'
151
- obj.must_be_kind_of(@app)
152
- end
153
-
154
- it "should handle middleware added to subclass using superclass route" do
155
- c = @c
156
- app(:bare) do
157
- route do |r|
158
- r.get "hello" do
159
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
160
- end
161
- end
162
- end
163
- @app = Class.new(app)
164
- @app.use(c, '1', '2'){"3"}
165
-
166
- body('/hello').must_equal 'D 1 2 3'
167
- end
168
-
169
- it "should not have future middleware additions to superclass affect subclass" do
170
- c = @c
171
- a = app
172
- @app = Class.new(a)
173
- @app.route do |r|
174
- r.get "hello" do
175
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
176
- end
177
- end
178
- a.use(c, '1', '2'){"3"}
179
-
180
- body('/hello').must_equal 'D '
181
- end
182
-
183
- it "should not have future middleware additions to subclass affect superclass" do
184
- c = @c
185
- a = app do |r|
186
- r.get "hello" do
187
- "D #{r.env['m.first']} #{r.env['m.second']} #{r.env['m.block']}"
188
- end
189
- end
190
- @app = Class.new(a)
191
- @app.use(c, '1', '2'){"3"}
192
- @app = a
193
-
194
- body('/hello').must_equal 'D '
195
- end
196
-
197
- it "should have app return the rack application to call" do
198
- app(:bare){}.app.must_be_kind_of(Proc)
199
- app.route{|r|}
200
- app.app.must_be_kind_of(Proc)
201
- c = Class.new{def initialize(app) @app = app end; def call(env) @app.call(env) end}
202
- app.use c
203
- app.app.must_be_kind_of(c)
204
- end
205
-
206
- it "should have route_block return the route block" do
207
- app{|r| 1}.route_block.call(nil).must_equal 1
208
- end
209
- end
@@ -1,832 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "capturing" do
4
- it "doesn't yield the verb for verb matcher" do
5
- app do |r|
6
- r.get do |*args|
7
- args.size.to_s
8
- end
9
- end
10
-
11
- body.must_equal '0'
12
- end
13
-
14
- it "doesn't yield the path for string matcher" do
15
- app do |r|
16
- r.get "home" do |*args|
17
- args.size.to_s
18
- end
19
- end
20
-
21
- body('/home').must_equal '0'
22
- end
23
-
24
- it "yields the segment for symbol matcher" do
25
- app do |r|
26
- r.get "user", :id do |id|
27
- id
28
- end
29
- end
30
-
31
- body("/user/johndoe").must_equal 'johndoe'
32
- end
33
-
34
- it "yields an integer segment as a string when using symbol matcher" do
35
- app do |r|
36
- r.get "user", :id do |id|
37
- id
38
- end
39
- end
40
-
41
- body("/user/101").must_equal '101'
42
- end
43
-
44
- it "yields a segment per nested block for symbol matcher" do
45
- app do |r|
46
- r.on :one do |one|
47
- r.on :two do |two|
48
- r.on :three do |three|
49
- one + two + three
50
- end
51
- end
52
- end
53
- end
54
-
55
- body("/one/two/three").must_equal "onetwothree"
56
- end
57
-
58
- it "yields a segment per argument for symbol matcher" do
59
- app do |r|
60
- r.on :one, :two, :three do |one, two, three|
61
- one + two + three
62
- end
63
- end
64
-
65
- body("/one/two/three").must_equal "onetwothree"
66
- end
67
-
68
- it "yields regex captures as separate arguments" do
69
- app do |r|
70
- r.get %r{posts/(\d+)-(.*)} do |id, slug|
71
- id + slug
72
- end
73
- end
74
-
75
- body("/posts/123-postal-service").must_equal "123postal-service"
76
- end
77
-
78
- it "yields an integer segment as an integer when using Integer matcher " do
79
- app do |r|
80
- r.get "user", Integer do |id|
81
- "#{id}-#{id.is_a?(Integer)}"
82
- end
83
- "b"
84
- end
85
-
86
- body("/user/101").must_equal '101-true'
87
- body("/user/a").must_equal 'b'
88
- end
89
-
90
- it "yields the segment for String class matcher" do
91
- app do |r|
92
- r.get "user", String do |id|
93
- id
94
- end
95
- end
96
-
97
- body("/user/johndoe").must_equal 'johndoe'
98
- end
99
-
100
- it "raises error for unsupported class matcher" do
101
- app do |r|
102
- r.get Hash do |id|
103
- id
104
- end
105
- end
106
-
107
- proc{status}.must_raise Roda::RodaError
108
- end
109
- end
110
-
111
- describe "r.is" do
112
- it "ensures the patch is matched fully" do
113
- app do |r|
114
- r.is "" do
115
- "+1"
116
- end
117
- end
118
-
119
- body.must_equal '+1'
120
- status('//').must_equal 404
121
- end
122
-
123
- it "handles no arguments" do
124
- app do |r|
125
- r.on "" do
126
- r.is do
127
- "+1"
128
- end
129
- end
130
- end
131
-
132
- body.must_equal '+1'
133
- status('//').must_equal 404
134
- end
135
-
136
- it "matches strings" do
137
- app do |r|
138
- r.is "123" do
139
- "+1"
140
- end
141
- end
142
-
143
- body("/123").must_equal '+1'
144
- status("/123/").must_equal 404
145
- end
146
-
147
- it "matches regexps" do
148
- app do |r|
149
- r.is(/(\w+)/) do |id|
150
- id
151
- end
152
- end
153
-
154
- body("/123").must_equal '123'
155
- status("/123/").must_equal 404
156
- end
157
-
158
- it "matches regexps" do
159
- app do |r|
160
- r.on(/foo/) do |id|
161
- 'a'
162
- end
163
- end
164
-
165
- body("/foo").must_equal 'a'
166
- body("/foo/").must_equal 'a'
167
- status("/food").must_equal 404
168
- end
169
-
170
- it "matches segments" do
171
- app do |r|
172
- r.is :id do |id|
173
- id
174
- end
175
- end
176
-
177
- body("/123").must_equal '123'
178
- status("/123/").must_equal 404
179
- end
180
- end
181
-
182
- describe "matchers" do
183
- it "should not handle string with embedded param if :verbatim_string_matcher option is set" do
184
- app do |r|
185
- r.on "posts/:id" do
186
- '1'
187
- end
188
-
189
- r.on "responses-:id" do
190
- '2'
191
- end
192
- end
193
-
194
- status('/post/123').must_equal 404
195
- status('/posts/123').must_equal 404
196
- body('/posts/:id').must_equal '1'
197
- status('/responses-123').must_equal 404
198
- body('/responses-:id').must_equal '2'
199
- end
200
-
201
- it "should handle regexes and nesting" do
202
- app do |r|
203
- r.on(/u\/(\w+)/) do |uid|
204
- r.on(/posts\/(\d+)/) do |id|
205
- uid + id
206
- end
207
- end
208
- end
209
-
210
- body("/u/jdoe/posts/123").must_equal 'jdoe123'
211
- status("/u/jdoe/pots/123").must_equal 404
212
- end
213
-
214
- it "should handle regex nesting colon param style" do
215
- app do |r|
216
- r.on(/u:(\w+)/) do |uid|
217
- r.on(/posts:(\d+)/) do |id|
218
- uid + id
219
- end
220
- end
221
- end
222
-
223
- body("/u:jdoe/posts:123").must_equal 'jdoe123'
224
- status("/u:jdoe/poss:123").must_equal 404
225
- end
226
-
227
- it "symbol matching" do
228
- app do |r|
229
- r.on "user", :id do |uid|
230
- r.on "posts", :pid do |id|
231
- uid + id
232
- end
233
- end
234
- end
235
-
236
- body("/user/jdoe/posts/123").must_equal 'jdoe123'
237
- status("/user/jdoe/pots/123").must_equal 404
238
- end
239
-
240
- it "paths and numbers" do
241
- app do |r|
242
- r.on "about" do
243
- r.on :one, :two do |one, two|
244
- one + two
245
- end
246
- end
247
- end
248
-
249
- body("/about/1/2").must_equal '12'
250
- status("/about/1").must_equal 404
251
- end
252
-
253
- it "paths and decimals" do
254
- app do |r|
255
- r.on "about" do
256
- r.on(/(\d+)/) do |one|
257
- one
258
- end
259
- end
260
- end
261
-
262
- body("/about/1").must_equal '1'
263
- status("/about/1.2").must_equal 404
264
- end
265
-
266
- it "should allow arrays to match any value" do
267
- app do |r|
268
- r.on [/(\d+)/, /\d+(bar)?/] do |id|
269
- id
270
- end
271
- end
272
-
273
- body('/123').must_equal '123'
274
- body('/123bar').must_equal 'bar'
275
- status('/123bard').must_equal 404
276
- end
277
-
278
- it "should allow arrays to match optional segments with splats" do
279
- app do |r|
280
- r.on :foo, [:bar, true] do |*m|
281
- m.inspect
282
- end
283
- end
284
-
285
- body('/123').must_equal '["123"]'
286
- body('/123/456').must_equal '["123", "456"]'
287
- end
288
-
289
- it "should allow arrays to match optional segments with separate arguments" do
290
- app do |r|
291
- r.on :foo, [:bar, true] do |f, b|
292
- [f, b].inspect
293
- end
294
- end
295
-
296
- body('/123').must_equal '["123", nil]'
297
- body('/123/456').must_equal '["123", "456"]'
298
- end
299
-
300
- it "should allow regexp to match optional segments with separate arguments" do
301
- app do |r|
302
- r.on(/(\d+)(?:\/(\d+))?/) do |f, b|
303
- [f, b].inspect
304
- end
305
- end
306
-
307
- body('/123').must_equal '["123", nil]'
308
- body('/123/456').must_equal '["123", "456"]'
309
- end
310
-
311
- it "should have array capture match string if match" do
312
- app do |r|
313
- r.on %w'p q' do |id|
314
- id
315
- end
316
- end
317
-
318
- body('/p').must_equal 'p'
319
- body('/q').must_equal 'q'
320
- status('/r').must_equal 404
321
- end
322
- end
323
-
324
- describe "r.on" do
325
- it "executes on no arguments" do
326
- app do |r|
327
- r.on do
328
- "+1"
329
- end
330
- end
331
-
332
- body.must_equal '+1'
333
- end
334
-
335
- it "executes on true" do
336
- app do |r|
337
- r.on true do
338
- "+1"
339
- end
340
- end
341
-
342
- body.must_equal '+1'
343
- end
344
-
345
- it "does not execute on false" do
346
- app do |r|
347
- r.on false do
348
- "+1"
349
- end
350
- end
351
-
352
- status.must_equal 404
353
- end
354
-
355
- it "does not execute on nil" do
356
- app do |r|
357
- r.on nil do
358
- "+1"
359
- end
360
- end
361
-
362
- status.must_equal 404
363
- end
364
-
365
- it "raises on arbitrary object" do
366
- app(:bare) do
367
- route do |r|
368
- r.on Object.new do
369
- "+1"
370
- end
371
- end
372
- end
373
-
374
- proc{body}.must_raise Roda::RodaError
375
- end
376
-
377
- it "executes on non-false" do
378
- app do |r|
379
- r.on "123" do
380
- "+1"
381
- end
382
- end
383
-
384
- body("/123").must_equal '+1'
385
- end
386
-
387
- it "does not modify SCRIPT_NAME/PATH_INFO during routing" do
388
- app(:pass) do |r|
389
- r.on "foo" do
390
- r.is "bar" do
391
- "bar|#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
392
- end
393
- r.is "baz" do
394
- r.pass
395
- end
396
- "foo|#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
397
- end
398
- "#{env['SCRIPT_NAME']}|#{env['PATH_INFO']}"
399
- end
400
-
401
- body.must_equal '|/'
402
- body('SCRIPT_NAME'=>'/a').must_equal '/a|/'
403
- body('/foo').must_equal 'foo||/foo'
404
- body('/foo', 'SCRIPT_NAME'=>'/a').must_equal 'foo|/a|/foo'
405
- body('/foo/bar').must_equal 'bar||/foo/bar'
406
- body('/foo/bar', 'SCRIPT_NAME'=>'/a').must_equal 'bar|/a|/foo/bar'
407
- body('/foo/baz').must_equal 'foo||/foo/baz'
408
- body('/foo/baz', 'SCRIPT_NAME'=>'/a').must_equal 'foo|/a|/foo/baz'
409
- end
410
-
411
- it "should have path/matched_path/remaining_path work correctly" do
412
- app do |r|
413
- r.on "foo" do
414
- "#{r.path}:#{r.matched_path}:#{r.remaining_path}"
415
- end
416
- end
417
-
418
- body("/foo/bar").must_equal "/foo/bar:/foo:/bar"
419
- end
420
-
421
- it "ensures remaining_path is reverted if modified in failing matcher" do
422
- app do |r|
423
- r.on lambda { @remaining_path = "/blah"; false } do
424
- "Unreachable"
425
- end
426
-
427
- r.on do
428
- r.matched_path + ':' + r.remaining_path
429
- end
430
- end
431
-
432
- body("/hello").must_equal ':/hello'
433
- end
434
-
435
- it "modifies matched_path/remaining_path during routing" do
436
- app do |r|
437
- r.on 'login', 'foo' do
438
- "Unreachable"
439
- end
440
-
441
- r.on 'hello' do
442
- r.matched_path + ':' + r.remaining_path
443
- end
444
- end
445
-
446
- body("/hello/you").must_equal '/hello:/you'
447
- end
448
-
449
- it "doesn't modify SCRIPT_NAME/PATH_INFO during routing" do
450
- app do |r|
451
- r.on 'login', 'foo' do
452
- "Unreachable"
453
- end
454
-
455
- r.on 'hello' do
456
- r.env["SCRIPT_NAME"] + ':' + r.env["PATH_INFO"]
457
- end
458
- end
459
-
460
- body("/hello/you").must_equal ':/hello/you'
461
- end
462
-
463
- it "doesn't mutate SCRIPT_NAME or PATH_INFO after request is returned" do
464
- app do |r|
465
- r.on 'login', 'foo' do
466
- "Unreachable"
467
- end
468
-
469
- r.on do
470
- r.env["SCRIPT_NAME"] + ':' + r.env["PATH_INFO"]
471
- end
472
- end
473
-
474
- pi, sn = '/login', ''
475
- env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => pi, "SCRIPT_NAME" => sn}
476
- app.call(env)[2].join.must_equal ":/login"
477
- env["PATH_INFO"].must_equal pi
478
- env["SCRIPT_NAME"].must_equal sn
479
- end
480
-
481
- it "skips consecutive matches" do
482
- app do |r|
483
- r.on do
484
- "foo"
485
- end
486
-
487
- r.on do
488
- "bar"
489
- end
490
- end
491
-
492
- body.must_equal "foo"
493
- end
494
-
495
- it "finds first match available" do
496
- app do |r|
497
- r.on false do
498
- "foo"
499
- end
500
-
501
- r.on do
502
- "bar"
503
- end
504
- end
505
-
506
- body.must_equal "bar"
507
- end
508
-
509
- it "reverts a half-met matcher" do
510
- app do |r|
511
- r.on "post", false do
512
- "Should be unmet"
513
- end
514
-
515
- r.on do
516
- r.env["SCRIPT_NAME"] + ':' + r.env["PATH_INFO"]
517
- end
518
- end
519
-
520
- body("/hello").must_equal ':/hello'
521
- end
522
-
523
- it "doesn't write to body if body already written to" do
524
- app do |r|
525
- r.on do
526
- response.write "a"
527
- "b"
528
- end
529
- end
530
-
531
- body.must_equal 'a'
532
- end
533
- end
534
-
535
- describe "path matchers" do
536
- it "one level path" do
537
- app do |r|
538
- r.on "about" do
539
- "About"
540
- end
541
- end
542
-
543
- body('/about').must_equal "About"
544
- status("/abot").must_equal 404
545
- end
546
-
547
- it "two level nested paths" do
548
- app do |r|
549
- r.on "about" do
550
- r.on "1" do
551
- "+1"
552
- end
553
-
554
- r.on "2" do
555
- "+2"
556
- end
557
- end
558
- end
559
-
560
- body('/about/1').must_equal "+1"
561
- body('/about/2').must_equal "+2"
562
- status('/about/3').must_equal 404
563
- end
564
-
565
- it "two level inlined paths" do
566
- app do |r|
567
- r.on "a/b" do
568
- "ab"
569
- end
570
- end
571
-
572
- body('/a/b').must_equal "ab"
573
- status('/a/d').must_equal 404
574
- end
575
-
576
- it "a path with some regex captures" do
577
- app do |r|
578
- r.on(/user(\d+)/) do |uid|
579
- uid
580
- end
581
- end
582
-
583
- body('/user123').must_equal "123"
584
- status('/useradf').must_equal 404
585
- end
586
-
587
- it "matching the root with a string" do
588
- app do |r|
589
- r.is "" do
590
- "Home"
591
- end
592
- end
593
-
594
- body.must_equal 'Home'
595
- status("//").must_equal 404
596
- status("/foo").must_equal 404
597
- end
598
-
599
- it "matching the root with the root method" do
600
- app do |r|
601
- r.root do
602
- "Home"
603
- end
604
- end
605
-
606
- body.must_equal 'Home'
607
- status('REQUEST_METHOD'=>'POST').must_equal 404
608
- status("//").must_equal 404
609
- status("/foo").must_equal 404
610
- end
611
- end
612
-
613
- describe "root/empty segment matching" do
614
- it "matching an empty segment" do
615
- app do |r|
616
- r.on "" do
617
- r.path
618
- end
619
- end
620
-
621
- body.must_equal '/'
622
- status("/foo").must_equal 404
623
- end
624
-
625
- it "nested empty segments" do
626
- app do |r|
627
- r.on "" do
628
- r.on "" do
629
- r.on "1" do
630
- r.path
631
- end
632
- end
633
- end
634
- end
635
-
636
- body("///1").must_equal '///1'
637
- status("/1").must_equal 404
638
- status("//1").must_equal 404
639
- end
640
-
641
- it "/events/? scenario" do
642
- a = app do |r|
643
- r.on "" do
644
- "Hooray"
645
- end
646
-
647
- r.is do
648
- "Foo"
649
- end
650
- end
651
-
652
- app(:new) do |r|
653
- r.on "events" do
654
- r.run a
655
- end
656
- end
657
-
658
- body("/events").must_equal 'Foo'
659
- body("/events/").must_equal 'Hooray'
660
- status("/events/foo").must_equal 404
661
- end
662
- end
663
-
664
- describe "segment handling" do
665
- before do
666
- app do |r|
667
- r.on "post" do
668
- r.on :id do |id|
669
- id
670
- end
671
- end
672
- end
673
- end
674
-
675
- it "matches numeric ids" do
676
- body('/post/1').must_equal '1'
677
- end
678
-
679
- it "matches decimal numbers" do
680
- body('/post/1.1').must_equal '1.1'
681
- end
682
-
683
- it "matches slugs" do
684
- body('/post/my-blog-post-about-cuba').must_equal 'my-blog-post-about-cuba'
685
- end
686
-
687
- it "matches only the first segment available" do
688
- body('/post/one/two/three').must_equal 'one'
689
- end
690
- end
691
-
692
- describe "request verb methods" do
693
- it "executes if verb matches" do
694
- app do |r|
695
- r.get do
696
- "g"
697
- end
698
- r.post do
699
- "p"
700
- end
701
- end
702
-
703
- body.must_equal 'g'
704
- body('REQUEST_METHOD'=>'POST').must_equal 'p'
705
- end
706
-
707
- it "requires exact match if given arguments" do
708
- app do |r|
709
- r.get "" do
710
- "g"
711
- end
712
- r.post "" do
713
- "p"
714
- end
715
- end
716
-
717
- body.must_equal 'g'
718
- body('REQUEST_METHOD'=>'POST').must_equal 'p'
719
- status("/a").must_equal 404
720
- status("/a", 'REQUEST_METHOD'=>'POST').must_equal 404
721
- end
722
-
723
- it "does not require exact match if given arguments" do
724
- app do |r|
725
- r.get do
726
- r.is "" do
727
- "g"
728
- end
729
-
730
- "get"
731
- end
732
- r.post do
733
- r.is "" do
734
- "p"
735
- end
736
-
737
- "post"
738
- end
739
- end
740
-
741
- body.must_equal 'g'
742
- body('REQUEST_METHOD'=>'POST').must_equal 'p'
743
- body("/a").must_equal 'get'
744
- body("/a", 'REQUEST_METHOD'=>'POST').must_equal 'post'
745
- end
746
- end
747
-
748
- describe "all matcher" do
749
- it "should match only all all arguments match" do
750
- app do |r|
751
- r.is :all=>['foo', :y] do |file|
752
- file
753
- end
754
- end
755
-
756
- body("/foo/bar").must_equal 'bar'
757
- status.must_equal 404
758
- status("/foo").must_equal 404
759
- status("/foo/").must_equal 404
760
- status("/foo/bar/baz").must_equal 404
761
- end
762
- end
763
-
764
- describe "method matcher" do
765
- it "should match given request types" do
766
- app do |r|
767
- r.is "", :method=>:get do
768
- "foo"
769
- end
770
- r.is "", :method=>[:patch, :post] do
771
- "bar"
772
- end
773
- end
774
-
775
- body("REQUEST_METHOD"=>"GET").must_equal 'foo'
776
- body("REQUEST_METHOD"=>"PATCH").must_equal 'bar'
777
- body("REQUEST_METHOD"=>"POST").must_equal 'bar'
778
- status("REQUEST_METHOD"=>"DELETE").must_equal 404
779
- end
780
- end
781
-
782
- describe "route block that returns string" do
783
- it "should be treated as if an on block returned string" do
784
- app do |r|
785
- "+1"
786
- end
787
-
788
- body.must_equal '+1'
789
- end
790
- end
791
-
792
- describe "app with :unsupported_block_result => :raise option" do
793
- def app_value(v)
794
- app(:bare) do
795
- opts[:unsupported_block_result] = :raise
796
- route do |r|
797
- r.is 'a' do v end
798
- v
799
- end
800
- end
801
- end
802
-
803
- it "should handle String as body" do
804
- app_value '1'
805
- status.must_equal 200
806
- body.must_equal '1'
807
- status('/a').must_equal 200
808
- body('/a').must_equal '1'
809
- end
810
-
811
- it "should handle nil and false as not found" do
812
- app_value nil
813
- status.must_equal 404
814
- body.must_equal ''
815
- status('/a').must_equal 404
816
- body('/a').must_equal ''
817
- end
818
-
819
- it "should handle false as not found" do
820
- app_value false
821
- status.must_equal 404
822
- body.must_equal ''
823
- status('/a').must_equal 404
824
- body('/a').must_equal ''
825
- end
826
-
827
- it "should raise RodaError for other types" do
828
- app_value Object.new
829
- proc{body}.must_raise Roda::RodaError
830
- proc{body('/a')}.must_raise Roda::RodaError
831
- end
832
- end