actionpack 5.2.4.4 → 6.1.1

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.

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -322
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller.rb +1 -0
  6. data/lib/abstract_controller/base.rb +38 -4
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/caching/fragments.rb +6 -22
  9. data/lib/abstract_controller/callbacks.rb +14 -2
  10. data/lib/abstract_controller/collector.rb +1 -2
  11. data/lib/abstract_controller/helpers.rb +106 -90
  12. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  13. data/lib/abstract_controller/rendering.rb +9 -9
  14. data/lib/abstract_controller/translation.rb +11 -5
  15. data/lib/action_controller.rb +7 -4
  16. data/lib/action_controller/api.rb +4 -3
  17. data/lib/action_controller/base.rb +6 -9
  18. data/lib/action_controller/caching.rb +1 -3
  19. data/lib/action_controller/log_subscriber.rb +10 -7
  20. data/lib/action_controller/metal.rb +10 -8
  21. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  22. data/lib/action_controller/metal/conditional_get.rb +19 -5
  23. data/lib/action_controller/metal/content_security_policy.rb +1 -2
  24. data/lib/action_controller/metal/cookies.rb +3 -1
  25. data/lib/action_controller/metal/data_streaming.rb +6 -7
  26. data/lib/action_controller/metal/default_headers.rb +17 -0
  27. data/lib/action_controller/metal/etag_with_template_digest.rb +3 -5
  28. data/lib/action_controller/metal/exceptions.rb +56 -2
  29. data/lib/action_controller/metal/flash.rb +5 -5
  30. data/lib/action_controller/metal/head.rb +7 -4
  31. data/lib/action_controller/metal/helpers.rb +14 -5
  32. data/lib/action_controller/metal/http_authentication.rb +24 -23
  33. data/lib/action_controller/metal/implicit_render.rb +5 -15
  34. data/lib/action_controller/metal/instrumentation.rb +13 -14
  35. data/lib/action_controller/metal/live.rb +30 -32
  36. data/lib/action_controller/metal/logging.rb +20 -0
  37. data/lib/action_controller/metal/mime_responds.rb +19 -4
  38. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  39. data/lib/action_controller/metal/params_wrapper.rb +31 -22
  40. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  41. data/lib/action_controller/metal/redirecting.rb +6 -6
  42. data/lib/action_controller/metal/renderers.rb +4 -4
  43. data/lib/action_controller/metal/rendering.rb +8 -3
  44. data/lib/action_controller/metal/request_forgery_protection.rb +62 -34
  45. data/lib/action_controller/metal/rescue.rb +1 -1
  46. data/lib/action_controller/metal/streaming.rb +0 -1
  47. data/lib/action_controller/metal/strong_parameters.rb +167 -58
  48. data/lib/action_controller/metal/url_for.rb +1 -1
  49. data/lib/action_controller/railties/helpers.rb +1 -1
  50. data/lib/action_controller/renderer.rb +37 -13
  51. data/lib/action_controller/template_assertions.rb +1 -1
  52. data/lib/action_controller/test_case.rb +70 -65
  53. data/lib/action_dispatch.rb +9 -3
  54. data/lib/action_dispatch/http/cache.rb +26 -21
  55. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  56. data/lib/action_dispatch/http/content_security_policy.rb +33 -19
  57. data/lib/action_dispatch/http/filter_parameters.rb +9 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  59. data/lib/action_dispatch/http/headers.rb +4 -4
  60. data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
  61. data/lib/action_dispatch/http/mime_type.rb +42 -23
  62. data/lib/action_dispatch/http/parameters.rb +14 -23
  63. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  64. data/lib/action_dispatch/http/request.rb +45 -22
  65. data/lib/action_dispatch/http/response.rb +45 -25
  66. data/lib/action_dispatch/http/upload.rb +9 -1
  67. data/lib/action_dispatch/http/url.rb +82 -82
  68. data/lib/action_dispatch/journey.rb +0 -2
  69. data/lib/action_dispatch/journey/formatter.rb +54 -30
  70. data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
  71. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  72. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
  73. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  74. data/lib/action_dispatch/journey/nodes/node.rb +13 -11
  75. data/lib/action_dispatch/journey/parser.rb +13 -13
  76. data/lib/action_dispatch/journey/parser.y +1 -1
  77. data/lib/action_dispatch/journey/path/pattern.rb +19 -21
  78. data/lib/action_dispatch/journey/route.rb +10 -20
  79. data/lib/action_dispatch/journey/router.rb +26 -34
  80. data/lib/action_dispatch/journey/router/utils.rb +14 -12
  81. data/lib/action_dispatch/journey/routes.rb +0 -2
  82. data/lib/action_dispatch/journey/scanner.rb +10 -4
  83. data/lib/action_dispatch/journey/visitors.rb +1 -4
  84. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  85. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  86. data/lib/action_dispatch/middleware/cookies.rb +128 -109
  87. data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
  88. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  89. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  90. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
  91. data/lib/action_dispatch/middleware/flash.rb +1 -1
  92. data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
  93. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  94. data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
  95. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  96. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
  97. data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
  98. data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
  99. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  100. data/lib/action_dispatch/middleware/stack.rb +56 -2
  101. data/lib/action_dispatch/middleware/static.rb +153 -93
  102. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  103. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  108. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  109. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  111. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  112. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
  114. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -1
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
  123. data/lib/action_dispatch/railtie.rb +8 -2
  124. data/lib/action_dispatch/request/session.rb +10 -9
  125. data/lib/action_dispatch/request/utils.rb +26 -2
  126. data/lib/action_dispatch/routing.rb +21 -20
  127. data/lib/action_dispatch/routing/inspector.rb +100 -52
  128. data/lib/action_dispatch/routing/mapper.rb +155 -103
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
  130. data/lib/action_dispatch/routing/redirection.rb +3 -3
  131. data/lib/action_dispatch/routing/route_set.rb +71 -69
  132. data/lib/action_dispatch/routing/url_for.rb +2 -2
  133. data/lib/action_dispatch/system_test_case.rb +54 -11
  134. data/lib/action_dispatch/system_testing/browser.rb +53 -16
  135. data/lib/action_dispatch/system_testing/driver.rb +11 -3
  136. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
  137. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
  138. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  139. data/lib/action_dispatch/testing/assertions.rb +1 -1
  140. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  141. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  142. data/lib/action_dispatch/testing/integration.rb +61 -28
  143. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  144. data/lib/action_dispatch/testing/test_process.rb +29 -4
  145. data/lib/action_dispatch/testing/test_request.rb +3 -3
  146. data/lib/action_dispatch/testing/test_response.rb +4 -32
  147. data/lib/action_pack.rb +1 -1
  148. data/lib/action_pack/gem_version.rb +4 -4
  149. metadata +38 -26
  150. data/lib/action_controller/metal/force_ssl.rb +0 -99
  151. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  152. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  153. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  154. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  155. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -20,7 +20,6 @@ module ActionDispatch
