actionpack 4.2.8 → 5.2.4.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.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. 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 Http
3
5
  # Models uploaded files.
@@ -24,23 +26,27 @@ module ActionDispatch
24
26
  attr_accessor :headers
25
27
 
26
28
  def initialize(hash) # :nodoc:
27
- @tempfile = hash[:tempfile]
28
- raise(ArgumentError, ':tempfile is required') unless @tempfile
29
+ @tempfile = hash[:tempfile]
30
+ raise(ArgumentError, ":tempfile is required") unless @tempfile
31
+
32
+ if hash[:filename]
33
+ @original_filename = hash[:filename].dup
29
34
 
30
- @original_filename = hash[:filename]
31
- if @original_filename
32
35
  begin
33
36
  @original_filename.encode!(Encoding::UTF_8)
34
37
  rescue EncodingError
35
38
  @original_filename.force_encoding(Encoding::UTF_8)
36
39
  end
40
+ else
41
+ @original_filename = nil
37
42
  end
43
+
38
44
  @content_type = hash[:type]
39
45
  @headers = hash[:head]
40
46
  end
41
47
 
42
48
  # Shortcut for +tempfile.read+.
43
- def read(length=nil, buffer=nil)
49
+ def read(length = nil, buffer = nil)
44
50
  @tempfile.read(length, buffer)
45
51
  end
46
52
 
@@ -50,7 +56,7 @@ module ActionDispatch
50
56
  end
51
57
 
52
58
  # Shortcut for +tempfile.close+.
53
- def close(unlink_now=false)
59
+ def close(unlink_now = false)
54
60
  @tempfile.close(unlink_now)
55
61
  end
56
62
 
@@ -1,5 +1,6 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/hash/slice'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
3
4
 
4
5
  module ActionDispatch
5
6
  module Http
@@ -8,14 +9,25 @@ module ActionDispatch
8
9
  HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
9
10
  PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
10
11
 
11
- mattr_accessor :tld_length
12
- self.tld_length = 1
12
+ mattr_accessor :tld_length, default: 1
13
13
 
14
14
  class << self
15
+ # Returns the domain part of a host given the domain level.
16
+ #
17
+ # # Top-level domain example
18
+ # extract_domain('www.example.com', 1) # => "example.com"
19
+ # # Second-level domain example
20
+ # extract_domain('dev.www.example.co.uk', 2) # => "example.co.uk"
15
21
  def extract_domain(host, tld_length)
16
22
  extract_domain_from(host, tld_length) if named_host?(host)
17
23
  end
18
24
 
25
+ # Returns the subdomains of a host as an Array given the domain level.
26
+ #
27
+ # # Top-level domain example
28
+ # extract_subdomains('www.example.com', 1) # => ["www"]
29
+ # # Second-level domain example
30
+ # extract_subdomains('dev.www.example.co.uk', 2) # => ["dev", "www"]
19
31
  def extract_subdomains(host, tld_length)
20
32
  if named_host?(host)
21
33
  extract_subdomains_from(host, tld_length)
@@ -24,8 +36,14 @@ module ActionDispatch
24
36
  end
25
37
  end
26
38
 
39
+ # Returns the subdomains of a host as a String given the domain level.
40
+ #
41
+ # # Top-level domain example
42
+ # extract_subdomain('www.example.com', 1) # => "www"
43
+ # # Second-level domain example
44
+ # extract_subdomain('dev.www.example.co.uk', 2) # => "dev.www"
27
45
  def extract_subdomain(host, tld_length)
28
- extract_subdomains(host, tld_length).join('.')
46
+ extract_subdomains(host, tld_length).join(".")
29
47
  end
30
48
 
31
49
  def url_for(options)
@@ -42,14 +60,14 @@ module ActionDispatch
42
60
  port = options[:port]
43
61
 
44
62
  unless host
45
- raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
63
+ raise ArgumentError, "Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true"
46
64
  end
47
65
 
48
66
  build_host_url(host, port, protocol, options, path_for(options))
49
67
  end
50
68
 
51
69
  def path_for(options)
52
- path = options[:script_name].to_s.chomp("/")
70
+ path = options[:script_name].to_s.chomp("/".freeze)
53
71
  path << options[:path] if options.key?(:path)
54
72
 
