journey 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +8 -0
- data/.gemtest +0 -0
- data/CHANGELOG.rdoc +6 -0
- data/Gemfile +11 -0
- data/Manifest.txt +50 -0
- data/README.rdoc +48 -0
- data/Rakefile +31 -0
- data/journey.gemspec +42 -0
- data/lib/journey.rb +5 -0
- data/lib/journey/backwards.rb +5 -0
- data/lib/journey/core-ext/hash.rb +11 -0
- data/lib/journey/formatter.rb +129 -0
- data/lib/journey/gtg/builder.rb +159 -0
- data/lib/journey/gtg/simulator.rb +44 -0
- data/lib/journey/gtg/transition_table.rb +152 -0
- data/lib/journey/nfa/builder.rb +74 -0
- data/lib/journey/nfa/dot.rb +34 -0
- data/lib/journey/nfa/simulator.rb +45 -0
- data/lib/journey/nfa/transition_table.rb +164 -0
- data/lib/journey/nodes/node.rb +104 -0
- data/lib/journey/parser.rb +204 -0
- data/lib/journey/parser.y +47 -0
- data/lib/journey/parser_extras.rb +21 -0
- data/lib/journey/path/pattern.rb +190 -0
- data/lib/journey/route.rb +92 -0
- data/lib/journey/router.rb +138 -0
- data/lib/journey/router/strexp.rb +22 -0
- data/lib/journey/router/utils.rb +57 -0
- data/lib/journey/routes.rb +74 -0
- data/lib/journey/scanner.rb +58 -0
- data/lib/journey/visitors.rb +186 -0
- data/lib/journey/visualizer/d3.min.js +2 -0
- data/lib/journey/visualizer/fsm.css +34 -0
- data/lib/journey/visualizer/fsm.js +134 -0
- data/lib/journey/visualizer/index.html.erb +50 -0
- data/lib/journey/visualizer/reset.css +48 -0
- data/test/gtg/test_builder.rb +77 -0
- data/test/gtg/test_transition_table.rb +113 -0
- data/test/helper.rb +4 -0
- data/test/nfa/test_simulator.rb +96 -0
- data/test/nfa/test_transition_table.rb +70 -0
- data/test/nodes/test_symbol.rb +15 -0
- data/test/path/test_pattern.rb +260 -0
- data/test/route/definition/test_parser.rb +108 -0
- data/test/route/definition/test_scanner.rb +52 -0
- data/test/router/test_strexp.rb +30 -0
- data/test/router/test_utils.rb +19 -0
- data/test/test_route.rb +95 -0
- data/test/test_router.rb +464 -0
- data/test/test_routes.rb +51 -0
- 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
|
data/test/test_route.rb
ADDED
@@ -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
|
data/test/test_router.rb
ADDED
@@ -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
|