20
20
  # A +Tempfile+ object with the actual uploaded file. Note that some of
21
21
  # its interface is available directly.
22
22
  attr_accessor :tempfile
23
- alias :to_io :tempfile
24
23
 
25
24
  # A string with the headers of the multipart request.
26
25
  attr_accessor :headers
@@ -65,6 +64,11 @@ module ActionDispatch
65
64
  @tempfile.path
66
65
  end
67
66
 
67
+ # Shortcut for +tempfile.to_path+.
68
+ def to_path
69
+ @tempfile.to_path
70
+ end
71
+
68
72
  # Shortcut for +tempfile.rewind+.
69
73
  def rewind
70
74
  @tempfile.rewind
@@ -79,6 +83,10 @@ module ActionDispatch
79
83
  def eof?
80
84
  @tempfile.eof?
81
85
  end
86
+
87
+ def to_io
88
+ @tempfile.to_io
89
+ end
82
90
  end
83
91
  end
84
92
  end
@@ -9,6 +9,7 @@ module ActionDispatch
9
9
  HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
10
10
  PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
11
11
 
12
+ mattr_accessor :secure_protocol, default: false
12
13
  mattr_accessor :tld_length, default: 1
13
14
 
14
15
  class << self
@@ -67,7 +68,7 @@ module ActionDispatch
67
68
  end
