journey 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.autotest +8 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +6 -0
  4. data/Gemfile +11 -0
  5. data/Manifest.txt +50 -0
  6. data/README.rdoc +48 -0
  7. data/Rakefile +31 -0
  8. data/journey.gemspec +42 -0
  9. data/lib/journey.rb +5 -0
  10. data/lib/journey/backwards.rb +5 -0
  11. data/lib/journey/core-ext/hash.rb +11 -0
  12. data/lib/journey/formatter.rb +129 -0
  13. data/lib/journey/gtg/builder.rb +159 -0
  14. data/lib/journey/gtg/simulator.rb +44 -0
  15. data/lib/journey/gtg/transition_table.rb +152 -0
  16. data/lib/journey/nfa/builder.rb +74 -0
  17. data/lib/journey/nfa/dot.rb +34 -0
  18. data/lib/journey/nfa/simulator.rb +45 -0
  19. data/lib/journey/nfa/transition_table.rb +164 -0
  20. data/lib/journey/nodes/node.rb +104 -0
  21. data/lib/journey/parser.rb +204 -0
  22. data/lib/journey/parser.y +47 -0
  23. data/lib/journey/parser_extras.rb +21 -0
  24. data/lib/journey/path/pattern.rb +190 -0
  25. data/lib/journey/route.rb +92 -0
  26. data/lib/journey/router.rb +138 -0
  27. data/lib/journey/router/strexp.rb +22 -0
  28. data/lib/journey/router/utils.rb +57 -0
  29. data/lib/journey/routes.rb +74 -0
  30. data/lib/journey/scanner.rb +58 -0
  31. data/lib/journey/visitors.rb +186 -0
  32. data/lib/journey/visualizer/d3.min.js +2 -0
  33. data/lib/journey/visualizer/fsm.css +34 -0
  34. data/lib/journey/visualizer/fsm.js +134 -0
  35. data/lib/journey/visualizer/index.html.erb +50 -0
  36. data/lib/journey/visualizer/reset.css +48 -0
  37. data/test/gtg/test_builder.rb +77 -0
  38. data/test/gtg/test_transition_table.rb +113 -0
  39. data/test/helper.rb +4 -0
  40. data/test/nfa/test_simulator.rb +96 -0
  41. data/test/nfa/test_transition_table.rb +70 -0
  42. data/test/nodes/test_symbol.rb +15 -0
  43. data/test/path/test_pattern.rb +260 -0
  44. data/test/route/definition/test_parser.rb +108 -0
  45. data/test/route/definition/test_scanner.rb +52 -0
  46. data/test/router/test_strexp.rb +30 -0
  47. data/test/router/test_utils.rb +19 -0
  48. data/test/test_route.rb +95 -0
  49. data/test/test_router.rb +464 -0
  50. data/test/test_routes.rb +51 -0
  51. metadata +168 -0
