actionpack 4.2.11.1 → 5.2.4.3
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 +287 -488
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +45 -49
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +47 -31
- data/lib/abstract_controller/collector.rb +8 -11
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +25 -25
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
- data/lib/abstract_controller/rendering.rb +42 -41
- data/lib/abstract_controller/translation.rb +10 -7
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +12 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/base.rb +27 -19
- data/lib/action_controller/caching.rb +14 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +10 -15
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +118 -44
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +3 -3
- data/lib/action_controller/metal/data_streaming.rb +27 -46
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
- data/lib/action_controller/metal/exceptions.rb +8 -14
- data/lib/action_controller/metal/flash.rb +4 -3
- data/lib/action_controller/metal/force_ssl.rb +23 -21
- data/lib/action_controller/metal/head.rb +21 -19
- data/lib/action_controller/metal/helpers.rb +24 -14
- data/lib/action_controller/metal/http_authentication.rb +64 -57
- data/lib/action_controller/metal/implicit_render.rb +62 -8
- data/lib/action_controller/metal/instrumentation.rb +19 -21
- data/lib/action_controller/metal/live.rb +90 -106
- data/lib/action_controller/metal/mime_responds.rb +33 -46
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +61 -53
- data/lib/action_controller/metal/redirecting.rb +49 -28
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +72 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +229 -93
- data/lib/action_controller/metal/rescue.rb +9 -16
- data/lib/action_controller/metal/streaming.rb +12 -10
- data/lib/action_controller/metal/strong_parameters.rb +583 -164
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +98 -83
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +280 -411
- data/lib/action_controller.rb +29 -21
- data/lib/action_dispatch/http/cache.rb +93 -47
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +26 -20
- data/lib/action_dispatch/http/filter_redirect.rb +10 -11
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +56 -41
- data/lib/action_dispatch/http/mime_type.rb +134 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameter_filter.rb +25 -11
- data/lib/action_dispatch/http/parameters.rb +98 -39
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +200 -118
- data/lib/action_dispatch/http/response.rb +225 -110
- data/lib/action_dispatch/http/upload.rb +12 -6
- data/lib/action_dispatch/http/url.rb +110 -28
- data/lib/action_dispatch/journey/formatter.rb +55 -32
- data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
- data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
- data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
- data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
- data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
- data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
- data/lib/action_dispatch/journey/nodes/node.rb +18 -6
- data/lib/action_dispatch/journey/parser.rb +23 -22
- data/lib/action_dispatch/journey/parser.y +3 -2
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +50 -44
- data/lib/action_dispatch/journey/route.rb +106 -28
- data/lib/action_dispatch/journey/router/utils.rb +20 -11
- data/lib/action_dispatch/journey/router.rb +35 -23
- data/lib/action_dispatch/journey/routes.rb +18 -16
- data/lib/action_dispatch/journey/scanner.rb +18 -15
- data/lib/action_dispatch/journey/visitors.rb +99 -52
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +304 -193
- data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
- data/lib/action_dispatch/middleware/request_id.rb +17 -9
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
- data/lib/action_dispatch/middleware/ssl.rb +114 -36
- data/lib/action_dispatch/middleware/stack.rb +31 -44
- data/lib/action_dispatch/middleware/static.rb +57 -50
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -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 +64 -64
- data/lib/action_dispatch/railtie.rb +19 -11
- data/lib/action_dispatch/request/session.rb +106 -59
- data/lib/action_dispatch/request/utils.rb +67 -24
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +58 -67
- data/lib/action_dispatch/routing/mapper.rb +733 -447
- data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
- data/lib/action_dispatch/routing/redirection.rb +36 -26
- data/lib/action_dispatch/routing/route_set.rb +321 -291
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +65 -25
- data/lib/action_dispatch/routing.rb +17 -18
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions/response.rb +45 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +347 -209
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +28 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +35 -7
- data/lib/action_dispatch.rb +27 -19
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +56 -38
- 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/middleware/params_parser.rb +0 -60
- 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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
module Journey # :nodoc:
|
3
5
|
class Router # :nodoc:
|
@@ -5,7 +7,7 @@ module ActionDispatch
|
|
5
7
|
# Normalizes URI path.
|
6
8
|
#
|
7
9
|
# Strips off trailing slash and ensures there is a leading slash.
|
8
|
-
# Also converts downcase
|
10
|
+
# Also converts downcase URL encoded string to uppercase.
|
9
11
|
#
|
10
12
|
# normalize_path("/foo") # => "/foo"
|
11
13
|
# normalize_path("/foo/") # => "/foo"
|
@@ -13,22 +15,25 @@ module ActionDispatch
|
|
13
15
|
# normalize_path("") # => "/"
|
14
16
|
# normalize_path("/%ab") # => "/%AB"
|
15
17
|
def self.normalize_path(path)
|
16
|
-
path
|
17
|
-
path.
|
18
|
-
path
|
18
|
+
path ||= ""
|
19
|
+
encoding = path.encoding
|
20
|
+
path = "/#{path}".dup
|
21
|
+
path.squeeze!("/".freeze)
|
22
|
+
path.sub!(%r{/+\Z}, "".freeze)
|
19
23
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
20
|
-
path =
|
24
|
+
path = "/".dup if path == "".freeze
|
25
|
+
path.force_encoding(encoding)
|
21
26
|
path
|
22
27
|
end
|
23
28
|
|
24
29
|
# URI path and fragment escaping
|
25
|
-
#
|
30
|
+
# https://tools.ietf.org/html/rfc3986
|
26
31
|
class UriEncoder # :nodoc:
|
27
32
|
ENCODE = "%%%02X".freeze
|
28
33
|
US_ASCII = Encoding::US_ASCII
|
29
34
|
UTF_8 = Encoding::UTF_8
|
30
|
-
EMPTY = "".force_encoding(US_ASCII).freeze
|
31
|
-
DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
|
35
|
+
EMPTY = "".dup.force_encoding(US_ASCII).freeze
|
36
|
+
DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
|
32
37
|
|
33
38
|
ALPHA = "a-zA-Z".freeze
|
34
39
|
DIGIT = "0-9".freeze
|
@@ -55,12 +60,12 @@ module ActionDispatch
|
|
55
60
|
|
56
61
|
def unescape_uri(uri)
|
57
62
|
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
|
58
|
-
uri.gsub(ESCAPED) { [
|
63
|
+
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
|
59
64
|
end
|
60
65
|
|
61
|
-
|
66
|
+
private
|
62
67
|
def escape(component, pattern)
|
63
|
-
component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
|
68
|
+
component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
|
64
69
|
end
|
65
70
|
|
66
71
|
def percent_encode(unsafe)
|
@@ -84,6 +89,10 @@ module ActionDispatch
|
|
84
89
|
ENCODER.escape_fragment(fragment.to_s)
|
85
90
|
end
|
86
91
|
|
92
|
+
# Replaces any escaped sequences with their unescaped representations.
|
93
|
+
#
|
94
|
+
# uri = "/topics?title=Ruby%20on%20Rails"
|
95
|
+
# unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
|
87
96
|
def self.unescape_uri(uri)
|
88
97
|
ENCODER.unescape_uri(uri)
|
89
98
|
end
|
@@ -1,15 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch/journey/router/utils"
|
4
|
+
require "action_dispatch/journey/routes"
|
5
|
+
require "action_dispatch/journey/formatter"
|
5
6
|
|
6
7
|
before = $-w
|
7
8
|
$-w = false
|
8
|
-
require
|
9
|
+
require "action_dispatch/journey/parser"
|
9
10
|
$-w = before
|
10
11
|
|
11
|
-
require
|
12
|
-
require
|
12
|
+
require "action_dispatch/journey/route"
|
13
|
+
require "action_dispatch/journey/path/pattern"
|
13
14
|
|
14
15
|
module ActionDispatch
|
15
16
|
module Journey # :nodoc:
|
@@ -17,15 +18,19 @@ module ActionDispatch
|
|
17
18
|
class RoutingError < ::StandardError # :nodoc:
|
18
19
|
end
|
19
20
|
|
20
|
-
# :nodoc:
|
21
|
-
VERSION = '2.0.0'
|
22
|
-
|
23
21
|
attr_accessor :routes
|
24
22
|
|
25
23
|
def initialize(routes)
|
26
24
|
@routes = routes
|
27
25
|
end
|
28
26
|
|
27
|
+
def eager_load!
|
28
|
+
# Eagerly trigger the simulator's initialization so
|
29
|
+
# it doesn't happen during a request cycle.
|
30
|
+
simulator
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
29
34
|
def serve(req)
|
30
35
|
find_routes(req).each do |match, parameters, route|
|
31
36
|
set_params = req.path_parameters
|
@@ -33,16 +38,20 @@ module ActionDispatch
|
|
33
38
|
script_name = req.script_name
|
34
39
|
|
35
40
|
unless route.path.anchored
|
36
|
-
req.script_name = (script_name.to_s + match.to_s).chomp(
|
41
|
+
req.script_name = (script_name.to_s + match.to_s).chomp("/")
|
37
42
|
req.path_info = match.post_match
|
38
43
|
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
39
44
|
end
|
40
45
|
|
46
|
+
parameters = route.defaults.merge parameters.transform_values { |val|
|
47
|
+
val.dup.force_encoding(::Encoding::UTF_8)
|
48
|
+
}
|
49
|
+
|
41
50
|
req.path_parameters = set_params.merge parameters
|
42
51
|
|
43
52
|
status, headers, body = route.app.serve(req)
|
44
53
|
|
45
|
-
if
|
54
|
+
if "pass" == headers["X-Cascade"]
|
46
55
|
req.script_name = script_name
|
47
56
|
req.path_info = path_info
|
48
57
|
req.path_parameters = set_params
|
@@ -52,7 +61,7 @@ module ActionDispatch
|
|
52
61
|
return [status, headers, body]
|
53
62
|
end
|
54
63
|
|
55
|
-
|
64
|
+
[404, { "X-Cascade" => "pass" }, ["Not Found"]]
|
56
65
|
end
|
57
66
|
|
58
67
|
def recognize(rails_req)
|
@@ -62,21 +71,24 @@ module ActionDispatch
|
|
62
71
|
rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
|
63
72
|
end
|
64
73
|
|
74
|
+
parameters = route.defaults.merge parameters
|
65
75
|
yield(route, parameters)
|
66
76
|
end
|
67
77
|
end
|
68
78
|
|
69
79
|
def visualizer
|
70
80
|
tt = GTG::Builder.new(ast).transition_table
|
71
|
-
groups = partitioned_routes.first.map(&:ast).group_by
|
72
|
-
asts = groups.values.map
|
81
|
+
groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
|
82
|
+
asts = groups.values.map(&:first)
|
73
83
|
tt.visualizer(asts)
|
74
84
|
end
|
75
85
|
|
76
86
|
private
|
77
87
|
|
78
88
|
def partitioned_routes
|
79
|
-
routes.
|
89
|
+
routes.partition { |r|
|
90
|
+
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
|
91
|
+
}
|
80
92
|
end
|
81
93
|
|
82
94
|
def ast
|
@@ -88,7 +100,7 @@ module ActionDispatch
|
|
88
100
|
end
|
89
101
|
|
90
102
|
def custom_routes
|
91
|
-
|
103
|
+
routes.custom_routes
|
92
104
|
end
|
93
105
|
|
94
106
|
def filter_routes(path)
|
@@ -96,13 +108,13 @@ module ActionDispatch
|
|
96
108
|
simulator.memos(path) { [] }
|
97
109
|
end
|
98
110
|
|
99
|
-
def find_routes
|
111
|
+
def find_routes(req)
|
100
112
|
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
|
101
113
|
r.path.match(req.path_info)
|
102
114
|
}
|
103
115
|
|
104
116
|
routes =
|
105
|
-
if req.
|
117
|
+
if req.head?
|
106
118
|
match_head_routes(routes, req)
|
107
119
|
else
|
108
120
|
match_routes(routes, req)
|
@@ -111,9 +123,9 @@ module ActionDispatch
|
|
111
123
|
routes.sort_by!(&:precedence)
|
112
124
|
|
113
125
|
routes.map! { |r|
|
114
|
-
match_data
|
115
|
-
path_parameters =
|
116
|
-
match_data.names.zip(match_data.captures) { |name,val|
|
126
|
+
match_data = r.path.match(req.path_info)
|
127
|
+
path_parameters = {}
|
128
|
+
match_data.names.zip(match_data.captures) { |name, val|
|
117
129
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
118
130
|
}
|
119
131
|
[match_data, path_parameters, r]
|
@@ -121,7 +133,7 @@ module ActionDispatch
|
|
121
133
|
end
|
122
134
|
|
123
135
|
def match_head_routes(routes, req)
|
124
|
-
verb_specific_routes = routes.
|
136
|
+
verb_specific_routes = routes.select(&:requires_matching_verb?)
|
125
137
|
head_routes = match_routes(verb_specific_routes, req)
|
126
138
|
|
127
139
|
if head_routes.empty?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
module Journey # :nodoc:
|
3
5
|
# The Routing table. Contains all routes for a system. Routes can be
|
@@ -5,13 +7,13 @@ module ActionDispatch
|
|
5
7
|
class Routes # :nodoc:
|
6
8
|
include Enumerable
|
7
9
|
|
8
|
-
attr_reader :routes, :
|
10
|
+
attr_reader :routes, :custom_routes, :anchored_routes
|
9
11
|
|
10
12
|
def initialize
|
11
13
|
@routes = []
|
12
|
-
@named_routes = {}
|
13
14
|
@ast = nil
|
14
|
-
@
|
15
|
+
@anchored_routes = []
|
16
|
+
@custom_routes = []
|
15
17
|
@simulator = nil
|
16
18
|
end
|
17
19
|
|
@@ -34,36 +36,37 @@ module ActionDispatch
|
|
34
36
|
|
35
37
|
def clear
|
36
38
|
routes.clear
|
37
|
-
|
39
|
+
anchored_routes.clear
|
40
|
+
custom_routes.clear
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
+
def partition_route(route)
|
44
|
+
if route.path.anchored && route.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
|
45
|
+
anchored_routes << route
|
46
|
+
else
|
47
|
+
custom_routes << route
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
46
51
|
def ast
|
47
52
|
@ast ||= begin
|
48
|
-
asts =
|
49
|
-
Nodes::Or.new(asts)
|
53
|
+
asts = anchored_routes.map(&:ast)
|
54
|
+
Nodes::Or.new(asts)
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
53
58
|
def simulator
|
59
|
+
return if ast.nil?
|
54
60
|
@simulator ||= begin
|
55
61
|
gtg = GTG::Builder.new(ast).transition_table
|
56
62
|
GTG::Simulator.new(gtg)
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
route = Route.new(name, app, path, conditions, defaults)
|
63
|
-
|
64
|
-
route.precedence = routes.length
|
66
|
+
def add_route(name, mapping)
|
67
|
+
route = mapping.make_route name, routes.length
|
65
68
|
routes << route
|
66
|
-
|
69
|
+
partition_route(route)
|
67
70
|
clear_cache!
|
68
71
|
route
|
69
72
|
end
|
@@ -72,7 +75,6 @@ module ActionDispatch
|
|
72
75
|
|
73
76
|
def clear_cache!
|
74
77
|
@ast = nil
|
75
|
-
@partitioned_routes = nil
|
76
78
|
@simulator = nil
|
77
79
|
end
|
78
80
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
2
4
|
|
3
5
|
module ActionDispatch
|
4
6
|
module Journey # :nodoc:
|
@@ -35,22 +37,23 @@ module ActionDispatch
|
|
35
37
|
def scan
|
36
38
|
case
|
37
39
|
# /
|
38
|
-
when
|
39
|
-
[:SLASH,
|
40
|
+
when @ss.skip(/\//)
|
41
|
+
[:SLASH, "/"]
|
42
|
+
when @ss.skip(/\(/)
|
43
|
+
[:LPAREN, "("]
|
44
|
+
when @ss.skip(/\)/)
|
45
|
+
[:RPAREN, ")"]
|
46
|
+
when @ss.skip(/\|/)
|
47
|
+
[:OR, "|"]
|
48
|
+
when @ss.skip(/\./)
|
49
|
+
[:DOT, "."]
|
50
|
+
when text = @ss.scan(/:\w+/)
|
51
|
+
[:SYMBOL, text]
|
40
52
|
when text = @ss.scan(/\*\w+/)
|
41
53
|
[:STAR, text]
|
42
|
-
when text = @ss.scan(/(
|
43
|
-
|
44
|
-
|
45
|
-
[:RPAREN, text]
|
46
|
-
when text = @ss.scan(/\|/)
|
47
|
-
[:OR, text]
|
48
|
-
when text = @ss.scan(/\./)
|
49
|
-
[:DOT, text]
|
50
|
-
when text = @ss.scan(/(?<!\\):\w+/)
|
51
|
-
[:SYMBOL, text]
|
52
|
-
when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/)
|
53
|
-
[:LITERAL, text.tr('\\', '')]
|
54
|
+
when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
|
55
|
+
text.tr! "\\", ""
|
56
|
+
[:LITERAL, text]
|
54
57
|
# any char
|
55
58
|
when text = @ss.scan(/./)
|
56
59
|
[:LITERAL, text]
|
@@ -1,12 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
-
|
4
|
+
# :stopdoc:
|
5
|
+
module Journey
|
5
6
|
class Format
|
6
7
|
ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
|
7
8
|
ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
|
8
9
|
|
9
|
-
|
10
|
+
Parameter = Struct.new(:name, :escaper) do
|
10
11
|
def escape(value); escaper.call value; end
|
11
12
|
end
|
12
13
|
|
@@ -23,7 +24,7 @@ module ActionDispatch
|
|
23
24
|
@children = []
|
24
25
|
@parameters = []
|
25
26
|
|
26
|
-
parts.each_with_index do |object,i|
|
27
|
+
parts.each_with_index do |object, i|
|
27
28
|
case object
|
28
29
|
when Journey::Format
|
29
30
|
@children << i
|
@@ -39,7 +40,7 @@ module ActionDispatch
|
|
39
40
|
@parameters.each do |index|
|
40
41
|
param = parts[index]
|
41
42
|
value = hash[param.name]
|
42
|
-
return
|
43
|
+
return "".freeze unless value
|
43
44
|
parts[index] = param.escape value
|
44
45
|
end
|
45
46
|
|
@@ -59,7 +60,7 @@ module ActionDispatch
|
|
59
60
|
|
60
61
|
private
|
61
62
|
|
62
|
-
def visit
|
63
|
+
def visit(node)
|
63
64
|
send(DISPATCH_CACHE[node.type], node)
|
64
65
|
end
|
65
66
|
|
@@ -92,6 +93,45 @@ module ActionDispatch
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
96
|
+
class FunctionalVisitor # :nodoc:
|
97
|
+
DISPATCH_CACHE = {}
|
98
|
+
|
99
|
+
def accept(node, seed)
|
100
|
+
visit(node, seed)
|
101
|
+
end
|
102
|
+
|
103
|
+
def visit(node, seed)
|
104
|
+
send(DISPATCH_CACHE[node.type], node, seed)
|
105
|
+
end
|
106
|
+
|
107
|
+
def binary(node, seed)
|
108
|
+
visit(node.right, visit(node.left, seed))
|
109
|
+
end
|
110
|
+
def visit_CAT(n, seed); binary(n, seed); end
|
111
|
+
|
112
|
+
def nary(node, seed)
|
113
|
+
node.children.inject(seed) { |s, c| visit(c, s) }
|
114
|
+
end
|
115
|
+
def visit_OR(n, seed); nary(n, seed); end
|
116
|
+
|
117
|
+
def unary(node, seed)
|
118
|
+
visit(node.left, seed)
|
119
|
+
end
|
120
|
+
def visit_GROUP(n, seed); unary(n, seed); end
|
121
|
+
def visit_STAR(n, seed); unary(n, seed); end
|
122
|
+
|
123
|
+
def terminal(node, seed); seed; end
|
124
|
+
def visit_LITERAL(n, seed); terminal(n, seed); end
|
125
|
+
def visit_SYMBOL(n, seed); terminal(n, seed); end
|
126
|
+
def visit_SLASH(n, seed); terminal(n, seed); end
|
127
|
+
def visit_DOT(n, seed); terminal(n, seed); end
|
128
|
+
|
129
|
+
instance_methods(false).each do |pim|
|
130
|
+
next unless pim =~ /^visit_(.*)$/
|
131
|
+
DISPATCH_CACHE[$1.to_sym] = pim
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
95
135
|
class FormatBuilder < Visitor # :nodoc:
|
96
136
|
def accept(node); Journey::Format.new(super); end
|
97
137
|
def terminal(node); [node.left]; end
|
@@ -116,106 +156,113 @@ module ActionDispatch
|
|
116
156
|
end
|
117
157
|
end
|
118
158
|
|
119
|
-
# Loop through the requirements AST
|
120
|
-
class Each <
|
121
|
-
|
122
|
-
|
123
|
-
def initialize(block)
|
124
|
-
@block = block
|
125
|
-
end
|
126
|
-
|
127
|
-
def visit(node)
|
159
|
+
# Loop through the requirements AST.
|
160
|
+
class Each < FunctionalVisitor # :nodoc:
|
161
|
+
def visit(node, block)
|
128
162
|
block.call(node)
|
129
163
|
super
|
130
164
|
end
|
165
|
+
|
166
|
+
INSTANCE = new
|
131
167
|
end
|
132
168
|
|
133
|
-
class String <
|
169
|
+
class String < FunctionalVisitor # :nodoc:
|
134
170
|
private
|
135
171
|
|
136
|
-
|
137
|
-
|
138
|
-
|
172
|
+
def binary(node, seed)
|
173
|
+
visit(node.right, visit(node.left, seed))
|
174
|
+
end
|
139
175
|
|
140
|
-
|
141
|
-
|
142
|
-
|
176
|
+
def nary(node, seed)
|
177
|
+
last_child = node.children.last
|
178
|
+
node.children.inject(seed) { |s, c|
|
179
|
+
string = visit(c, s)
|
180
|
+
string << "|" unless last_child == c
|
181
|
+
string
|
182
|
+
}
|
183
|
+
end
|
143
184
|
|
144
|
-
|
145
|
-
|
146
|
-
|
185
|
+
def terminal(node, seed)
|
186
|
+
seed + node.left
|
187
|
+
end
|
147
188
|
|
148
|
-
|
149
|
-
|
150
|
-
|
189
|
+
def visit_GROUP(node, seed)
|
190
|
+
visit(node.left, seed.dup << "(") << ")"
|
191
|
+
end
|
192
|
+
|
193
|
+
INSTANCE = new
|
151
194
|
end
|
152
195
|
|
153
|
-
class Dot <
|
196
|
+
class Dot < FunctionalVisitor # :nodoc:
|
154
197
|
def initialize
|
155
198
|
@nodes = []
|
156
199
|
@edges = []
|
157
200
|
end
|
158
201
|
|
159
|
-
def accept(node)
|
202
|
+
def accept(node, seed = [[], []])
|
160
203
|
super
|
204
|
+
nodes, edges = seed
|
161
205
|
<<-eodot
|
162
206
|
digraph parse_tree {
|
163
207
|
size="8,5"
|
164
208
|
node [shape = none];
|
165
209
|
edge [dir = none];
|
166
|
-
#{
|
167
|
-
#{
|
210
|
+
#{nodes.join "\n"}
|
211
|
+
#{edges.join("\n")}
|
168
212
|
}
|
169
213
|
eodot
|
170
214
|
end
|
171
215
|
|
172
216
|
private
|
173
217
|
|
174
|
-
def binary(node)
|
175
|
-
node.children.
|
176
|
-
|
177
|
-
|
218
|
+
def binary(node, seed)
|
219
|
+
seed.last.concat node.children.map { |c|
|
220
|
+
"#{node.object_id} -> #{c.object_id};"
|
221
|
+
}
|
178
222
|
super
|
179
223
|
end
|
180
224
|
|
181
|
-
def nary(node)
|
182
|
-
node.children.
|
183
|
-
|
184
|
-
|
225
|
+
def nary(node, seed)
|
226
|
+
seed.last.concat node.children.map { |c|
|
227
|
+
"#{node.object_id} -> #{c.object_id};"
|
228
|
+
}
|
185
229
|
super
|
186
230
|
end
|
187
231
|
|
188
|
-
def unary(node)
|
189
|
-
|
232
|
+
def unary(node, seed)
|
233
|
+
seed.last << "#{node.object_id} -> #{node.left.object_id};"
|
190
234
|
super
|
191
235
|
end
|
192
236
|
|
193
|
-
def visit_GROUP(node)
|
194
|
-
|
237
|
+
def visit_GROUP(node, seed)
|
238
|
+
seed.first << "#{node.object_id} [label=\"()\"];"
|
195
239
|
super
|
196
240
|
end
|
197
241
|
|
198
|
-
def visit_CAT(node)
|
199
|
-
|
242
|
+
def visit_CAT(node, seed)
|
243
|
+
seed.first << "#{node.object_id} [label=\"○\"];"
|
200
244
|
super
|
201
245
|
end
|
202
246
|
|
203
|
-
def visit_STAR(node)
|
204
|
-
|
247
|
+
def visit_STAR(node, seed)
|
248
|
+
seed.first << "#{node.object_id} [label=\"*\"];"
|
205
249
|
super
|
206
250
|
end
|
207
251
|
|
208
|
-
def visit_OR(node)
|
209
|
-
|
252
|
+
def visit_OR(node, seed)
|
253
|
+
seed.first << "#{node.object_id} [label=\"|\"];"
|
210
254
|
super
|
211
255
|
end
|
212
256
|
|
213
|
-
def terminal(node)
|
257
|
+
def terminal(node, seed)
|
214
258
|
value = node.left
|
215
259
|
|
216
|
-
|
260
|
+
seed.first << "#{node.object_id} [label=\"#{value}\"];"
|
261
|
+
seed
|
217
262
|
end
|
263
|
+
INSTANCE = new
|
218
264
|
end
|
219
265
|
end
|
220
266
|
end
|
267
|
+
# :startdoc:
|
221
268
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch/journey/router"
|
4
|
+
require "action_dispatch/journey/gtg/builder"
|
5
|
+
require "action_dispatch/journey/gtg/simulator"
|
6
|
+
require "action_dispatch/journey/nfa/builder"
|
7
|
+
require "action_dispatch/journey/nfa/simulator"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module ActionDispatch
|
3
4
|
# Provides callbacks to be executed before and after dispatching the request.
|
@@ -7,8 +8,6 @@ module ActionDispatch
|
|
7
8
|
define_callbacks :call
|
8
9
|
|
9
10
|
class << self
|
10
|
-
delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
|
11
|
-
|
12
11
|
def before(*args, &block)
|
13
12
|
set_callback(:call, :before, *args, &block)
|
14
13
|
end
|