68
69
 
69
70
  def path_for(options)
70
- path = options[:script_name].to_s.chomp("/".freeze)
71
+ path = options[:script_name].to_s.chomp("/")
71
72
  path << options[:path] if options.key?(:path)
72
73
 
73
74
  add_trailing_slash(path) if options[:trailing_slash]
@@ -78,109 +79,108 @@ module ActionDispatch
78
79
  end
79
80
 
80
81
  private
81
-
82
- def add_params(path, params)
83
- params = { params: params } unless params.is_a?(Hash)
84
- params.reject! { |_, v| v.to_param.nil? }
85
- query = params.to_query
86
- path << "?#{query}" unless query.empty?
87
- end
88
-
89
- def add_anchor(path, anchor)
90
- if anchor
91
- path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
82
+ def add_params(path, params)
83
+ params = { params: params } unless params.is_a?(Hash)
84
+ params.reject! { |_, v| v.to_param.nil? }
85
+ query = params.to_query
86
+ path << "?#{query}" unless query.empty?
92
87
  end
93
- end
94
88
 
95
- def extract_domain_from(host, tld_length)
96
- host.split(".").last(1 + tld_length).join(".")
97
- end
89
+ def add_anchor(path, anchor)
90
+ if anchor
91
+ path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
92
+ end
93
+ end
98
94
 
99
- def extract_subdomains_from(host, tld_length)
100
- parts = host.split(".")
101
- parts[0..-(tld_length + 2)]
102
- end
95
+ def extract_domain_from(host, tld_length)
96
+ host.split(".").last(1 + tld_length).join(".")
97
+ end
103
98
 
104
- def add_trailing_slash(path)
105
- if path.include?("?")
106
- path.sub!(/\?/, '/\&')
107
- elsif !path.include?(".")
108
- path.sub!(/[^\/]\z|\A\z/, '\&/')
99
+ def extract_subdomains_from(host, tld_length)
100
+ parts = host.split(".")
101
+ parts[0..-(tld_length + 2)]
109
102
  end
110
- end
111
103
 
112
- def build_host_url(host, port, protocol, options, path)
113
- if match = host.match(HOST_REGEXP)
114
- protocol ||= match[1] unless protocol == false
115
- host = match[2]
116
- port = match[3] unless options.key? :port
104
+ def add_trailing_slash(path)
105
+ if path.include?("?")
106
+ path.sub!(/\?/, '/\&')
107
+ elsif !path.include?(".")
108
+ path.sub!(/[^\/]\z|\A\z/, '\&/')
109
+ end
117
110
  end
118
111
 
119
- protocol = normalize_protocol protocol
120
- host = normalize_host(host, options)
112
+ def build_host_url(host, port, protocol, options, path)
113
+ if match = host.match(HOST_REGEXP)
114
+ protocol ||= match[1] unless protocol == false
115
+ host = match[2]
116
+ port = match[3] unless options.key? :port
117
+ end
121
118
 
122
- result = protocol.dup
119
+ protocol = normalize_protocol protocol
120
+ host = normalize_host(host, options)
123
121
 
124
- if options[:user] && options[:password]
125
- result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
- end
122
+ result = protocol.dup
127
123
 
128
- result << host
129
- normalize_port(port, protocol) { |normalized_port|
130
- result << ":#{normalized_port}"
131
- }
124
+ if options[:user] && options[:password]
125
+ result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
+ end
132
127
 
133
- result.concat path
134
- end
128
+ result << host
129
+ normalize_port(port, protocol) { |normalized_port|
130
+ result << ":#{normalized_port}"
131
+ }
135
132
 
136
- def named_host?(host)
137
- IP_HOST_REGEXP !~ host
138
- end
133
+ result.concat path
134
+ end
139
135
 