55
73
  add_trailing_slash(path) if options[:trailing_slash]
@@ -63,8 +81,9 @@ module ActionDispatch
63
81
 
64
82
  def add_params(path, params)
65
83
  params = { params: params } unless params.is_a?(Hash)
66
- params.reject! { |_,v| v.to_param.nil? }
67
- path << "?#{params.to_query}" unless params.empty?
84
+ params.reject! { |_, v| v.to_param.nil? }
85
+ query = params.to_query
86
+ path << "?#{query}" unless query.empty?
68
87
  end
69
88
 
70
89
  def add_anchor(path, anchor)
@@ -74,19 +93,17 @@ module ActionDispatch
74
93
  end
75
94
 
76
95
  def extract_domain_from(host, tld_length)
77
- host.split('.').last(1 + tld_length).join('.')
96
+ host.split(".").last(1 + tld_length).join(".")
78
97
  end
79
98
 
80
99
  def extract_subdomains_from(host, tld_length)
81
- parts = host.split('.')
100
+ parts = host.split(".")
82
101
  parts[0..-(tld_length + 2)]
83
102
  end
84
103
 
85
104
  def add_trailing_slash(path)
86
- # includes querysting
87
- if path.include?('?')
105
+ if path.include?("?")
88
106
  path.sub!(/\?/, '/\&')
89
- # does not have a .format
90
107
  elsif !path.include?(".")
91
108
  path.sub!(/[^\/]\z|\A\z/, '\&/')
92
109
  end
@@ -140,11 +157,11 @@ module ActionDispatch
140
157
  subdomain = options.fetch :subdomain, true
141
158
  domain = options[:domain]
142
159
 
143
- host = ""
160
+ host = "".dup
144
161
  if subdomain == true
145
162
  return _host if domain.nil?
146
163
 
147
- host << extract_subdomains_from(_host, tld_length).join('.')
164
+ host << extract_subdomains_from(_host, tld_length).join(".")
148
165
  elsif subdomain
149
166
  host << subdomain.to_param
150
167
  end
@@ -166,43 +183,80 @@ module ActionDispatch
166
183
  end
167
184
  end
168
185
 
169
- def initialize(env)
186
+ def initialize
170
187
  super
171
188
  @protocol = nil
172
189
  @port = nil
173
190
  end
174
191
 
175
192
  # Returns the complete URL used for this request.
193
+ #
194
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
195
+ # req.url # => "http://example.com"
176
196
  def url
177
197
  protocol + host_with_port + fullpath
178
198
  end
179
199
 
180
200
  # Returns 'https://' if this is an SSL request and 'http://' otherwise.
201
+ #
202
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
203
+ # req.protocol # => "http://"
204
+ #
205
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com', 'HTTPS' => 'on'
206
+ # req.protocol # => "https://"
181
207
  def protocol
182
- @protocol ||= ssl? ? 'https://' : 'http://'
208
+ @protocol ||= ssl? ? "https://" : "http://"
183
209
  end
184
210
 
185
- # Returns the \host for this request, such as "example.com".
211
+ # Returns the \host and port for this request, such as "example.com:8080".
212
+ #
213
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
214
+ # req.raw_host_with_port # => "example.com"
215
+ #
216
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
217
+ # req.raw_host_with_port # => "example.com:80"
218
+ #
219
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
220
+ # req.raw_host_with_port # => "example.com:8080"
186
221
  def raw_host_with_port
187
- if forwarded = env["HTTP_X_FORWARDED_HOST"].presence
222
+ if forwarded = x_forwarded_host.presence
188
223
  forwarded.split(/,\s?/).last
189
224
  else
190
- env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
225
+ get_header("HTTP_HOST") || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}"
191
226
  end
192
227
  end
193
228
 
194
- # Returns the host for this request, such as example.com.
229
+ # Returns the host for this request, such as "example.com".
230
+ #
231
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
232
+ # req.host # => "example.com"
195
233
  def host
196
- raw_host_with_port.sub(/:\d+$/, '')
234
+ raw_host_with_port.sub(/:\d+$/, "".freeze)
197
235
  end
198
236
 
199
237
  # Returns a \host:\port string for this request, such as "example.com" or
