actionpack 4.2.11.3 → 5.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +890 -384
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +54 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +14 -11
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +14 -34
- data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +11 -11
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +71 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +9 -9
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +83 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +527 -134
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/railtie.rb +11 -7
- data/lib/action_controller/renderer.rb +113 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +311 -374
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +16 -12
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +45 -14
- data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +19 -9
- data/lib/action_dispatch/http/parameters.rb +70 -40
- data/lib/action_dispatch/http/request.rb +144 -89
- data/lib/action_dispatch/http/response.rb +215 -102
- data/lib/action_dispatch/http/upload.rb +6 -2
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +47 -30
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser.rb +2 -0
- data/lib/action_dispatch/journey/parser_extras.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +88 -26
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +8 -10
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +89 -44
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +188 -134
- data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
- data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +124 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +515 -348
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +5 -4
- data/lib/action_dispatch/routing/route_set.rb +148 -240
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +377 -149
- data/lib/action_dispatch/testing/request_encoder.rb +53 -0
- data/lib/action_dispatch/testing/test_process.rb +24 -20
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +12 -4
- data/lib/action_dispatch.rb +4 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +32 -34
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'action_dispatch/journey/router/strexp'
|
2
|
-
|
3
1
|
module ActionDispatch
|
4
2
|
module Journey # :nodoc:
|
5
3
|
module Path # :nodoc:
|
@@ -7,14 +5,20 @@ module ActionDispatch
|
|
7
5
|
attr_reader :spec, :requirements, :anchored
|
8
6
|
|
9
7
|
def self.from_string string
|
10
|
-
|
8
|
+
build(string, {}, "/.?", true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build(path, requirements, separators, anchored)
|
12
|
+
parser = Journey::Parser.new
|
13
|
+
ast = parser.parse path
|
14
|
+
new ast, requirements, separators, anchored
|
11
15
|
end
|
12
16
|
|
13
|
-
def initialize(
|
14
|
-
@spec =
|
15
|
-
@requirements =
|
16
|
-
@separators =
|
17
|
-
@anchored =
|
17
|
+
def initialize(ast, requirements, separators, anchored)
|
18
|
+
@spec = ast
|
19
|
+
@requirements = requirements
|
20
|
+
@separators = separators
|
21
|
+
@anchored = anchored
|
18
22
|
|
19
23
|
@names = nil
|
20
24
|
@optional_names = nil
|
@@ -28,12 +32,12 @@ module ActionDispatch
|
|
28
32
|
end
|
29
33
|
|
30
34
|
def ast
|
31
|
-
@spec.
|
35
|
+
@spec.find_all(&:symbol?).each do |node|
|
32
36
|
re = @requirements[node.to_sym]
|
33
37
|
node.regexp = re if re
|
34
38
|
end
|
35
39
|
|
36
|
-
@spec.
|
40
|
+
@spec.find_all(&:star?).each do |node|
|
37
41
|
node = node.left
|
38
42
|
node.regexp = @requirements[node.to_sym] || /(.+)/
|
39
43
|
end
|
@@ -42,7 +46,7 @@ module ActionDispatch
|
|
42
46
|
end
|
43
47
|
|
44
48
|
def names
|
45
|
-
@names ||= spec.
|
49
|
+
@names ||= spec.find_all(&:symbol?).map(&:name)
|
46
50
|
end
|
47
51
|
|
48
52
|
def required_names
|
@@ -50,34 +54,9 @@ module ActionDispatch
|
|
50
54
|
end
|
51
55
|
|
52
56
|
def optional_names
|
53
|
-
@optional_names ||= spec.
|
54
|
-
group.
|
55
|
-
}.map
|
56
|
-
end
|
57
|
-
|
58
|
-
class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
|
59
|
-
attr_reader :offsets
|
60
|
-
|
61
|
-
def initialize(matchers)
|
62
|
-
@matchers = matchers
|
63
|
-
@capture_count = [0]
|
64
|
-
end
|
65
|
-
|
66
|
-
def visit(node)
|
67
|
-
super
|
68
|
-
@capture_count
|
69
|
-
end
|
70
|
-
|
71
|
-
def visit_SYMBOL(node)
|
72
|
-
node = node.to_sym
|
73
|
-
|
74
|
-
if @matchers.key?(node)
|
75
|
-
re = /#{@matchers[node]}|/
|
76
|
-
@capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
|
77
|
-
else
|
78
|
-
@capture_count << (@capture_count.last || 0)
|
79
|
-
end
|
80
|
-
end
|
57
|
+
@optional_names ||= spec.find_all(&:group?).flat_map { |group|
|
58
|
+
group.find_all(&:symbol?)
|
59
|
+
}.map(&:name).uniq
|
81
60
|
end
|
82
61
|
|
83
62
|
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
|
@@ -122,6 +101,11 @@ module ActionDispatch
|
|
122
101
|
re = @matchers[node.left.to_sym] || '.+'
|
123
102
|
"(#{re})"
|
124
103
|
end
|
104
|
+
|
105
|
+
def visit_OR(node)
|
106
|
+
children = node.children.map { |n| visit n }
|
107
|
+
"(?:#{children.join(?|)})"
|
108
|
+
end
|
125
109
|
end
|
126
110
|
|
127
111
|
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
|
@@ -140,7 +124,7 @@ module ActionDispatch
|
|
140
124
|
end
|
141
125
|
|
142
126
|
def captures
|
143
|
-
(length - 1)
|
127
|
+
Array.new(length - 1) { |i| self[i + 1] }
|
144
128
|
end
|
145
129
|
|
146
130
|
def [](x)
|
@@ -184,8 +168,20 @@ module ActionDispatch
|
|
184
168
|
def offsets
|
185
169
|
return @offsets if @offsets
|
186
170
|
|
187
|
-
|
188
|
-
|
171
|
+
@offsets = [0]
|
172
|
+
|
173
|
+
spec.find_all(&:symbol?).each do |node|
|
174
|
+
node = node.to_sym
|
175
|
+
|
176
|
+
if @requirements.key?(node)
|
177
|
+
re = /#{@requirements[node]}|/
|
178
|
+
@offsets.push((re.match('').length - 1) + @offsets.last)
|
179
|
+
else
|
180
|
+
@offsets << @offsets.last
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
@offsets
|
189
185
|
end
|
190
186
|
end
|
191
187
|
end
|
@@ -1,41 +1,89 @@
|
|
1
1
|
module ActionDispatch
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
# :stopdoc:
|
3
|
+
module Journey
|
4
|
+
class Route
|
5
|
+
attr_reader :app, :path, :defaults, :name, :precedence
|
5
6
|
|
6
|
-
attr_reader :constraints
|
7
|
+
attr_reader :constraints, :internal
|
7
8
|
alias :conditions :constraints
|
8
9
|
|
9
|
-
|
10
|
+
module VerbMatchers
|
11
|
+
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
12
|
+
VERBS.each do |v|
|
13
|
+
class_eval <<-eoc
|
14
|
+
class #{v}
|
15
|
+
def self.verb; name.split("::").last; end
|
16
|
+
def self.call(req); req.#{v.downcase}?; end
|
17
|
+
end
|
18
|
+
eoc
|
19
|
+
end
|
20
|
+
|
21
|
+
class Unknown
|
22
|
+
attr_reader :verb
|
23
|
+
|
24
|
+
def initialize(verb)
|
25
|
+
@verb = verb
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(request); @verb === request.request_method; end
|
29
|
+
end
|
30
|
+
|
31
|
+
class All
|
32
|
+
def self.call(_); true; end
|
33
|
+
def self.verb; ''; end
|
34
|
+
end
|
35
|
+
|
36
|
+
VERB_TO_CLASS = VERBS.each_with_object({ :all => All }) do |verb, hash|
|
37
|
+
klass = const_get verb
|
38
|
+
hash[verb] = klass
|
39
|
+
hash[verb.downcase] = klass
|
40
|
+
hash[verb.downcase.to_sym] = klass
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.verb_matcher(verb)
|
46
|
+
VerbMatchers::VERB_TO_CLASS.fetch(verb) do
|
47
|
+
VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.build(name, app, path, constraints, required_defaults, defaults)
|
52
|
+
request_method_match = verb_matcher(constraints.delete(:request_method))
|
53
|
+
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
54
|
+
end
|
10
55
|
|
11
56
|
##
|
12
57
|
# +path+ is a path constraint.
|
13
58
|
# +constraints+ is a hash of constraints to be applied to this route.
|
14
|
-
def initialize(name, app, path, constraints, defaults =
|
59
|
+
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
|
15
60
|
@name = name
|
16
61
|
@app = app
|
17
62
|
@path = path
|
18
63
|
|
64
|
+
@request_method_match = request_method_match
|
19
65
|
@constraints = constraints
|
20
66
|
@defaults = defaults
|
21
67
|
@required_defaults = nil
|
68
|
+
@_required_defaults = required_defaults
|
22
69
|
@required_parts = nil
|
23
70
|
@parts = nil
|
24
71
|
@decorated_ast = nil
|
25
|
-
@precedence =
|
72
|
+
@precedence = precedence
|
26
73
|
@path_formatter = @path.build_formatter
|
74
|
+
@internal = internal
|
27
75
|
end
|
28
76
|
|
29
77
|
def ast
|
30
78
|
@decorated_ast ||= begin
|
31
79
|
decorated_ast = path.ast
|
32
|
-
decorated_ast.
|
80
|
+
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
33
81
|
decorated_ast
|
34
82
|
end
|
35
83
|
end
|
36
84
|
|
37
|
-
def requirements
|
38
|
-
# needed for rails `
|
85
|
+
def requirements
|
86
|
+
# needed for rails `rails routes`
|
39
87
|
@defaults.merge(path.requirements).delete_if { |_,v|
|
40
88
|
/.+?/ == v
|
41
89
|
}
|
@@ -49,18 +97,23 @@ module ActionDispatch
|
|
49
97
|
required_parts + required_defaults.keys
|
50
98
|
end
|
51
99
|
|
52
|
-
def score(
|
100
|
+
def score(supplied_keys)
|
53
101
|
required_keys = path.required_names
|
54
|
-
supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
|
55
102
|
|
56
|
-
|
103
|
+
required_keys.each do |k|
|
104
|
+
return -1 unless supplied_keys.include?(k)
|
105
|
+
end
|
106
|
+
|
107
|
+
score = 0
|
108
|
+
path.names.each do |k|
|
109
|
+
score += 1 if supplied_keys.include?(k)
|
110
|
+
end
|
57
111
|
|
58
|
-
score = (supplied_keys & path.names).length
|
59
112
|
score + (required_defaults.length * 2)
|
60
113
|
end
|
61
114
|
|
62
115
|
def parts
|
63
|
-
@parts ||= segments.map
|
116
|
+
@parts ||= segments.map(&:to_sym)
|
64
117
|
end
|
65
118
|
alias :segment_keys :parts
|
66
119
|
|
@@ -68,16 +121,12 @@ module ActionDispatch
|
|
68
121
|
@path_formatter.evaluate path_options
|
69
122
|
end
|
70
123
|
|
71
|
-
def optional_parts
|
72
|
-
path.optional_names.map { |n| n.to_sym }
|
73
|
-
end
|
74
|
-
|
75
124
|
def required_parts
|
76
|
-
@required_parts ||= path.required_names.map
|
125
|
+
@required_parts ||= path.required_names.map(&:to_sym)
|
77
126
|
end
|
78
127
|
|
79
128
|
def required_default?(key)
|
80
|
-
|
129
|
+
@_required_defaults.include?(key)
|
81
130
|
end
|
82
131
|
|
83
132
|
def required_defaults
|
@@ -95,9 +144,8 @@ module ActionDispatch
|
|
95
144
|
end
|
96
145
|
|
97
146
|
def matches?(request)
|
98
|
-
|
99
|
-
|
100
|
-
|
147
|
+
match_verb(request) &&
|
148
|
+
constraints.all? { |method, value|
|
101
149
|
case value
|
102
150
|
when Regexp, String
|
103
151
|
value === request.send(method).to_s
|
@@ -110,16 +158,30 @@ module ActionDispatch
|
|
110
158
|
else
|
111
159
|
value === request.send(method)
|
112
160
|
end
|
113
|
-
|
161
|
+
}
|
114
162
|
end
|
115
163
|
|
116
164
|
def ip
|
117
165
|
constraints[:ip] || //
|
118
166
|
end
|
119
167
|
|
168
|
+
def requires_matching_verb?
|
169
|
+
!@request_method_match.all? { |x| x == VerbMatchers::All }
|
170
|
+
end
|
171
|
+
|
120
172
|
def verb
|
121
|
-
|
173
|
+
verbs.join('|')
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
def verbs
|
178
|
+
@request_method_match.map(&:verb)
|
179
|
+
end
|
180
|
+
|
181
|
+
def match_verb(request)
|
182
|
+
@request_method_match.any? { |m| m.call request }
|
122
183
|
end
|
123
184
|
end
|
124
185
|
end
|
186
|
+
# :startdoc:
|
125
187
|
end
|
@@ -13,11 +13,11 @@ module ActionDispatch
|
|
13
13
|
# normalize_path("") # => "/"
|
14
14
|
# normalize_path("/%ab") # => "/%AB"
|
15
15
|
def self.normalize_path(path)
|
16
|
-
path = "/#{path}"
|
17
|
-
path.squeeze!('/')
|
18
|
-
path.sub!(%r{/+\Z}, '')
|
16
|
+
path = "/#{path}"
|
17
|
+
path.squeeze!('/'.freeze)
|
18
|
+
path.sub!(%r{/+\Z}, ''.freeze)
|
19
19
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
20
|
-
path = '/' if path == ''
|
20
|
+
path = '/' if path == ''.freeze
|
21
21
|
path
|
22
22
|
end
|
23
23
|
|
@@ -55,7 +55,7 @@ module ActionDispatch
|
|
55
55
|
|
56
56
|
def unescape_uri(uri)
|
57
57
|
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
|
58
|
-
uri.gsub(ESCAPED) { [
|
58
|
+
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack('C') }.force_encoding(encoding)
|
59
59
|
end
|
60
60
|
|
61
61
|
protected
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'action_dispatch/journey/router/utils'
|
2
|
-
require 'action_dispatch/journey/router/strexp'
|
3
2
|
require 'action_dispatch/journey/routes'
|
4
3
|
require 'action_dispatch/journey/formatter'
|
5
4
|
|
@@ -17,9 +16,6 @@ module ActionDispatch
|
|
17
16
|
class RoutingError < ::StandardError # :nodoc:
|
18
17
|
end
|
19
18
|
|
20
|
-
# :nodoc:
|
21
|
-
VERSION = '2.0.0'
|
22
|
-
|
23
19
|
attr_accessor :routes
|
24
20
|
|
25
21
|
def initialize(routes)
|
@@ -68,15 +64,17 @@ module ActionDispatch
|
|
68
64
|
|
69
65
|
def visualizer
|
70
66
|
tt = GTG::Builder.new(ast).transition_table
|
71
|
-
groups = partitioned_routes.first.map(&:ast).group_by
|
72
|
-
asts = groups.values.map
|
67
|
+
groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
|
68
|
+
asts = groups.values.map(&:first)
|
73
69
|
tt.visualizer(asts)
|
74
70
|
end
|
75
71
|
|
76
72
|
private
|
77
73
|
|
78
74
|
def partitioned_routes
|
79
|
-
routes.
|
75
|
+
routes.partition { |r|
|
76
|
+
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
|
77
|
+
}
|
80
78
|
end
|
81
79
|
|
82
80
|
def ast
|
@@ -88,7 +86,7 @@ module ActionDispatch
|
|
88
86
|
end
|
89
87
|
|
90
88
|
def custom_routes
|
91
|
-
|
89
|
+
routes.custom_routes
|
92
90
|
end
|
93
91
|
|
94
92
|
def filter_routes(path)
|
@@ -102,7 +100,7 @@ module ActionDispatch
|
|
102
100
|
}
|
103
101
|
|
104
102
|
routes =
|
105
|
-
if req.
|
103
|
+
if req.head?
|
106
104
|
match_head_routes(routes, req)
|
107
105
|
else
|
108
106
|
match_routes(routes, req)
|
@@ -121,7 +119,7 @@ module ActionDispatch
|
|
121
119
|
end
|
122
120
|
|
123
121
|
def match_head_routes(routes, req)
|
124
|
-
verb_specific_routes = routes.
|
122
|
+
verb_specific_routes = routes.select(&:requires_matching_verb?)
|
125
123
|
head_routes = match_routes(verb_specific_routes, req)
|
126
124
|
|
127
125
|
if head_routes.empty?
|
@@ -5,13 +5,13 @@ module ActionDispatch
|
|
5
5
|
class Routes # :nodoc:
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
attr_reader :routes, :
|
8
|
+
attr_reader :routes, :custom_routes, :anchored_routes
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@routes = []
|
12
|
-
@named_routes = {}
|
13
12
|
@ast = nil
|
14
|
-
@
|
13
|
+
@anchored_routes = []
|
14
|
+
@custom_routes = []
|
15
15
|
@simulator = nil
|
16
16
|
end
|
17
17
|
|
@@ -34,18 +34,21 @@ module ActionDispatch
|
|
34
34
|
|
35
35
|
def clear
|
36
36
|
routes.clear
|
37
|
-
|
37
|
+
anchored_routes.clear
|
38
|
+
custom_routes.clear
|
38
39
|
end
|
39
40
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
41
|
+
def partition_route(route)
|
42
|
+
if route.path.anchored && route.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
|
43
|
+
anchored_routes << route
|
44
|
+
else
|
45
|
+
custom_routes << route
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
49
|
def ast
|
47
50
|
@ast ||= begin
|
48
|
-
asts =
|
51
|
+
asts = anchored_routes.map(&:ast)
|
49
52
|
Nodes::Or.new(asts) unless asts.empty?
|
50
53
|
end
|
51
54
|
end
|
@@ -57,13 +60,10 @@ module ActionDispatch
|
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
route = Route.new(name, app, path, conditions, defaults)
|
63
|
-
|
64
|
-
route.precedence = routes.length
|
63
|
+
def add_route(name, mapping)
|
64
|
+
route = mapping.make_route name, routes.length
|
65
65
|
routes << route
|
66
|
-
|
66
|
+
partition_route(route)
|
67
67
|
clear_cache!
|
68
68
|
route
|
69
69
|
end
|
@@ -72,7 +72,6 @@ module ActionDispatch
|
|
72
72
|
|
73
73
|
def clear_cache!
|
74
74
|
@ast = nil
|
75
|
-
@partitioned_routes = nil
|
76
75
|
@simulator = nil
|
77
76
|
end
|
78
77
|
end
|
@@ -1,7 +1,6 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module ActionDispatch
|
4
|
-
|
2
|
+
# :stopdoc:
|
3
|
+
module Journey
|
5
4
|
class Format
|
6
5
|
ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
|
7
6
|
ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
|
@@ -92,6 +91,45 @@ module ActionDispatch
|
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
94
|
+
class FunctionalVisitor # :nodoc:
|
95
|
+
DISPATCH_CACHE = {}
|
96
|
+
|
97
|
+
def accept(node, seed)
|
98
|
+
visit(node, seed)
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit node, seed
|
102
|
+
send(DISPATCH_CACHE[node.type], node, seed)
|
103
|
+
end
|
104
|
+
|
105
|
+
def binary(node, seed)
|
106
|
+
visit(node.right, visit(node.left, seed))
|
107
|
+
end
|
108
|
+
def visit_CAT(n, seed); binary(n, seed); end
|
109
|
+
|
110
|
+
def nary(node, seed)
|
111
|
+
node.children.inject(seed) { |s, c| visit(c, s) }
|
112
|
+
end
|
113
|
+
def visit_OR(n, seed); nary(n, seed); end
|
114
|
+
|
115
|
+
def unary(node, seed)
|
116
|
+
visit(node.left, seed)
|
117
|
+
end
|
118
|
+
def visit_GROUP(n, seed); unary(n, seed); end
|
119
|
+
def visit_STAR(n, seed); unary(n, seed); end
|
120
|
+
|
121
|
+
def terminal(node, seed); seed; end
|
122
|
+
def visit_LITERAL(n, seed); terminal(n, seed); end
|
123
|
+
def visit_SYMBOL(n, seed); terminal(n, seed); end
|
124
|
+
def visit_SLASH(n, seed); terminal(n, seed); end
|
125
|
+
def visit_DOT(n, seed); terminal(n, seed); end
|
126
|
+
|
127
|
+
instance_methods(false).each do |pim|
|
128
|
+
next unless pim =~ /^visit_(.*)$/
|
129
|
+
DISPATCH_CACHE[$1.to_sym] = pim
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
95
133
|
class FormatBuilder < Visitor # :nodoc:
|
96
134
|
def accept(node); Journey::Format.new(super); end
|
97
135
|
def terminal(node); [node.left]; end
|
@@ -117,105 +155,112 @@ module ActionDispatch
|
|
117
155
|
end
|
118
156
|
|
119
157
|
# Loop through the requirements AST
|
120
|
-
class Each <
|
121
|
-
|
122
|
-
|
123
|
-
def initialize(block)
|
124
|
-
@block = block
|
125
|
-
end
|
126
|
-
|
127
|
-
def visit(node)
|
158
|
+
class Each < FunctionalVisitor # :nodoc:
|
159
|
+
def visit(node, block)
|
128
160
|
block.call(node)
|
129
161
|
super
|
130
162
|
end
|
163
|
+
|
164
|
+
INSTANCE = new
|
131
165
|
end
|
132
166
|
|
133
|
-
class String <
|
167
|
+
class String < FunctionalVisitor # :nodoc:
|
134
168
|
private
|
135
169
|
|
136
|
-
def binary(node)
|
137
|
-
|
170
|
+
def binary(node, seed)
|
171
|
+
visit(node.right, visit(node.left, seed))
|
138
172
|
end
|
139
173
|
|
140
|
-
def nary(node)
|
141
|
-
node.children.
|
174
|
+
def nary(node, seed)
|
175
|
+
last_child = node.children.last
|
176
|
+
node.children.inject(seed) { |s, c|
|
177
|
+
string = visit(c, s)
|
178
|
+
string << "|".freeze unless last_child == c
|
179
|
+
string
|
180
|
+
}
|
142
181
|
end
|
143
182
|
|
144
|
-
def terminal(node)
|
145
|
-
node.left
|
183
|
+
def terminal(node, seed)
|
184
|
+
seed + node.left
|
146
185
|
end
|
147
186
|
|
148
|
-
def visit_GROUP(node)
|
149
|
-
|
187
|
+
def visit_GROUP(node, seed)
|
188
|
+
visit(node.left, seed << "(".freeze) << ")".freeze
|
150
189
|
end
|
190
|
+
|
191
|
+
INSTANCE = new
|
151
192
|
end
|
152
193
|
|
153
|
-
class Dot <
|
194
|
+
class Dot < FunctionalVisitor # :nodoc:
|
154
195
|
def initialize
|
155
196
|
@nodes = []
|
156
197
|
@edges = []
|
157
198
|
end
|
158
199
|
|
159
|
-
def accept(node)
|
200
|
+
def accept(node, seed = [[], []])
|
160
201
|
super
|
202
|
+
nodes, edges = seed
|
161
203
|
<<-eodot
|
162
204
|
digraph parse_tree {
|
163
205
|
size="8,5"
|
164
206
|
node [shape = none];
|
165
207
|
edge [dir = none];
|
166
|
-
#{
|
167
|
-
#{
|
208
|
+
#{nodes.join "\n"}
|
209
|
+
#{edges.join("\n")}
|
168
210
|
}
|
169
211
|
eodot
|
170
212
|
end
|
171
213
|
|
172
214
|
private
|
173
215
|
|
174
|
-
def binary(node)
|
175
|
-
node.children.
|
176
|
-
|
177
|
-
|
216
|
+
def binary(node, seed)
|
217
|
+
seed.last.concat node.children.map { |c|
|
218
|
+
"#{node.object_id} -> #{c.object_id};"
|
219
|
+
}
|
178
220
|
super
|
179
221
|
end
|
180
222
|
|
181
|
-
def nary(node)
|
182
|
-
node.children.
|
183
|
-
|
184
|
-
|
223
|
+
def nary(node, seed)
|
224
|
+
seed.last.concat node.children.map { |c|
|
225
|
+
"#{node.object_id} -> #{c.object_id};"
|
226
|
+
}
|
185
227
|
super
|
186
228
|
end
|
187
229
|
|
188
|
-
def unary(node)
|
189
|
-
|
230
|
+
def unary(node, seed)
|
231
|
+
seed.last << "#{node.object_id} -> #{node.left.object_id};"
|
190
232
|
super
|
191
233
|
end
|
192
234
|
|
193
|
-
def visit_GROUP(node)
|
194
|
-
|
235
|
+
def visit_GROUP(node, seed)
|
236
|
+
seed.first << "#{node.object_id} [label=\"()\"];"
|
195
237
|
super
|
196
238
|
end
|
197
239
|
|
198
|
-
def visit_CAT(node)
|
199
|
-
|
240
|
+
def visit_CAT(node, seed)
|
241
|
+
seed.first << "#{node.object_id} [label=\"○\"];"
|
200
242
|
super
|
201
243
|
end
|
202
244
|
|
203
|
-
def visit_STAR(node)
|
204
|
-
|
245
|
+
def visit_STAR(node, seed)
|
246
|
+
seed.first << "#{node.object_id} [label=\"*\"];"
|
205
247
|
super
|
206
248
|
end
|
207
249
|
|
208
|
-
def visit_OR(node)
|
209
|
-
|
250
|
+
def visit_OR(node, seed)
|
251
|
+
seed.first << "#{node.object_id} [label=\"|\"];"
|
210
252
|
super
|
211
253
|
end
|
212
254
|
|
213
|
-
def terminal(node)
|
255
|
+
def terminal(node, seed)
|
214
256
|
value = node.left
|
215
257
|
|
216
|
-
|
258
|
+
seed.first << "#{node.object_id} [label=\"#{value}\"];"
|
259
|
+
seed
|
217
260
|
end
|
261
|
+
INSTANCE = new
|
218
262
|
end
|
219
263
|
end
|
220
264
|
end
|
265
|
+
# :startdoc:
|
221
266
|
end
|