140
- def normalize_protocol(protocol)
141
- case protocol
142
- when nil
143
- "http://"
144
- when false, "//"
145
- "//"
146
- when PROTOCOL_REGEXP
147
- "#{$1}://"
148
- else
149
- raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
136
+ def named_host?(host)
137
+ !IP_HOST_REGEXP.match?(host)
150
138
  end
151
- end
152
139
 
153
- def normalize_host(_host, options)
154
- return _host unless named_host?(_host)
140
+ def normalize_protocol(protocol)
141
+ case protocol
142
+ when nil
143
+ secure_protocol ? "https://" : "http://"
144
+ when false, "//"
145
+ "//"
146
+ when PROTOCOL_REGEXP
147
+ "#{$1}://"
148
+ else
149
+ raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
150
+ end
151
+ end
152
+
153
+ def normalize_host(_host, options)
154
+ return _host unless named_host?(_host)
155
155
 
156
- tld_length = options[:tld_length] || @@tld_length
157
- subdomain = options.fetch :subdomain, true
158
- domain = options[:domain]
156
+ tld_length = options[:tld_length] || @@tld_length
157
+ subdomain = options.fetch :subdomain, true
158
+ domain = options[:domain]
159
159
 
160
- host = "".dup
161
- if subdomain == true
162
- return _host if domain.nil?
160
+ host = +""
161
+ if subdomain == true
162
+ return _host if domain.nil?
163
163
 
164
- host << extract_subdomains_from(_host, tld_length).join(".")
165
- elsif subdomain
166
- host << subdomain.to_param
164
+ host << extract_subdomains_from(_host, tld_length).join(".")
165
+ elsif subdomain
166
+ host << subdomain.to_param
167
+ end
168
+ host << "." unless host.empty?
169
+ host << (domain || extract_domain_from(_host, tld_length))
170
+ host
167
171
  end
168
- host << "." unless host.empty?
169
- host << (domain || extract_domain_from(_host, tld_length))
170
- host
171
- end
172
172
 
173
- def normalize_port(port, protocol)
174
- return unless port
173
+ def normalize_port(port, protocol)
174
+ return unless port
175
175
 
176
- case protocol
177
- when "//" then yield port
178
- when "https://"
179
- yield port unless port.to_i == 443
180
- else
181
- yield port unless port.to_i == 80
176
+ case protocol
177
+ when "//" then yield port
178
+ when "https://"
179
+ yield port unless port.to_i == 443
180
+ else
181
+ yield port unless port.to_i == 80
182
+ end
182
183
  end
183
- end
184
184
  end
185
185
 
186
186
  def initialize
@@ -231,7 +231,7 @@ module ActionDispatch
231
231
  # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
232
232
  # req.host # => "example.com"
233
233
  def host
234
- raw_host_with_port.sub(/:\d+$/, "".freeze)
234
+ raw_host_with_port.sub(/:\d+$/, "")
235
235
  end
236
236
 
237
237
  # Returns a \host:\port string for this request, such as "example.com" or
@@ -3,5 +3,3 @@
3
3
  require "action_dispatch/journey/router"
4
4
  require "action_dispatch/journey/gtg/builder"
5
5
  require "action_dispatch/journey/gtg/simulator"
6
- require "action_dispatch/journey/nfa/builder"
7
- require "action_dispatch/journey/nfa/simulator"
@@ -15,12 +15,53 @@ module ActionDispatch
15
15
  @cache = nil
16
16
  end
17
17
 
18
- def generate(name, options, path_parameters, parameterize = nil)
18
+ class RouteWithParams
19
+ attr_reader :params
20
+
21
+ def initialize(route, parameterized_parts, params)
22
+ @route = route
23
+ @parameterized_parts = parameterized_parts
24
+ @params = params
25
+ end
26
+
27
+ def path(_)
28
+ @route.format(@parameterized_parts)
29
+ end
30
+ end
31
+
32
+ class MissingRoute
33
+ attr_reader :routes, :name, :constraints, :missing_keys, :unmatched_keys
34
+
35
+ def initialize(constraints, missing_keys, unmatched_keys, routes, name)
36
+ @constraints = constraints
37
+ @missing_keys = missing_keys
38
+ @unmatched_keys = unmatched_keys
39
+ @routes = routes
40
+ @name = name
41
+ end
42
+
43
+ def path(method_name)
44
+ raise ActionController::UrlGenerationError.new(message, routes, name, method_name)
45
+ end
46
+
47
+ def params
48
+ path("unknown")
49
+ end
50
+
51
+ def message
52
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
53
+ message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
54
+ message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
55
+ message
56
+ end
57
+ end
58
+
59
+ def generate(name, options, path_parameters)
19
60
  constraints = path_parameters.merge(options)