@@ -0,0 +1,108 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ module Definition
5
+ class TestParser < MiniTest::Unit::TestCase
6
+ def setup
7
+ @parser = Parser.new
8
+ end
9
+
10
+ def test_slash
11
+ assert_equal :SLASH, @parser.parse('/').type
12
+ assert_round_trip '/'
13
+ end
14
+
15
+ def test_segment
16
+ assert_round_trip '/foo'
17
+ end
18
+
19
+ def test_segments
20
+ assert_round_trip '/foo/bar'
21
+ end
22
+
23
+ def test_segment_symbol
24
+ assert_round_trip '/foo/:id'
25
+ end
26
+
27
+ def test_symbol
28
+ assert_round_trip '/:foo'
29
+ end
30
+
31
+ def test_group
32
+ assert_round_trip '(/:foo)'
33
+ end
34
+
35
+ def test_groups
36
+ assert_round_trip '(/:foo)(/:bar)'
37
+ end
38
+
39
+ def test_nested_groups
40
+ assert_round_trip '(/:foo(/:bar))'
41
+ end
42
+
43
+ def test_dot_symbol
44
+ assert_round_trip('.:format')
45
+ end
46
+
47
+ def test_dot_literal
48
+ assert_round_trip('.xml')
49
+ end
50
+
51
+ def test_segment_dot
52
+ assert_round_trip('/foo.:bar')
53
+ end
54
+
55
+ def test_segment_group_dot
56
+ assert_round_trip('/foo(.:bar)')
57
+ end
58
+
59
+ def test_segment_group
60
+ assert_round_trip('/foo(/:action)')
61
+ end
62
+
63
+ def test_segment_groups
64
+ assert_round_trip('/foo(/:action)(/:bar)')
65
+ end
66
+
67
+ def test_segment_nested_groups
68
+ assert_round_trip('/foo(/:action(/:bar))')
69
+ end
70
+
71
+ def test_group_followed_by_path
72
+ assert_round_trip('/foo(/:action)/:bar')
73
+ end
74
+
75
+ def test_star
76
+ assert_round_trip('*foo')
77
+ assert_round_trip('/*foo')
78
+ assert_round_trip('/bar/*foo')
79
+ assert_round_trip('/bar/(*foo)')
80
+ end
81
+
82
+ def test_or
83
+ assert_round_trip('a|b')
84
+ assert_round_trip('a|b|c')
85
+ assert_round_trip('(a|b)|c')
86
+ assert_round_trip('a|(b|c)')
87
+ assert_round_trip('*a|(b|c)')
88
+ assert_round_trip('*a|:b|c')
89
+ end
90
+
91
+ def test_arbitrary
92
+ assert_round_trip('/bar/*foo#')
93
+ end
94
+
95
+ def test_literal_dot_paren
96
+ assert_round_trip "/sprockets.js(.:format)"
97
+ end
98
+
99
+ def test_groups_with_dot
100
+ assert_round_trip "/(:locale)(.:format)"
101
+ end
102
+
103
+ def assert_round_trip str
104
+ assert_equal str, @parser.parse(str).to_s
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,52 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ module Definition
5
+ class TestScanner < MiniTest::Unit::TestCase
6
+ def setup
7
+ @scanner = Scanner.new
8
+ end
9
+
10
+ # /page/:id(/:action)(.:format)
11
+ def test_tokens
12
+ [
13
+ ['/', [[:SLASH, '/']]],
14
+ ['*omg', [[:STAR, '*'], [:LITERAL, 'omg']]],
15
+ ['/page', [[:SLASH, '/'], [:LITERAL, 'page']]],
16
+ ['/:page', [[:SLASH, '/'], [:SYMBOL, ':page']]],
17
+ ['/(:page)', [
18
+ [:SLASH, '/'],
19
+ [:LPAREN, '('],
20
+ [:SYMBOL, ':page'],
21
+ [:RPAREN, ')'],
22
+ ]],
23
+ ['(/:action)', [
24
+ [:LPAREN, '('],
25
+ [:SLASH, '/'],
26
+ [:SYMBOL, ':action'],
27
+ [:RPAREN, ')'],
28
+ ]],
29
+ ['(())', [[:LPAREN, '('],
30
+ [:LPAREN, '('], [:RPAREN, ')'], [:RPAREN, ')']]],
31
+ ['(.:format)', [
32
+ [:LPAREN, '('],
33
+ [:DOT, '.'],
34
+ [:SYMBOL, ':format'],
35
+ [:RPAREN, ')'],
36
+ ]],
37
+ ].each do |str, expected|
38
+ @scanner.scan_setup str
39
+ assert_tokens expected, @scanner
40
+ end
41
+ end
42
+
43
+ def assert_tokens tokens, scanner
44
+ toks = []
45
+ while tok = scanner.next_token
46
+ toks << tok
47
+ end
48
+ assert_equal tokens, toks
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ class Router
5
+ class TestStrexp < MiniTest::Unit::TestCase
6
+ def test_many_names
7
+ exp = Strexp.new(
8
+ "/:controller(/:action(/:id(.:format)))",
9
+ {:controller=>/.+?/},
10
+ ["/", ".", "?"],
11
+ true)
12
+
13
+ assert_equal ["controller", "action", "id", "format"], exp.names
14
+ end
15
+
16
+ def test_names
17
+ {
18
+ "/bar(.:format)" => %w{ format },
19
+ ":format" => %w{ format },
20
+ ":format-" => %w{ format },
21
+ ":format0" => %w{ format0 },
22
+ ":format1,:format2" => %w{ format1 format2 },
23
+ }.each do |string, expected|
24
+ exp = Strexp.new(string, {}, ["/", ".", "?"])
25
+ assert_equal expected, exp.names
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ class Router
5
+ class TestUtils < MiniTest::Unit::TestCase
6
+ def test_path_escape
7
+ assert_equal "a/b%20c+d", Utils.escape_path("a/b c+d")
8
+ end
9
+
10
+ def test_fragment_escape
11
+ assert_equal "a/b%20c+d?e", Utils.escape_fragment("a/b c+d?e")
12
+ end
13
+
14
+ def test_uri_unescape
15
+ assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,95 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ class TestRoute < MiniTest::Unit::TestCase
5
+ def test_initialize
6
+ app = Object.new
7
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
8
+ defaults = Object.new
9
+ route = Route.new("name", app, path, {}, defaults)
10
+
11
+ assert_equal app, route.app
12
+ assert_equal path, route.path
13
+ assert_equal defaults, route.defaults
14
+ end
15
+
16
+ def test_route_adds_itself_as_memo
17
+ app = Object.new
18
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
19
+ defaults = Object.new
20
+ route = Route.new("name", app, path, {}, defaults)
21
+
22
+ route.ast.grep(Nodes::Terminal).each do |node|
23
+ assert_equal route, node.memo
24
+ end
25
+ end
26
+
27
+ def test_ip_address
28
+ path = Path::Pattern.new '/messages/:id(.:format)'
29
+ route = Route.new("name", nil, path, {:ip => '192.168.1.1'},
30
+ { :controller => 'foo', :action => 'bar' })
31
+ assert_equal '192.168.1.1', route.ip
32
+ end
33
+
34
+ def test_default_ip
35
+ path = Path::Pattern.new '/messages/:id(.:format)'
36
+ route = Route.new("name", nil, path, {},
37
+ { :controller => 'foo', :action => 'bar' })
38
+ assert_equal(//, route.ip)
39
+ end
40
+
41
+ def test_format_empty
42
+ path = Path::Pattern.new '/messages/:id(.:format)'
43
+ route = Route.new("name", nil, path, {},
44
+ { :controller => 'foo', :action => 'bar' })
45
+
46
+ assert_equal '/messages', route.format({})
47
+ end
48
+
49
+ def test_format_with_star
50
+ path = Path::Pattern.new '/:controller/*extra'
51
+ route = Route.new("name", nil, path, {},
52
+ { :controller => 'foo', :action => 'bar' })
53
+ assert_equal '/foo/himom', route.format({
54
+ :controller => 'foo',
55
+ :extra => 'himom',
56
+ })
57
+ end
58
+
59
+ def test_connects_all_match
60
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
61
+ route = Route.new("name", nil, path, {:action => 'bar'}, { :controller => 'foo' })
62
+
63
+ assert_equal '/foo/bar/10', route.format({
64
+ :controller => 'foo',
65
+ :action => 'bar',
66
+ :id => 10
67
+ })
68
+ end
69
+
70
+ def test_extras_are_not_included_if_optional
71
+ path = Path::Pattern.new '/page/:id(/:action)'
72
+ route = Route.new("name", nil, path, { }, { :action => 'show' })
73
+
74
+ assert_equal '/page/10', route.format({ :id => 10 })
75
+ end
76
+
77
+ def test_score
78
+ path = Path::Pattern.new "/page/:id(/:action)(.:format)"
79
+ specific = Route.new "name", nil, path, {}, {:controller=>"pages", :action=>"show"}
80
+
81
+ path = Path::Pattern.new "/:controller(/:action(/:id))(.:format)"
82
+ generic = Route.new "name", nil, path, {}
83
+
84
+ knowledge = {:id=>20, :controller=>"pages", :action=>"show"}
85
+
86
+ routes = [specific, generic]
87
+
88
+ refute_equal specific.score(knowledge), generic.score(knowledge)
89
+
90
+ found = routes.sort_by { |r| r.score(knowledge) }.last
91
+
92
+ assert_equal specific, found
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,464 @@
1
+ require 'helper'
2
+
3
+ module Journey
4
+ class TestRouter < MiniTest::Unit::TestCase
5
+ attr_reader :routes
6
+
7
+ def setup
8
+ @routes = Routes.new
9
+ @router = Router.new(@routes, {})
10
+ @formatter = Formatter.new(@routes)
11
+ end
12
+
13
+ def test_request_class_reader
14
+ klass = Object.new
15
+ router = Router.new(routes, :request_class => klass)
16
+ assert_equal klass, router.request_class
17
+ end
18
+
19
+ class FakeRequestFeeler < Struct.new(:env, :called)
20
+ def new env
21
+ self.env = env
22
+ self
23
+ end
24
+
25
+ def hello
26
+ self.called = true
27
+ 'world'
28
+ end
29
+ end
30
+
31
+ def test_dashes
32
+ klass = FakeRequestFeeler.new nil
33
+ router = Router.new(routes, {})
34
+
35
+ exp = Router::Strexp.new '/foo-bar-baz', {}, ['/.?']
36
+ path = Path::Pattern.new exp
37
+
38
+ routes.add_route nil, path, {}, {:id => nil}, {}
39
+
40
+ env = rails_env 'PATH_INFO' => '/foo-bar-baz'
41
+ called = false
42
+ router.recognize(env) do |r, _, params|
43
+ called = true
44
+ end
45
+ assert called
46
+ end
47
+
48
+ def test_request_class_and_requirements_success
49
+ klass = FakeRequestFeeler.new nil
50
+ router = Router.new(routes, {:request_class => klass })
51
+
52
+ requirements = { :hello => /world/ }
53
+
54
+ exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
55
+ path = Path::Pattern.new exp
56
+
57
+ routes.add_route nil, path, requirements, {:id => nil}, {}
58
+
59
+ env = rails_env 'PATH_INFO' => '/foo/10'
60
+ router.recognize(env) do |r, _, params|
61
+ assert_equal({:id => '10'}, params)
62
+ end
63
+
64
+ assert klass.called, 'hello should have been called'
65
+ assert_equal env.env, klass.env
66
+ end
67
+
68
+ def test_request_class_and_requirements_fail
69
+ klass = FakeRequestFeeler.new nil
70
+ router = Router.new(routes, {:request_class => klass })
71
+
72
+ requirements = { :hello => /mom/ }
73
+
74
+ exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
75
+ path = Path::Pattern.new exp
76
+
77
+ router.routes.add_route nil, path, requirements, {:id => nil}, {}
78
+
79
+ env = rails_env 'PATH_INFO' => '/foo/10'
80
+ router.recognize(env) do |r, _, params|
81
+ flunk 'route should not be found'
82
+ end
83
+
84
+ assert klass.called, 'hello should have been called'
85
+ assert_equal env.env, klass.env
86
+ end
87
+
88
+ def test_required_parts_verified_are_anchored
89
+ add_routes @router, [
90
+ Router::Strexp.new("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false)
91
+ ]
92
+
93
+ assert_raises(Router::RoutingError) do
94
+ @formatter.generate(:path_info, nil, { :id => '10' }, { })
95
+ end
96
+ end
97
+
98
+ def test_required_parts_are_verified_when_building
99
+ add_routes @router, [
100
+ Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
101
+ ]
102
+
103
+ path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
104
+ assert_equal '/foo/10', path
105
+
106
+ assert_raises(Router::RoutingError) do
107
+ @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
108
+ end
109
+ end
110
+
111
+ def test_only_required_parts_are_verified
112
+ add_routes @router, [
113
+ Router::Strexp.new("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false)
114
+ ]
115
+
116
+ path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
117
+ assert_equal '/foo/10', path
118
+
119
+ path, _ = @formatter.generate(:path_info, nil, { }, { })
120
+ assert_equal '/foo', path
121
+
122
+ path, _ = @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
123
+ assert_equal '/foo/aa', path
124
+ end
125
+
126
+ def test_X_Cascade
127
+ add_routes @router, [ "/messages(.:format)" ]
128
+ resp = @router.call({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })
129
+ assert_equal ['Not Found'], resp.last
130
+ assert_equal 'pass', resp[1]['X-Cascade']
131
+ assert_equal 404, resp.first
132
+ end
133
+
134
+ def test_defaults_merge_correctly
135
+ path = Path::Pattern.new '/foo(/:id)'
136
+ @router.routes.add_route nil, path, {}, {:id => nil}, {}
137
+
138
+ env = rails_env 'PATH_INFO' => '/foo/10'
139
+ @router.recognize(env) do |r, _, params|
140
+ assert_equal({:id => '10'}, params)
141
+ end
142
+
143
+ env = rails_env 'PATH_INFO' => '/foo'
144
+ @router.recognize(env) do |r, _, params|
145
+ assert_equal({:id => nil}, params)
146
+ end
147
+ end
148
+
149
+ def test_recognize_with_unbound_regexp
150
+ add_routes @router, [
151
+ Router::Strexp.new("/foo", { }, ['/', '.', '?'], false)
152
+ ]
153
+
154
+ env = rails_env 'PATH_INFO' => '/foo/bar'
155
+
156
+ @router.recognize(env) { |*_| }
157
+
158
+ assert_equal '/foo', env.env['SCRIPT_NAME']
159
+ assert_equal '/bar', env.env['PATH_INFO']
160
+ end
161
+
162
+ def test_bound_regexp_keeps_path_info
163
+ add_routes @router, [
164
+ Router::Strexp.new("/foo", { }, ['/', '.', '?'], true)
165
+ ]
166
+
167
+ env = rails_env 'PATH_INFO' => '/foo'
168
+
169
+ before = env.env['SCRIPT_NAME']
170
+
171
+ @router.recognize(env) { |*_| }
172
+
173
+ assert_equal before, env.env['SCRIPT_NAME']
174
+ assert_equal '/foo', env.env['PATH_INFO']
175
+ end
176
+
177
+ def test_path_not_found
178
+ add_routes @router, [
179
+ "/messages(.:format)",
180
+ "/messages/new(.:format)",
181
+ "/messages/:id/edit(.:format)",
182
+ "/messages/:id(.:format)"
183
+ ]
184
+ env = rails_env 'PATH_INFO' => '/messages/1.1.1'
185
+ yielded = false
186
+
187
+ @router.recognize(env) do |*whatever|
188
+ yielded = false
189
+ end
190
+ refute yielded
191
+ end
192
+
193
+ def test_required_part_in_recall
194
+ add_routes @router, [ "/messages/:a/:b" ]
195
+
196
+ path, _ = @formatter.generate(:path_info, nil, { :a => 'a' }, { :b => 'b' })
197
+ assert_equal "/messages/a/b", path
198
+ end
199
+
200
+ def test_splat_in_recall
201
+ add_routes @router, [ "/*path" ]
202
+
203
+ path, _ = @formatter.generate(:path_info, nil, { }, { :path => 'b' })
204
+ assert_equal "/b", path
205
+ end
206
+
207
+ def test_recall_should_be_used_when_scoring
208
+ add_routes @router, [
209
+ "/messages/:action(/:id(.:format))",
210
+ "/messages/:id(.:format)"
211
+ ]
212
+
213
+ path, _ = @formatter.generate(:path_info, nil, { :id => 10 }, { :action => 'index' })
214
+ assert_equal "/messages/index/10", path
215
+ end
216
+
217
+ def add_routes router, paths
218
+ paths.each do |path|
219
+ path = Path::Pattern.new path
220
+ router.routes.add_route nil, path, {}, {}, {}
221
+ end
222
+ end
223
+
224
+ def test_nil_path_parts_are_ignored
225
+ path = Path::Pattern.new "/:controller(/:action(.:format))"
226
+ @router.routes.add_route nil, path, {}, {}, {}
227
+
228
+ params = { :controller => "tasks", :format => nil }
229
+ extras = { :action => 'lol' }
230
+
231
+ path, _ = @formatter.generate(:path_info, nil, params, extras)
232
+ assert_equal '/tasks', path
233
+ end
234
+
235
+ def test_generate_slash
236
+ path = Path::Pattern.new '/'
237
+ @router.routes.add_route nil, path, {}, {}, {}
238
+
239
+ params = [ [:controller, "tasks"],
240
+ [:action, "show"] ]
241
+
242
+ path, _ = @formatter.generate(:path_info, nil, Hash[params], {})
243
+ assert_equal '/', path
244
+ end
245
+
246
+ def test_generate_calls_param_proc
247
+ path = Path::Pattern.new '/:controller(/:action)'
248
+ @router.routes.add_route nil, path, {}, {}, {}
249
+
250
+ parameterized = []
251
+ params = [ [:controller, "tasks"],
252
+ [:action, "show"] ]
253
+
254
+ @formatter.generate(
255
+ :path_info,
256
+ nil,
257
+ Hash[params],
258
+ {},
259
+ lambda { |k,v| parameterized << [k,v]; v })
260
+
261
+ assert_equal params.map(&:to_s).sort, parameterized.map(&:to_s).sort
262
+ end
263
+
264
+ def test_generate_id
265
+ path = Path::Pattern.new '/:controller(/:action)'
266
+ @router.routes.add_route nil, path, {}, {}, {}
267
+
268
+ path, params = @formatter.generate(
269
+ :path_info, nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {})
270
+ assert_equal '/tasks/show', path
271
+ assert_equal({:id => 1}, params)
272
+ end
273
+
274
+ def test_generate_escapes
275
+ path = Path::Pattern.new '/:controller(/:action)'
276
+ @router.routes.add_route nil, path, {}, {}, {}
277
+
278
+ path, _ = @formatter.generate(:path_info,
279
+ nil, { :controller => "tasks",
280
+ :action => "a/b c+d",
281
+ }, {})
282
+ assert_equal '/tasks/a/b%20c+d', path
283
+ end
284
+
285
+ def test_generate_extra_params
286
+ path = Path::Pattern.new '/:controller(/:action)'
287
+ @router.routes.add_route nil, path, {}, {}, {}
288
+
289
+ path, params = @formatter.generate(:path_info,
290
+ nil, { :id => 1,
291
+ :controller => "tasks",
292
+ :action => "show",
293
+ :relative_url_root => nil
294
+ }, {})
295
+ assert_equal '/tasks/show', path
296
+ assert_equal({:id => 1, :relative_url_root => nil}, params)
297
+ end
298
+
299
+ def test_generate_uses_recall_if_needed
300
+ path = Path::Pattern.new '/:controller(/:action(/:id))'
301
+ @router.routes.add_route nil, path, {}, {}, {}
302
+
303
+ path, params = @formatter.generate(:path_info,
304
+ nil,
305
+ {:controller =>"tasks", :id => 10},
306
+ {:action =>"index"})
307
+ assert_equal '/tasks/index/10', path
308
+ assert_equal({}, params)
309
+ end
310
+
311
+ def test_generate_with_name
312
+ path = Path::Pattern.new '/:controller(/:action)'
313
+ @router.routes.add_route nil, path, {}, {}, {}
314
+
315
+ path, params = @formatter.generate(:path_info,
316
+ "tasks",
317
+ {:controller=>"tasks"},
318
+ {:controller=>"tasks", :action=>"index"})
319
+ assert_equal '/tasks', path
320
+ assert_equal({}, params)
321
+ end
322
+
323
+ {
324
+ '/content' => { :controller => 'content' },
325
+ '/content/list' => { :controller => 'content', :action => 'list' },
326
+ '/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
327
+ }.each do |request_path, expected|
328
+ define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
329
+ path = Path::Pattern.new "/:controller(/:action(/:id))"
330
+ app = Object.new
331
+ route = @router.routes.add_route(app, path, {}, {}, {})
332
+
333
+ env = rails_env 'PATH_INFO' => request_path
334
+ called = false
335
+
336
+ @router.recognize(env) do |r, _, params|
337
+ assert_equal route, r
338
+ assert_equal(expected, params)
339
+ called = true
340
+ end
341
+
342
+ assert called
343
+ end
344
+ end
345
+
346
+ {
347
+ :segment => ['/a%2Fb%20c+d/splat', { :segment => 'a/b c+d', :splat => 'splat' }],
348
+ :splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }]
349
+ }.each do |name, (request_path, expected)|
350
+ define_method("test_recognize_#{name}") do
351
+ path = Path::Pattern.new '/:segment/*splat'
352
+ app = Object.new
353
+ route = @router.routes.add_route(app, path, {}, {}, {})
354
+
355
+ env = rails_env 'PATH_INFO' => request_path
356
+ called = false
357
+
358
+ @router.recognize(env) do |r, _, params|
359
+ assert_equal route, r
360
+ assert_equal(expected, params)
361
+ called = true
362
+ end
363
+
364
+ assert called
365
+ end
366
+ end
367
+
368
+ def test_namespaced_controller
369
+ strexp = Router::Strexp.new(
370
+ "/:controller(/:action(/:id))",
371
+ { :controller => /.+?/ },
372
+ ["/", ".", "?"]
373
+ )
374
+ path = Path::Pattern.new strexp
375
+ app = Object.new
376
+ route = @router.routes.add_route(app, path, {}, {}, {})
377
+
378
+ env = rails_env 'PATH_INFO' => '/admin/users/show/10'
379
+ called = false
380
+ expected = {
381
+ :controller => 'admin/users',
382
+ :action => 'show',
383
+ :id => '10'
384
+ }
385
+
386
+ @router.recognize(env) do |r, _, params|
387
+ assert_equal route, r
388
+ assert_equal(expected, params)
389
+ called = true
390
+ end
391
+ assert called
392
+ end
393
+
394
+ def test_recognize_literal
395
+ path = Path::Pattern.new "/books(/:action(.:format))"
396
+ app = Object.new
397
+ route = @router.routes.add_route(app, path, {}, {:controller => 'books'})
398
+
399
+ env = rails_env 'PATH_INFO' => '/books/list.rss'
400
+ expected = { :controller => 'books', :action => 'list', :format => 'rss' }
401
+ called = false
402
+ @router.recognize(env) do |r, _, params|
403
+ assert_equal route, r
404
+ assert_equal(expected, params)
405
+ called = true
406
+ end
407
+
408
+ assert called
409
+ end
410
+
411
+ def test_recognize_cares_about_verbs
412
+ path = Path::Pattern.new "/books(/:action(.:format))"
413
+ app = Object.new
414
+ conditions = {
415
+ :request_method => 'GET'
416
+ }
417
+ @router.routes.add_route(app, path, conditions, {})
418
+
419
+ conditions = conditions.dup
420
+ conditions[:request_method] = 'POST'
421
+
422
+ post = @router.routes.add_route(app, path, conditions, {})
423
+
424
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
425
+ "REQUEST_METHOD" => "POST"
426
+
427
+ called = false
428
+ @router.recognize(env) do |r, _, params|
429
+ assert_equal post, r
430
+ called = true
431
+ end
432
+
433
+ assert called
434
+ end
435
+
436
+ private
437
+
438
+ RailsEnv = Struct.new(:env)
439
+
440
+ def rails_env env
441
+ RailsEnv.new rack_env env
442
+ end
443
+
444
+ def rack_env env
445
+ {
446
+ "rack.version" => [1, 1],
447
+ "rack.input" => StringIO.new,
448
+ "rack.errors" => StringIO.new,
449
+ "rack.multithread" => true,
450
+ "rack.multiprocess" => true,
451
+ "rack.run_once" => false,
452
+ "REQUEST_METHOD" => "GET",
453
+ "SERVER_NAME" => "example.org",
454
+ "SERVER_PORT" => "80",
455
+ "QUERY_STRING" => "",
456
+ "PATH_INFO" => "/content",
457
+ "rack.url_scheme" => "http",
458
+ "HTTPS" => "off",
459
+ "SCRIPT_NAME" => "",
460
+ "CONTENT_LENGTH" => "0"
461
+ }.merge env
462
+ end
463
+ end
464
+ end