200
- # "example.com:8080".
238
+ # "example.com:8080". Port is only included if it is not a default port
239
+ # (80 or 443)
240
+ #
241
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
242
+ # req.host_with_port # => "example.com"
243
+ #
244
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
245
+ # req.host_with_port # => "example.com"
246
+ #
247
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
248
+ # req.host_with_port # => "example.com:8080"
201
249
  def host_with_port
202
250
  "#{host}#{port_string}"
203
251
  end
204
252
 
205
253
  # Returns the port number of this request as an integer.
254
+ #
255
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
256
+ # req.port # => 80
257
+ #
258
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
259
+ # req.port # => 8080
206
260
  def port
207
261
  @port ||= begin
208
262
  if raw_host_with_port =~ /:(\d+)$/
@@ -214,32 +268,60 @@ module ActionDispatch
214
268
  end
215
269
 
216
270
  # Returns the standard \port number for this request's protocol.
271
+ #
272
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
273
+ # req.standard_port # => 80
217
274
  def standard_port
218
275
  case protocol
219
- when 'https://' then 443
220
- else 80
276
+ when "https://" then 443
277
+ else 80
221
278
  end
222
279
  end
223
280
 
224
281
  # Returns whether this request is using the standard port
282
+ #
283
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
284
+ # req.standard_port? # => true
285
+ #
286
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
287
+ # req.standard_port? # => false
225
288
  def standard_port?
226
289
  port == standard_port
227
290
  end
228
291
 
229
292
  # Returns a number \port suffix like 8080 if the \port number of this request
230
293
  # is not the default HTTP \port 80 or HTTPS \port 443.
294
+ #
295
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
296
+ # req.optional_port # => nil
297
+ #
298
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
299
+ # req.optional_port # => 8080
231
300
  def optional_port
232
301
  standard_port? ? nil : port
233
302
  end
234
303
 
235
304
  # Returns a string \port suffix, including colon, like ":8080" if the \port
236
305
  # number of this request is not the default HTTP \port 80 or HTTPS \port 443.
306
+ #
307
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
308
+ # req.port_string # => ""
309
+ #
310
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
311
+ # req.port_string # => ":8080"
237
312
  def port_string
238
- standard_port? ? '' : ":#{port}"
313
+ standard_port? ? "" : ":#{port}"
239
314
  end
240
315
 
316
+ # Returns the requested port, such as 8080, based on SERVER_PORT
317
+ #
318
+ # req = ActionDispatch::Request.new 'SERVER_PORT' => '80'
319
+ # req.server_port # => 80
320
+ #
321
+ # req = ActionDispatch::Request.new 'SERVER_PORT' => '8080'
322
+ # req.server_port # => 8080
241
323
  def server_port
242
- @env['SERVER_PORT'].to_i
324
+ get_header("SERVER_PORT").to_i
243
325
  end
244
326
 
245
327
  # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
@@ -1,5 +1,7 @@
1
- require 'action_dispatch/journey/router'
2
- require 'action_dispatch/journey/gtg/builder'
3
- require 'action_dispatch/journey/gtg/simulator'
4
- require 'action_dispatch/journey/nfa/builder'
5
- require 'action_dispatch/journey/nfa/simulator'
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,11 +1,13 @@
1
- require 'action_controller/metal/exceptions'
2
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller/metal/exceptions"
3
4
 
4
5
  module ActionDispatch
6
+ # :stopdoc:
5
7
  module Journey
6
8
  # The Formatter class is used for formatting URLs. For example, parameters
7
9
  # passed to +url_for+ in Rails will eventually call Formatter#generate.
8
- class Formatter # :nodoc:
10
+ class Formatter
9
11
  attr_reader :routes
10
12
 
11
13
  def initialize(routes)
@@ -15,7 +17,7 @@ module ActionDispatch
15
17
 
16
18
  def generate(name, options, path_parameters, parameterize = nil)
17
19
  constraints = path_parameters.merge(options)
18
- missing_keys = []
20
+ missing_keys = nil
19
21
 
20
22
  match_route(name, constraints) do |route|
21
23
  parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
@@ -26,22 +28,31 @@ module ActionDispatch
26
28
  next unless name || route.dispatcher?
27
29
 
28
30
  missing_keys = missing_keys(route, parameterized_parts)
29
- next unless missing_keys.empty?
31
+ next if missing_keys && !missing_keys.empty?
30
32
  params = options.dup.delete_if do |key, _|