20
61
  missing_keys = nil
21
62
 
22
63
  match_route(name, constraints) do |route|
23
- parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
64
+ parameterized_parts = extract_parameterized_parts(route, options, path_parameters)
24
65
 
25
66
  # Skip this route unless a name has been provided or it is a
26
67
  # standard Rails route since we can't determine whether an options
@@ -44,17 +85,13 @@ module ActionDispatch
44
85
  parameterized_parts.delete(key)
45
86
  end
46
87
 
47
- return [route.format(parameterized_parts), params]
88
+ return RouteWithParams.new(route, parameterized_parts, params)
48
89
  end
49
90
 
50
91
  unmatched_keys = (missing_keys || []) & constraints.keys
51
92
  missing_keys = (missing_keys || []) - unmatched_keys
52
93
 
53
- message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
54
- message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
55
- message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
56
-
57
- raise ActionController::UrlGenerationError, message
94
+ MissingRoute.new(constraints, missing_keys, unmatched_keys, routes, name)
58
95
  end
59
96
 
60
97
  def clear
@@ -62,21 +99,22 @@ module ActionDispatch
62
99
  end
63
100
 
64
101
  private
65
-
66
- def extract_parameterized_parts(route, options, recall, parameterize = nil)
102
+ def extract_parameterized_parts(route, options, recall)
67
103
  parameterized_parts = recall.merge(options)
68
104
 
