actionpack 4.2.10 → 7.2.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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- 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/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +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
@@ -1,11 +1,15 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "action_controller/metal/exceptions"
|
3
6
|
|
4
7
|
module ActionDispatch
|
8
|
+
# :stopdoc:
|
5
9
|
module Journey
|
6
10
|
# The Formatter class is used for formatting URLs. For example, parameters
|
7
|
-
# passed to
|
8
|
-
class Formatter
|
11
|
+
# passed to `url_for` in Rails will eventually call Formatter#generate.
|
12
|
+
class Formatter
|
9
13
|
attr_reader :routes
|
10
14
|
|
11
15
|
def initialize(routes)
|
@@ -13,63 +17,121 @@ module ActionDispatch
|
|
13
17
|
@cache = nil
|
14
18
|
end
|
15
19
|
|
16
|
-
|
20
|
+
class RouteWithParams
|
21
|
+
attr_reader :params
|
22
|
+
|
23
|
+
def initialize(route, parameterized_parts, params)
|
24
|
+
@route = route
|
25
|
+
@parameterized_parts = parameterized_parts
|
26
|
+
@params = params
|
27
|
+
end
|
28
|
+
|
29
|
+
def path(_)
|
30
|
+
@route.format(@parameterized_parts)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class MissingRoute
|
35
|
+
attr_reader :routes, :name, :constraints, :missing_keys, :unmatched_keys
|
36
|
+
|
37
|
+
def initialize(constraints, missing_keys, unmatched_keys, routes, name)
|
38
|
+
@constraints = constraints
|
39
|
+
@missing_keys = missing_keys
|
40
|
+
@unmatched_keys = unmatched_keys
|
41
|
+
@routes = routes
|
42
|
+
@name = name
|
43
|
+
end
|
44
|
+
|
45
|
+
def path(method_name)
|
46
|
+
raise ActionController::UrlGenerationError.new(message, routes, name, method_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def params
|
50
|
+
path("unknown")
|
51
|
+
end
|
52
|
+
|
53
|
+
def message
|
54
|
+
message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
|
55
|
+
message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
|
56
|
+
message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
|
57
|
+
message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate(name, options, path_parameters)
|
62
|
+
original_options = options.dup
|
63
|
+
path_params = options.delete(:path_params) || {}
|
64
|
+
options = path_params.merge(options)
|
17
65
|
constraints = path_parameters.merge(options)
|
18
|
-
missing_keys =
|
66
|
+
missing_keys = nil
|
19
67
|
|
20
68
|
match_route(name, constraints) do |route|
|
21
|
-
parameterized_parts = extract_parameterized_parts(route, options, path_parameters
|
69
|
+
parameterized_parts = extract_parameterized_parts(route, options, path_parameters)
|
22
70
|
|
23
|
-
# Skip this route unless a name has been provided or it is a
|
24
|
-
#
|
25
|
-
#
|
71
|
+
# Skip this route unless a name has been provided or it is a standard Rails
|
72
|
+
# route since we can't determine whether an options hash passed to url_for
|
73
|
+
# matches a Rack application or a redirect.
|
26
74
|
next unless name || route.dispatcher?
|
27
75
|
|
28
76
|
missing_keys = missing_keys(route, parameterized_parts)
|
29
|
-
next
|
30
|
-
params = options.
|
31
|
-
|
77
|
+
next if missing_keys && !missing_keys.empty?
|
78
|
+
params = options.delete_if do |key, _|
|
79
|
+
# top-level params' normal behavior of generating query_params should be
|
80
|
+
# preserved even if the same key is also a bind_param
|
81
|
+
parameterized_parts.key?(key) || route.defaults.key?(key) ||
|
82
|
+
(path_params.key?(key) && !original_options.key?(key))
|
32
83
|
end
|
33
84
|
|
34
85
|
defaults = route.defaults
|
35
86
|
required_parts = route.required_parts
|
36
|
-
|
37
|
-
|
87
|
+
|
88
|
+
route.parts.reverse_each do |key|
|
89
|
+
break if defaults[key].nil? && parameterized_parts[key].present?
|
90
|
+
next if parameterized_parts[key].to_s != defaults[key].to_s
|
91
|
+
break if required_parts.include?(key)
|
92
|
+
|
93
|
+
parameterized_parts.delete(key)
|
38
94
|
end
|
39
95
|
|
40
|
-
return
|
96
|
+
return RouteWithParams.new(route, parameterized_parts, params)
|
41
97
|
end
|
42
98
|
|
43
|
-
|
44
|
-
|
99
|
+
unmatched_keys = (missing_keys || []) & constraints.keys
|
100
|
+
missing_keys = (missing_keys || []) - unmatched_keys
|
45
101
|
|
46
|
-
|
102
|
+
MissingRoute.new(constraints, missing_keys, unmatched_keys, routes, name)
|
47
103
|
end
|
48
104
|
|
49
105
|
def clear
|
50
106
|
@cache = nil
|
51
107
|
end
|
52
108
|
|
53
|
-
|
109
|
+
def eager_load!
|
110
|
+
cache
|
111
|
+
nil
|
112
|
+
end
|
54
113
|
|
55
|
-
|
114
|
+
private
|
115
|
+
def extract_parameterized_parts(route, options, recall)
|
56
116
|
parameterized_parts = recall.merge(options)
|
57
117
|
|
58
|
-
keys_to_keep = route.parts.
|
59
|
-
!options.key?(part) || (options[part]
|
118
|
+
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
119
|
+
!(options.key?(part) || route.scope_options.key?(part)) || (options[part].nil? && recall[part].nil?)
|
60
120
|
} | route.required_parts
|
61
121
|
|
62
|
-
|
63
|
-
|
122
|
+
parameterized_parts.delete_if do |bad_key, _|
|
123
|
+
!keys_to_keep.include?(bad_key)
|
64
124
|
end
|
65
125
|
|
66
|
-
|
67
|
-
|
68
|
-
parameterized_parts[k] =
|
126
|
+
parameterized_parts.each do |k, v|
|
127
|
+
if k == :controller
|
128
|
+
parameterized_parts[k] = v
|
129
|
+
else
|
130
|
+
parameterized_parts[k] = v.to_param
|
69
131
|
end
|
70
132
|
end
|
71
133
|
|
72
|
-
parameterized_parts.
|
134
|
+
parameterized_parts.compact!
|
73
135
|
parameterized_parts
|
74
136
|
end
|
75
137
|
|
@@ -81,28 +143,18 @@ module ActionDispatch
|
|
81
143
|
if named_routes.key?(name)
|
82
144
|
yield named_routes[name]
|
83
145
|
else
|
84
|
-
# Make sure we don't show the deprecation warning more than once
|
85
|
-
warned = false
|
86
|
-
|
87
146
|
routes = non_recursive(cache, options)
|
88
147
|
|
89
|
-
|
148
|
+
supplied_keys = options.each_with_object({}) do |(k, v), h|
|
149
|
+
h[k.to_s] = true if v
|
150
|
+
end
|
151
|
+
|
152
|
+
hash = routes.group_by { |_, r| r.score(supplied_keys) }
|
90
153
|
|
91
154
|
hash.keys.sort.reverse_each do |score|
|
92
155
|
break if score < 0
|
93
156
|
|
94
157
|
hash[score].sort_by { |i, _| i }.each do |_, route|
|
95
|
-
if name && !warned
|
96
|
-
ActiveSupport::Deprecation.warn <<-MSG.squish
|
97
|
-
You are trying to generate the URL for a named route called
|
98
|
-
#{name.inspect} but no such route was found. In the future,
|
99
|
-
this will result in an `ActionController::UrlGenerationError`
|
100
|
-
exception.
|
101
|
-
MSG
|
102
|
-
|
103
|
-
warned = true
|
104
|
-
end
|
105
|
-
|
106
158
|
yield route
|
107
159
|
end
|
108
160
|
end
|
@@ -127,13 +179,20 @@ module ActionDispatch
|
|
127
179
|
|
128
180
|
# Returns an array populated with missing keys if any are present.
|
129
181
|
def missing_keys(route, parts)
|
130
|
-
missing_keys =
|
131
|
-
tests = route.path.
|
182
|
+
missing_keys = nil
|
183
|
+
tests = route.path.requirements_for_missing_keys_check
|
132
184
|
route.required_parts.each { |key|
|
133
|
-
|
134
|
-
|
185
|
+
case tests[key]
|
186
|
+
when nil
|
187
|
+
unless parts[key]
|
188
|
+
missing_keys ||= []
|
189
|
+
missing_keys << key
|
190
|
+
end
|
135
191
|
else
|
136
|
-
|
192
|
+
unless tests[key].match?(parts[key])
|
193
|
+
missing_keys ||= []
|
194
|
+
missing_keys << key
|
195
|
+
end
|
137
196
|
end
|
138
197
|
}
|
139
198
|
missing_keys
|
@@ -149,7 +208,7 @@ module ActionDispatch
|
|
149
208
|
|
150
209
|
def build_cache
|
151
210
|
root = { ___routes: [] }
|
152
|
-
routes.each_with_index do |route, i|
|
211
|
+
routes.routes.each_with_index do |route, i|
|
153
212
|
leaf = route.required_defaults.inject(root) do |h, tuple|
|
154
213
|
h[tuple] ||= {}
|
155
214
|
end
|
@@ -163,4 +222,5 @@ module ActionDispatch
|
|
163
222
|
end
|
164
223
|
end
|
165
224
|
end
|
225
|
+
# :startdoc:
|
166
226
|
end
|
@@ -1,55 +1,58 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "action_dispatch/journey/gtg/transition_table"
|
2
6
|
|
3
7
|
module ActionDispatch
|
4
8
|
module Journey # :nodoc:
|
5
9
|
module GTG # :nodoc:
|
6
10
|
class Builder # :nodoc:
|
7
|
-
|
11
|
+
DUMMY_END_NODE = Nodes::Dummy.new
|
8
12
|
|
9
13
|
attr_reader :root, :ast, :endpoints
|
10
14
|
|
11
15
|
def initialize(root)
|
12
16
|
@root = root
|
13
|
-
@ast = Nodes::Cat.new root,
|
14
|
-
@followpos =
|
17
|
+
@ast = Nodes::Cat.new root, DUMMY_END_NODE
|
18
|
+
@followpos = build_followpos
|
15
19
|
end
|
16
20
|
|
17
21
|
def transition_table
|
18
22
|
dtrans = TransitionTable.new
|
19
|
-
marked = {}
|
20
|
-
state_id = Hash.new { |h,k| h[k] = h.length }
|
23
|
+
marked = {}.compare_by_identity
|
24
|
+
state_id = Hash.new { |h, k| h[k] = h.length }.compare_by_identity
|
25
|
+
dstates = [firstpos(root)]
|
21
26
|
|
22
|
-
start = firstpos(root)
|
23
|
-
dstates = [start]
|
24
27
|
until dstates.empty?
|
25
28
|
s = dstates.shift
|
26
29
|
next if marked[s]
|
27
30
|
marked[s] = true # mark s
|
28
31
|
|
29
32
|
s.group_by { |state| symbol(state) }.each do |sym, ps|
|
30
|
-
u = ps.flat_map { |l| followpos
|
33
|
+
u = ps.flat_map { |l| @followpos[l] }.uniq
|
31
34
|
next if u.empty?
|
32
35
|
|
33
|
-
|
34
|
-
from = state_id[s]
|
35
|
-
to = state_id[Object.new]
|
36
|
-
dtrans[from, to] = sym
|
36
|
+
from = state_id[s]
|
37
37
|
|
38
|
+
if u.all? { |pos| pos == DUMMY_END_NODE }
|
39
|
+
to = state_id[Object.new]
|
40
|
+
dtrans[from, to] = sym
|
38
41
|
dtrans.add_accepting(to)
|
42
|
+
|
39
43
|
ps.each { |state| dtrans.add_memo(to, state.memo) }
|
40
44
|
else
|
41
|
-
|
42
|
-
|
43
|
-
if u.include?(DUMMY)
|
44
|
-
to = state_id[u]
|
45
|
-
|
46
|
-
accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
|
45
|
+
to = state_id[u]
|
46
|
+
dtrans[from, to] = sym
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
if u.include?(DUMMY_END_NODE)
|
49
|
+
ps.each do |state|
|
50
|
+
if @followpos[state].include?(DUMMY_END_NODE)
|
51
|
+
dtrans.add_memo(to, state.memo)
|
52
|
+
end
|
53
|
+
end
|
51
54
|
|
52
|
-
dtrans.add_accepting(
|
55
|
+
dtrans.add_accepting(to)
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
@@ -65,7 +68,9 @@ module ActionDispatch
|
|
65
68
|
when Nodes::Group
|
66
69
|
true
|
67
70
|
when Nodes::Star
|
68
|
-
|
71
|
+
# the default star regex is /(.+)/ which is NOT nullable but since different
|
72
|
+
# constraints can be provided we must actually check if this is the case or not.
|
73
|
+
node.regexp.match?("")
|
69
74
|
when Nodes::Or
|
70
75
|
node.children.any? { |c| nullable?(c) }
|
71
76
|
when Nodes::Cat
|
@@ -75,7 +80,7 @@ module ActionDispatch
|
|
75
80
|
when Nodes::Unary
|
76
81
|
nullable?(node.left)
|
77
82
|
else
|
78
|
-
raise ArgumentError,
|
83
|
+
raise ArgumentError, "unknown nullable: %s" % node.class.name
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
@@ -90,22 +95,22 @@ module ActionDispatch
|
|
90
95
|
firstpos(node.left)
|
91
96
|
end
|
92
97
|
when Nodes::Or
|
93
|
-
node.children.flat_map { |c| firstpos(c) }.uniq
|
98
|
+
node.children.flat_map { |c| firstpos(c) }.tap(&:uniq!)
|
94
99
|
when Nodes::Unary
|
95
100
|
firstpos(node.left)
|
96
101
|
when Nodes::Terminal
|
97
102
|
nullable?(node) ? [] : [node]
|
98
103
|
else
|
99
|
-
raise ArgumentError,
|
104
|
+
raise ArgumentError, "unknown firstpos: %s" % node.class.name
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
103
108
|
def lastpos(node)
|
104
109
|
case node
|
105
110
|
when Nodes::Star
|
106
|
-
|
111
|
+
lastpos(node.left)
|
107
112
|
when Nodes::Or
|
108
|
-
node.children.flat_map { |c| lastpos(c) }.uniq
|
113
|
+
node.children.flat_map { |c| lastpos(c) }.tap(&:uniq!)
|
109
114
|
when Nodes::Cat
|
110
115
|
if nullable?(node.right)
|
111
116
|
lastpos(node.left) | lastpos(node.right)
|
@@ -117,44 +122,26 @@ module ActionDispatch
|
|
117
122
|
when Nodes::Unary
|
118
123
|
lastpos(node.left)
|
119
124
|
else
|
120
|
-
raise ArgumentError,
|
125
|
+
raise ArgumentError, "unknown lastpos: %s" % node.class.name
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
124
|
-
def followpos(node)
|
125
|
-
followpos_table[node]
|
126
|
-
end
|
127
|
-
|
128
129
|
private
|
129
|
-
|
130
|
-
def followpos_table
|
131
|
-
@followpos ||= build_followpos
|
132
|
-
end
|
133
|
-
|
134
130
|
def build_followpos
|
135
|
-
table = Hash.new { |h, k| h[k] = [] }
|
131
|
+
table = Hash.new { |h, k| h[k] = [] }.compare_by_identity
|
136
132
|
@ast.each do |n|
|
137
133
|
case n
|
138
134
|
when Nodes::Cat
|
139
135
|
lastpos(n.left).each do |i|
|
140
136
|
table[i] += firstpos(n.right)
|
141
137
|
end
|
142
|
-
when Nodes::Star
|
143
|
-
lastpos(n).each do |i|
|
144
|
-
table[i] += firstpos(n)
|
145
|
-
end
|
146
138
|
end
|
147
139
|
end
|
148
140
|
table
|
149
141
|
end
|
150
142
|
|
151
143
|
def symbol(edge)
|
152
|
-
|
153
|
-
when Journey::Nodes::Symbol
|
154
|
-
edge.regexp
|
155
|
-
else
|
156
|
-
edge.left
|
157
|
-
end
|
144
|
+
edge.symbol? ? edge.regexp : edge.left
|
158
145
|
end
|
159
146
|
end
|
160
147
|
end
|
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "strscan"
|
2
6
|
|
3
7
|
module ActionDispatch
|
4
8
|
module Journey # :nodoc:
|
@@ -12,34 +16,33 @@ module ActionDispatch
|
|
12
16
|
end
|
13
17
|
|
14
18
|
class Simulator # :nodoc:
|
19
|
+
INITIAL_STATE = [ [0, nil] ].freeze
|
20
|
+
|
15
21
|
attr_reader :tt
|
16
22
|
|
17
23
|
def initialize(transition_table)
|
18
24
|
@tt = transition_table
|
19
25
|
end
|
20
26
|
|
21
|
-
def simulate(string)
|
22
|
-
ms = memos(string) { return }
|
23
|
-
MatchData.new(ms)
|
24
|
-
end
|
25
|
-
|
26
|
-
alias :=~ :simulate
|
27
|
-
alias :match :simulate
|
28
|
-
|
29
27
|
def memos(string)
|
30
28
|
input = StringScanner.new(string)
|
31
|
-
state =
|
29
|
+
state = INITIAL_STATE
|
30
|
+
start_index = 0
|
31
|
+
|
32
32
|
while sym = input.scan(%r([/.?]|[^/.?]+))
|
33
|
-
|
34
|
-
end
|
33
|
+
end_index = start_index + sym.length
|
35
34
|
|
36
|
-
|
37
|
-
tt.accepting? s
|
38
|
-
}
|
35
|
+
state = tt.move(state, string, start_index, end_index)
|
39
36
|
|
40
|
-
|
37
|
+
start_index = end_index
|
38
|
+
end
|
39
|
+
|
40
|
+
acceptance_states = state.each_with_object([]) do |s_d, memos|
|
41
|
+
s, idx = s_d
|
42
|
+
memos.concat(tt.memo(s)) if idx.nil? && tt.accepting?(s)
|
43
|
+
end
|
41
44
|
|
42
|
-
acceptance_states.
|
45
|
+
acceptance_states.empty? ? yield : acceptance_states
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|