31
33
  parameterized_parts.key?(key) || route.defaults.key?(key)
32
34
  end
33
35
 
34
36
  defaults = route.defaults
35
37
  required_parts = route.required_parts
36
- parameterized_parts.delete_if do |key, value|
37
- value.to_s == defaults[key].to_s && !required_parts.include?(key)
38
+
39
+ route.parts.reverse_each do |key|
40
+ break if defaults[key].nil? && parameterized_parts[key].present?
41
+ next if parameterized_parts[key].to_s != defaults[key].to_s
42
+ break if required_parts.include?(key)
43
+
44
+ parameterized_parts.delete(key)
38
45
  end
39
46
 
40
47
  return [route.format(parameterized_parts), params]
41
48
  end
42
49
 
43
- message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
44
- message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
50
+ unmatched_keys = (missing_keys || []) & constraints.keys
51
+ missing_keys = (missing_keys || []) - unmatched_keys
52
+
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?
45
56
 
46
57
  raise ActionController::UrlGenerationError, message
47
58
  end
@@ -55,12 +66,12 @@ module ActionDispatch
55
66
  def extract_parameterized_parts(route, options, recall, parameterize = nil)
56
67
  parameterized_parts = recall.merge(options)
57
68
 
58
- keys_to_keep = route.parts.reverse.drop_while { |part|
69
+ keys_to_keep = route.parts.reverse_each.drop_while { |part|
59
70
  !options.key?(part) || (options[part] || recall[part]).nil?
60
71
  } | route.required_parts
61
72
 
62
- (parameterized_parts.keys - keys_to_keep).each do |bad_key|
63
- parameterized_parts.delete(bad_key)
73
+ parameterized_parts.delete_if do |bad_key, _|
74
+ !keys_to_keep.include?(bad_key)
64
75
  end
65
76
 
66
77
  if parameterize
@@ -81,28 +92,18 @@ module ActionDispatch
81
92
  if named_routes.key?(name)
82
93
  yield named_routes[name]
83
94
  else
84
- # Make sure we don't show the deprecation warning more than once
85
- warned = false
86
-
87
95
  routes = non_recursive(cache, options)
88
96
 
89
- hash = routes.group_by { |_, r| r.score(options) }
97
+ supplied_keys = options.each_with_object({}) do |(k, v), h|
98
+ h[k.to_s] = true if v
99
+ end
100
+
101
+ hash = routes.group_by { |_, r| r.score(supplied_keys) }
90
102
 
91
103
  hash.keys.sort.reverse_each do |score|
92
104
  break if score < 0
93
105
 
94
106
  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
107
  yield route
107
108
  end
108
109
  end
@@ -125,15 +126,36 @@ module ActionDispatch
125
126
  routes
126
127
  end
127
128
 
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
+
128
138
  # Returns an array populated with missing keys if any are present.
129
139
  def missing_keys(route, parts)
130
- missing_keys = []
140
+ missing_keys = nil
131
141
  tests = route.path.requirements
132
142
  route.required_parts.each { |key|
133
- if tests.key?(key)
134
- missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
143
+ case tests[key]
144
+ when nil
145
+ unless parts[key]
146
+ missing_keys ||= []
147
+ missing_keys << key
148
+ end
149
+ when RegexCaseComparator
150
+ unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
151
+ missing_keys ||= []
152
+ missing_keys << key
153
+ end
135
154
  else
136
- missing_keys << key unless parts[key]
155
+ unless /\A#{tests[key]}\Z/ === parts[key]
156
+ missing_keys ||= []
157
+ missing_keys << key
158
+ end
137
159
  end
138
160
  }
139
161
  missing_keys
@@ -149,7 +171,7 @@ module ActionDispatch
149
171
 
150
172
  def build_cache
151
173
  root = { ___routes: [] }
152
- routes.each_with_index do |route, i|
174
+ routes.routes.each_with_index do |route, i|
153
175
  leaf = route.required_defaults.inject(root) do |h, tuple|
154
176
  h[tuple] ||= {}
155
177
  end
@@ -163,4 +185,5 @@ module ActionDispatch
163
185
  end
164
186
  end
165
187
  end
188
+ # :startdoc:
166
189
  end