69
105
  keys_to_keep = route.parts.reverse_each.drop_while { |part|
70
- !options.key?(part) || (options[part] || recall[part]).nil?
106
+ !(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
71
107
  } | route.required_parts
72
108
 
73
109
  parameterized_parts.delete_if do |bad_key, _|
74
110
  !keys_to_keep.include?(bad_key)
75
111
  end
76
112
 
77
- if parameterize
78
- parameterized_parts.each do |k, v|
79
- parameterized_parts[k] = parameterize.call(k, v)
113
+ parameterized_parts.each do |k, v|
114
+ if k == :controller
115
+ parameterized_parts[k] = v
116
+ else
117
+ parameterized_parts[k] = v.to_param
80
118
  end
81
119
  end
82
120
 
@@ -126,19 +164,10 @@ module ActionDispatch
126
164
  routes
127
165
  end
128
166
 
129
- module RegexCaseComparator
130
- DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
131
- DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
132
-
133
- def self.===(regex)
134
- DEFAULT_INPUT == regex
135
- end
136
- end
137
-
138
167
  # Returns an array populated with missing keys if any are present.
139
168
  def missing_keys(route, parts)
140
169
  missing_keys = nil
141
- tests = route.path.requirements
170
+ tests = route.path.requirements_for_missing_keys_check
142
171
  route.required_parts.each { |key|
143
172
  case tests[key]
144
173
  when nil
@@ -146,13 +175,8 @@ module ActionDispatch
146
175
  missing_keys ||= []
147
176
  missing_keys << key
148
177
  end
149
- when RegexCaseComparator
150
- unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
151
- missing_keys ||= []
152
- missing_keys << key
153
- end
154
178
  else
155
- unless /\A#{tests[key]}\Z/ === parts[key]
179
+ unless tests[key].match?(parts[key])
156
180
  missing_keys ||= []
157
181
  missing_keys << key
158
182
  end
@@ -13,45 +13,44 @@ module ActionDispatch
13
13
  def initialize(root)
14
14
  @root = root
15
15
  @ast = Nodes::Cat.new root, DUMMY
16
- @followpos = nil
16
+ @followpos = build_followpos
17
17
  end
18
18
 
19
19
  def transition_table
20
20
  dtrans = TransitionTable.new
21
- marked = {}
22
- state_id = Hash.new { |h, k| h[k] = h.length }
21
+ marked = {}.compare_by_identity
22
+ state_id = Hash.new { |h, k| h[k] = h.length }.compare_by_identity
23
+ dstates = [firstpos(root)]
23
24
 
24
- start = firstpos(root)
25
- dstates = [start]
26
25
  until dstates.empty?
27
26
  s = dstates.shift
28
27
  next if marked[s]
29
28
  marked[s] = true # mark s
30
29
 
31
30
  s.group_by { |state| symbol(state) }.each do |sym, ps|
32
- u = ps.flat_map { |l| followpos(l) }
31
+ u = ps.flat_map { |l| @followpos[l] }
33
32
  next if u.empty?
34
33
 
35
- if u.uniq == [DUMMY]
36
- from = state_id[s]
37
- to = state_id[Object.new]
38
- dtrans[from, to] = sym
34
+ from = state_id[s]
39
35
 
36
+ if u.all? { |pos| pos == DUMMY }
37
+ to = state_id[Object.new]
38
+ dtrans[from, to] = sym
40
39
  dtrans.add_accepting(to)
40
+
41
41
  ps.each { |state| dtrans.add_memo(to, state.memo) }
42
42
  else
43
- dtrans[state_id[s], state_id[u]] = sym
43
+ to = state_id[u]
44
+ dtrans[from, to] = sym
44
45
 
45
46
  if u.include?(DUMMY)
46
- to = state_id[u]
47
+ ps.each do |state|
48
+ if @followpos[state].include?(DUMMY)
49
+ dtrans.add_memo(to, state.memo)
50
+ end
51
+ end
47
52
 
48
- accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
49
-
50
- accepting.each { |accepting_state|
51
- dtrans.add_memo(to, accepting_state.memo)
52
- }
53
-
54
- dtrans.add_accepting(state_id[u])
53
+ dtrans.add_accepting(to)
55
54
  end
56
55
  end
57
56
 
@@ -92,7 +91,7 @@ module ActionDispatch
92
91
  firstpos(node.left)
93
92
  end
94
93
  when Nodes::Or
95
- node.children.flat_map { |c| firstpos(c) }.uniq
94
+ node.children.flat_map { |c| firstpos(c) }.tap(&:uniq!)
96
95
  when Nodes::Unary
97
96
  firstpos(node.left)
98
97
  when Nodes::Terminal
@@ -107,7 +106,7 @@ module ActionDispatch
107
106
  when Nodes::Star
108
107
  firstpos(node.left)
109
108
  when Nodes::Or
110
- node.children.flat_map { |c| lastpos(c) }.uniq
109
+ node.children.flat_map { |c| lastpos(c) }.tap(&:uniq!)
111
110
  when Nodes::Cat
112
111
  if nullable?(node.right)
113
112
  lastpos(node.left) | lastpos(node.right)
@@ -123,18 +122,9 @@ module ActionDispatch
123
122
  end
124
123
  end
125
124
 
126
- def followpos(node)
127
- followpos_table[node]
128
- end
129
-
130
125
  private
131
-
132
- def followpos_table
133
- @followpos ||= build_followpos
134
- end
135
-
136
126
  def build_followpos
137
- table = Hash.new { |h, k| h[k] = [] }
127
+ table = Hash.new { |h, k| h[k] = [] }.compare_by_identity
138
128
  @ast.each do |n|
139
129
  case n
140
130
  when Nodes::Cat
@@ -151,12 +141,7 @@ module ActionDispatch
151
141
  end
152
142
 
153
143
  def symbol(edge)
154
- case edge
155
- when Journey::Nodes::Symbol
156
- edge.regexp
157
- else
158
- edge.left
159
- end
144
+ edge.symbol? ? edge.regexp : edge.left
160
145
  end
161
146
  end
162
147
  end