actionpack 6.0.5.1 → 6.1.0

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +248 -344
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +35 -2
  6. data/lib/abstract_controller/callbacks.rb +2 -2
  7. data/lib/abstract_controller/helpers.rb +105 -90
  8. data/lib/abstract_controller/rendering.rb +9 -9
  9. data/lib/abstract_controller/translation.rb +8 -2
  10. data/lib/abstract_controller.rb +1 -0
  11. data/lib/action_controller/api.rb +2 -2
  12. data/lib/action_controller/base.rb +4 -2
  13. data/lib/action_controller/caching.rb +0 -1
  14. data/lib/action_controller/log_subscriber.rb +3 -3
  15. data/lib/action_controller/metal/conditional_get.rb +10 -2
  16. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  17. data/lib/action_controller/metal/cookies.rb +3 -1
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
  20. data/lib/action_controller/metal/exceptions.rb +33 -0
  21. data/lib/action_controller/metal/head.rb +7 -4
  22. data/lib/action_controller/metal/helpers.rb +11 -1
  23. data/lib/action_controller/metal/http_authentication.rb +5 -3
  24. data/lib/action_controller/metal/implicit_render.rb +1 -1
  25. data/lib/action_controller/metal/instrumentation.rb +11 -9
  26. data/lib/action_controller/metal/live.rb +1 -1
  27. data/lib/action_controller/metal/logging.rb +20 -0
  28. data/lib/action_controller/metal/mime_responds.rb +6 -2
  29. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  30. data/lib/action_controller/metal/params_wrapper.rb +16 -11
  31. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  32. data/lib/action_controller/metal/redirecting.rb +1 -1
  33. data/lib/action_controller/metal/rendering.rb +6 -0
  34. data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
  35. data/lib/action_controller/metal/rescue.rb +1 -1
  36. data/lib/action_controller/metal/strong_parameters.rb +103 -15
  37. data/lib/action_controller/metal.rb +2 -2
  38. data/lib/action_controller/renderer.rb +23 -13
  39. data/lib/action_controller/test_case.rb +62 -56
  40. data/lib/action_controller.rb +2 -3
  41. data/lib/action_dispatch/http/cache.rb +12 -10
  42. data/lib/action_dispatch/http/content_security_policy.rb +11 -0
  43. data/lib/action_dispatch/http/filter_parameters.rb +1 -1
  44. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  45. data/lib/action_dispatch/http/headers.rb +3 -2
  46. data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
  47. data/lib/action_dispatch/http/mime_type.rb +29 -16
  48. data/lib/action_dispatch/http/parameters.rb +1 -19
  49. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  50. data/lib/action_dispatch/http/request.rb +24 -8
  51. data/lib/action_dispatch/http/response.rb +17 -16
  52. data/lib/action_dispatch/http/url.rb +3 -2
  53. data/lib/action_dispatch/journey/formatter.rb +53 -28
  54. data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
  55. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  56. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
  57. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  58. data/lib/action_dispatch/journey/nodes/node.rb +4 -3
  59. data/lib/action_dispatch/journey/parser.rb +13 -13
  60. data/lib/action_dispatch/journey/parser.y +1 -1
  61. data/lib/action_dispatch/journey/path/pattern.rb +13 -18
  62. data/lib/action_dispatch/journey/route.rb +7 -18
  63. data/lib/action_dispatch/journey/router/utils.rb +6 -4
  64. data/lib/action_dispatch/journey/router.rb +26 -30
  65. data/lib/action_dispatch/journey.rb +0 -2
  66. data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
  67. data/lib/action_dispatch/middleware/cookies.rb +67 -32
  68. data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
  69. data/lib/action_dispatch/middleware/debug_view.rb +1 -1
  70. data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
  71. data/lib/action_dispatch/middleware/executor.rb +1 -1
  72. data/lib/action_dispatch/middleware/host_authorization.rb +35 -35
  73. data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
  74. data/lib/action_dispatch/middleware/request_id.rb +4 -5
  75. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
  76. data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
  77. data/lib/action_dispatch/middleware/ssl.rb +9 -6
  78. data/lib/action_dispatch/middleware/stack.rb +18 -0
  79. data/lib/action_dispatch/middleware/static.rb +154 -93
  80. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -1
  82. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
  83. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
  85. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -3
  86. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +100 -8
  87. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  88. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
  89. data/lib/action_dispatch/railtie.rb +3 -2
  90. data/lib/action_dispatch/request/session.rb +2 -8
  91. data/lib/action_dispatch/request/utils.rb +26 -2
  92. data/lib/action_dispatch/routing/inspector.rb +8 -7
  93. data/lib/action_dispatch/routing/mapper.rb +102 -71
  94. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -19
  95. data/lib/action_dispatch/routing/redirection.rb +3 -3
  96. data/lib/action_dispatch/routing/route_set.rb +49 -41
  97. data/lib/action_dispatch/system_test_case.rb +29 -24
  98. data/lib/action_dispatch/system_testing/browser.rb +33 -27
  99. data/lib/action_dispatch/system_testing/driver.rb +6 -7
  100. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
  101. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
  102. data/lib/action_dispatch/testing/assertions/response.rb +2 -4
  103. data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
  104. data/lib/action_dispatch/testing/assertions.rb +1 -1
  105. data/lib/action_dispatch/testing/integration.rb +38 -27
  106. data/lib/action_dispatch/testing/test_process.rb +29 -4
  107. data/lib/action_dispatch/testing/test_request.rb +3 -3
  108. data/lib/action_dispatch.rb +3 -2
  109. data/lib/action_pack/gem_version.rb +3 -3
  110. data/lib/action_pack.rb +1 -1
  111. metadata +21 -23
  112. data/lib/action_controller/metal/force_ssl.rb +0 -58
  113. data/lib/action_dispatch/http/parameter_filter.rb +0 -12
  114. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  115. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  116. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -4,6 +4,7 @@ require "active_support/core_ext/hash/slice"
4
4
  require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/array/extract_options"
6
6
  require "active_support/core_ext/regexp"
7
+ require "active_support/core_ext/symbol/starts_ends_with"
7
8
  require "action_dispatch/routing/redirection"
8
9
  require "action_dispatch/routing/endpoint"
9
10
 
@@ -74,13 +75,17 @@ module ActionDispatch
74
75
  :default_action, :required_defaults, :ast, :scope_options
75
76
 
76
77
  def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
77
- options = scope[:options].merge(options) if scope[:options]
78
-
79
- defaults = (scope[:defaults] || {}).dup
80
- scope_constraints = scope[:constraints] || {}
81
- scope_options = scope[:options] || {}
78
+ scope_params = {
79
+ blocks: scope[:blocks] || [],
80
+ constraints: scope[:constraints] || {},
81
+ defaults: (scope[:defaults] || {}).dup,
82
+ module: scope[:module],
83
+ options: scope[:options] || {}
84
+ }
82
85
 
83
- new set, ast, defaults, controller, default_action, scope[:module], to, formatted, scope_constraints, scope_options, scope[:blocks] || [], via, options_constraints, anchor, options
86
+ new set: set, ast: ast, controller: controller, default_action: default_action,
87
+ to: to, formatted: formatted, via: via, options_constraints: options_constraints,
88
+ anchor: anchor, scope_params: scope_params, options: scope_params[:options].merge(options)
84
89
  end
85
90
 
86
91
  def self.check_via(via)
@@ -108,13 +113,12 @@ module ActionDispatch
108
113
  end
109
114
 
110
115
  def self.optional_format?(path, format)
111
- format != false && path !~ OPTIONAL_FORMAT_REGEX
116
+ format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
112
117
  end
113
118
 
114
- def initialize(set, ast, defaults, controller, default_action, modyoule, to, formatted, scope_constraints, scope_options, blocks, via, options_constraints, anchor, options)
115
- @defaults = defaults
116
- @set = set
117
-
119
+ def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:)
120
+ @defaults = scope_params[:defaults]
121
+ @set = set
118
122
  @to = intern(to)
119
123
  @default_controller = intern(controller)
120
124
  @default_action = intern(default_action)
@@ -122,23 +126,35 @@ module ActionDispatch
122
126
  @anchor = anchor
123
127
  @via = via
124
128
  @internal = options.delete(:internal)
125
- @scope_options = scope_options
126
-
127
- path_params = ast.find_all(&:symbol?).map(&:to_sym)
129
+ @scope_options = scope_params[:options]
130
+
131
+ path_params = []
132
+ wildcard_options = {}
133
+ ast.each do |node|
134
+ if node.symbol?
135
+ path_params << node.to_sym
136
+ elsif formatted != false && node.star?
137
+ # Add a constraint for wildcard route to make it non-greedy and match the
138
+ # optional format part of the route by default.
139
+ wildcard_options[node.name.to_sym] ||= /.+?/
140
+ elsif node.cat?
141
+ alter_regex_for_custom_routes(node)
142
+ end
143
+ end
128
144
 
129
- options = add_wildcard_options(options, formatted, ast)
145
+ options = wildcard_options.merge!(options)
130
146
 
131
- options = normalize_options!(options, path_params, modyoule)
147
+ options = normalize_options!(options, path_params, scope_params[:module])
132
148
 
133
149
  split_options = constraints(options, path_params)
134
150
 
135
- constraints = scope_constraints.merge Hash[split_options[:constraints] || []]
151
+ constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
136
152
 
137
153
  if options_constraints.is_a?(Hash)
138
154
  @defaults = Hash[options_constraints.find_all { |key, default|
139
155
  URL_OPTIONS.include?(key) && (String === default || Integer === default)
140
156
  }].merge @defaults
141
- @blocks = blocks
157
+ @blocks = scope_params[:blocks]
142
158
  constraints.merge! options_constraints
143
159
  else
144
160
  @blocks = blocks(options_constraints)
@@ -161,16 +177,20 @@ module ActionDispatch
161
177
  end
162
178
 
163
179
  def make_route(name, precedence)
164
- Journey::Route.new(name, application, path, conditions, required_defaults,
165
- defaults, request_method, precedence, scope_options, @internal)
180
+ Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
181
+ required_defaults: required_defaults, defaults: defaults,
182
+ request_method_match: request_method, precedence: precedence,
183
+ scope_options: scope_options, internal: @internal)
166
184
  end
167
185
 
168
186
  def application
169
187
  app(@blocks)
170
188
  end
171
189
 
190
+ JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
191
+
172
192
  def path
173
- build_path @ast, requirements, @anchor
193
+ Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
174
194
  end
175
195
 
176
196
  def conditions
@@ -191,16 +211,10 @@ module ActionDispatch
191
211
  end
192
212
  private :request_method
193
213
 
194
- JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
195
-
196
- def build_path(ast, requirements, anchor)
197
- pattern = Journey::Path::Pattern.new(ast, requirements, JOINED_SEPARATORS, anchor)
198
-
214
+ private
199
215
  # Find all the symbol nodes that are adjacent to literal nodes and alter
200
216
  # the regexp so that Journey will partition them into custom routes.
201
- ast.find_all { |node|
202
- next unless node.cat?
203
-
217
+ def alter_regex_for_custom_routes(node)
204
218
  if node.left.literal? && node.right.symbol?
205
219
  symbol = node.right
206
220
  elsif node.left.literal? && node.right.cat? && node.right.left.symbol?
@@ -209,36 +223,17 @@ module ActionDispatch
209
223
  symbol = node.left
210
224
  elsif node.left.symbol? && node.right.cat? && node.right.left.literal?
211
225
  symbol = node.left
212
- else
213
- next
214
226
  end
215
227
 
216
228
  if symbol
217
229
  symbol.regexp = /(?:#{Regexp.union(symbol.regexp, '-')})+/
218
230
  end
219
- }
220
-
221
- pattern
222
- end
223
- private :build_path
231
+ end
224
232
 
225
- private
226
233
  def intern(object)
227
234
  object.is_a?(String) ? -object : object
228
235
  end
229
236
 
230
- def add_wildcard_options(options, formatted, path_ast)
231
- # Add a constraint for wildcard route to make it non-greedy and match the
232
- # optional format part of the route by default.
233
- if formatted != false
234
- path_ast.grep(Journey::Nodes::Star).each_with_object({}) { |node, hash|
235
- hash[node.name.to_sym] ||= /.+?/
236
- }.merge options
237
- else
238
- options
239
- end
240
- end
241
-
242
237
  def normalize_options!(options, path_params, modyoule)
243
238
  if path_params.include?(:controller)
244
239
  raise ArgumentError, ":controller segment is not allowed within a namespace block" if modyoule
@@ -342,7 +337,7 @@ module ActionDispatch
342
337
 
343
338
  def split_to(to)
344
339
  if /#/.match?(to)
345
- to.split("#")
340
+ to.split("#").map!(&:-@)
346
341
  else
347
342
  []
348
343
  end
@@ -350,10 +345,10 @@ module ActionDispatch
350
345
 
351
346
  def add_controller_module(controller, modyoule)
352
347
  if modyoule && !controller.is_a?(Regexp)
353
- if %r{\A/}.match?(controller)
354
- controller[1..-1]
348
+ if controller&.start_with?("/")
349
+ -controller[1..-1]
355
350
  else
356
- [modyoule, controller].compact.join("/")
351
+ -[modyoule, controller].compact.join("/")
357
352
  end
358
353
  else
359
354
  controller
@@ -393,12 +388,23 @@ module ActionDispatch
393
388
  end
394
389
  end
395
390
 
396
- # Invokes Journey::Router::Utils.normalize_path and ensure that
397
- # (:locale) becomes (/:locale) instead of /(:locale). Except
398
- # for root cases, where the latter is the correct one.
391
+ # Invokes Journey::Router::Utils.normalize_path, then ensures that
392
+ # /(:locale) becomes (/:locale). Except for root cases, where the
393
+ # former is the correct one.
399
394
  def self.normalize_path(path)
400
395
  path = Journey::Router::Utils.normalize_path(path)
401
- path.gsub!(%r{/(\(+)/?}, '\1/') unless %r{^/(\(+[^)]+\)){1,}$}.match?(path)
396
+
397
+ # the path for a root URL at this point can be something like
398
+ # "/(/:locale)(/:platform)/(:browser)", and we would want
399
+ # "/(:locale)(/:platform)(/:browser)"
400
+
401
+ # reverse "/(", "/((" etc to "(/", "((/" etc
402
+ path.gsub!(%r{/(\(+)/?}, '\1/')
403
+ # if a path is all optional segments, change the leading "(/" back to
404
+ # "/(" so it evaluates to "/" when interpreted with no options.
405
+ # Unless, however, at least one secondary segment consists of a static
406
+ # part, ex. "(/:locale)(/pages/:page)"
407
+ path.sub!(%r{^(\(+)/}, '/\1') if %r{^(\(+[^)]+\))(\(+/:[^)]+\))*$}.match?(path)
402
408
  path
403
409
  end
404
410
 
@@ -555,7 +561,7 @@ module ActionDispatch
555
561
  # Constrains parameters with a hash of regular expressions
556
562
  # or an object that responds to <tt>matches?</tt>. In addition, constraints
557
563
  # other than path can also be specified with any object
558
- # that responds to <tt>===</tt> (eg. String, Array, Range, etc.).
564
+ # that responds to <tt>===</tt> (e.g. String, Array, Range, etc.).
559
565
  #
560
566
  # match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
561
567
  #
@@ -684,7 +690,7 @@ module ActionDispatch
684
690
 
685
691
  # We must actually delete prefix segment keys to avoid passing them to next url_for.
686
692
  _route.segment_keys.each { |k| options.delete(k) }
687
- _url_helpers.send("#{name}_path", prefix_options)
693
+ _url_helpers.public_send("#{name}_path", prefix_options)
688
694
  end
689
695
 
690
696
  app.routes.define_mounted_helper(name, script_namer)
@@ -744,6 +750,14 @@ module ActionDispatch
744
750
  map_method(:delete, args, &block)
745
751
  end
746
752
 
753
+ # Define a route that only recognizes HTTP OPTIONS.
754
+ # For supported arguments, see match[rdoc-ref:Base#match]
755
+ #
756
+ # options 'carrots', to: 'food#carrots'
757
+ def options(*args, &block)
758
+ map_method(:options, args, &block)
759
+ end
760
+
747
761
  private
748
762
  def map_method(method, args, &block)
749
763
  options = args.extract_options!
@@ -991,7 +1005,7 @@ module ActionDispatch
991
1005
  #
992
1006
  # Requests to routes can be constrained based on specific criteria:
993
1007
  #
994
- # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
1008
+ # constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do
995
1009
  # resources :iphones
996
1010
  # end
997
1011
  #
@@ -1001,7 +1015,7 @@ module ActionDispatch
1001
1015
  #
1002
1016
  # class Iphone
1003
1017
  # def self.matches?(request)
1004
- # request.env["HTTP_USER_AGENT"] =~ /iPhone/
1018
+ # /iPhone/.match?(request.env["HTTP_USER_AGENT"])
1005
1019
  # end
1006
1020
  # end
1007
1021
  #
@@ -1594,6 +1608,22 @@ module ActionDispatch
1594
1608
  !parent_resource.singleton? && @scope[:shallow]
1595
1609
  end
1596
1610
 
1611
+ def draw(name)
1612
+ path = @draw_paths.find do |_path|
1613
+ File.exist? "#{_path}/#{name}.rb"
1614
+ end
1615
+
1616
+ unless path
1617
+ msg = "Your router tried to #draw the external file #{name}.rb,\n" \
1618
+ "but the file was not found in:\n\n"
1619
+ msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n")
1620
+ raise ArgumentError, msg
1621
+ end
1622
+
1623
+ route_path = "#{path}/#{name}.rb"
1624
+ instance_eval(File.read(route_path), route_path.to_s)
1625
+ end
1626
+
1597
1627
  # Matches a URL pattern to one or more routes.
1598
1628
  # For more information, see match[rdoc-ref:Base#match].
1599
1629
  #
@@ -1674,20 +1704,20 @@ module ActionDispatch
1674
1704
 
1675
1705
  def apply_common_behavior_for(method, resources, options, &block)
1676
1706
  if resources.length > 1
1677
- resources.each { |r| send(method, r, options, &block) }
1707
+ resources.each { |r| public_send(method, r, options, &block) }
1678
1708
  return true
1679
1709
  end
1680
1710
 
1681
1711
  if options[:shallow]
1682
1712
  options.delete(:shallow)
1683
1713
  shallow do
1684
- send(method, resources.pop, options, &block)
1714
+ public_send(method, resources.pop, options, &block)
1685
1715
  end
1686
1716
  return true
1687
1717
  end
1688
1718
 
1689
1719
  if resource_scope?
1690
- nested { send(method, resources.pop, options, &block) }
1720
+ nested { public_send(method, resources.pop, options, &block) }
1691
1721
  return true
1692
1722
  end
1693
1723
 
@@ -1698,7 +1728,7 @@ module ActionDispatch
1698
1728
  scope_options = options.slice!(*RESOURCE_OPTIONS)
1699
1729
  unless scope_options.empty?
1700
1730
  scope(scope_options) do
1701
- send(method, resources.pop, options, &block)
1731
+ public_send(method, resources.pop, options, &block)
1702
1732
  end
1703
1733
  return true
1704
1734
  end
@@ -1828,7 +1858,7 @@ module ActionDispatch
1828
1858
  # and return nil in case it isn't. Otherwise, we pass the invalid name
1829
1859
  # forward so the underlying router engine treats it and raises an exception.
1830
1860
  if as.nil?
1831
- candidate unless candidate !~ /\A[_a-z]/i || has_named_route?(candidate)
1861
+ candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
1832
1862
  else
1833
1863
  candidate
1834
1864
  end
@@ -1882,7 +1912,7 @@ module ActionDispatch
1882
1912
  options_constraints = options.delete(:constraints) || {}
1883
1913
 
1884
1914
  path_types = paths.group_by(&:class)
1885
- path_types.fetch(String, []).each do |_path|
1915
+ (path_types[String] || []).each do |_path|
1886
1916
  route_options = options.dup
1887
1917
  if _path && option_path
1888
1918
  raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
@@ -1891,7 +1921,7 @@ module ActionDispatch
1891
1921
  decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
1892
1922
  end
1893
1923
 
1894
- path_types.fetch(Symbol, []).each do |action|
1924
+ (path_types[Symbol] || []).each do |action|
1895
1925
  route_options = options.dup
1896
1926
  decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
1897
1927
  end
@@ -1904,14 +1934,14 @@ module ActionDispatch
1904
1934
 
1905
1935
  path_without_format = path.sub(/\(\.:format\)$/, "")
1906
1936
  if using_match_shorthand?(path_without_format)
1907
- path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
1937
+ path_without_format.delete_prefix("/").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
1908
1938
  else
1909
1939
  nil
1910
1940
  end
1911
1941
  end
1912
1942
 
1913
1943
  def using_match_shorthand?(path)
1914
- path =~ %r{^/?[-\w]+/[-\w/]+$}
1944
+ %r{^/?[-\w]+/[-\w/]+$}.match?(path)
1915
1945
  end
1916
1946
 
1917
1947
  def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
@@ -1949,7 +1979,7 @@ module ActionDispatch
1949
1979
  name_for_action(options.delete(:as), action)
1950
1980
  end
1951
1981
 
1952
- path = Mapping.normalize_path URI.parser.escape(path), formatted
1982
+ path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted
1953
1983
  ast = Journey::Parser.parse path
1954
1984
 
1955
1985
  mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@@ -2272,6 +2302,7 @@ module ActionDispatch
2272
2302
 
2273
2303
  def initialize(set) #:nodoc:
2274
2304
  @set = set
2305
+ @draw_paths = set.draw_paths
2275
2306
  @scope = Scope.new(path_names: @set.resources_path_names)
2276
2307
  @concerns = {}
2277
2308
  end
@@ -145,6 +145,7 @@ module ActionDispatch
145
145
 
146
146
  %w(edit new).each do |action|
147
147
  module_eval <<-EOT, __FILE__, __LINE__ + 1
148
+ # frozen_string_literal: true
148
149
  def #{action}_polymorphic_url(record_or_hash, options = {})
149
150
  polymorphic_url_for_action("#{action}", record_or_hash, options)
150
151
  end
@@ -173,15 +174,15 @@ module ActionDispatch
173
174
  end
174
175
 
175
176
  class HelperMethodBuilder # :nodoc:
176
- CACHE = { "path" => {}, "url" => {} }
177
+ CACHE = { path: {}, url: {} }
177
178
 
178
179
  def self.get(action, type)
179
- type = type.to_s
180
+ type = type.to_sym
180
181
  CACHE[type].fetch(action) { build action, type }
181
182
  end
182
183
 
183
- def self.url; CACHE["url"][nil]; end
184
- def self.path; CACHE["path"][nil]; end
184
+ def self.url; CACHE[:url][nil]; end
185
+ def self.path; CACHE[:path][nil]; end
185
186
 
186
187
  def self.build(action, type)
187
188
  prefix = action ? "#{action}_" : ""
@@ -227,9 +228,9 @@ module ActionDispatch
227
228
  end
228
229
 
229
230
  if options.empty?
230
- recipient.send(method, *args)
231
+ recipient.public_send(method, *args)
231
232
  else
232
- recipient.send(method, *args, options)
233
+ recipient.public_send(method, *args, options)
233
234
  end
234
235
  end
235
236
 
@@ -246,7 +247,7 @@ module ActionDispatch
246
247
  end
247
248
 
248
249
  def handle_string_call(target, str)
249
- target.send get_method_for_string str
250
+ target.public_send get_method_for_string str
250
251
  end
251
252
 
252
253
  def handle_class(klass)
@@ -254,7 +255,7 @@ module ActionDispatch
254
255
  end
255
256
 
256
257
  def handle_class_call(target, klass)
257
- target.send get_method_for_class klass
258
+ target.public_send get_method_for_class klass
258
259
  end
259
260
 
260
261
  def handle_model(record)
@@ -276,7 +277,7 @@ module ActionDispatch
276
277
  mapping.call(target, [record], suffix == "path")
277
278
  else
278
279
  method, args = handle_model(record)
279
- target.send(method, *args)
280
+ target.public_send(method, *args)
280
281
  end
281
282
  end
282
283
 
@@ -286,12 +287,10 @@ module ActionDispatch
286
287
 
287
288
  args = []
288
289
 
289
- route = record_list.map do |parent|
290
+ route = record_list.map { |parent|
290
291
  case parent
291
- when Symbol
292
+ when Symbol, String
292
293
  parent.to_s
293
- when String
294
- raise(ArgumentError, "Please use symbols for polymorphic route arguments.")
295
294
  when Class
296
295
  args << parent
297
296
  parent.model_name.singular_route_key
@@ -299,14 +298,12 @@ module ActionDispatch
299
298
  args << parent.to_model
300
299
  parent.to_model.model_name.singular_route_key
301
300
  end
302
- end
301
+ }
303
302
 
304
303
  route <<
305
304
  case record
306
- when Symbol
305
+ when Symbol, String
307
306
  record.to_s
308
- when String
309
- raise(ArgumentError, "Please use symbols for polymorphic route arguments.")
310
307
  when Class
311
308
  @key_strategy.call record.model_name
312
309
  else
@@ -344,8 +341,8 @@ module ActionDispatch
344
341
  end
345
342
 
346
343
  [nil, "new", "edit"].each do |action|
347
- CACHE["url"][action] = build action, "url"
348
- CACHE["path"][action] = build action, "path"
344
+ CACHE[:url][action] = build action, "url"
345
+ CACHE[:path][action] = build action, "path"
349
346
  end
350
347
  end
351
348
  end
@@ -65,15 +65,15 @@ module ActionDispatch
65
65
  end
66
66
 
67
67
  def escape(params)
68
- Hash[params.map { |k, v| [k, Rack::Utils.escape(v)] }]
68
+ params.transform_values { |v| Rack::Utils.escape(v) }
69
69
  end
70
70
 
71
71
  def escape_fragment(params)
72
- Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_fragment(v)] }]
72
+ params.transform_values { |v| Journey::Router::Utils.escape_fragment(v) }
73
73
  end
74
74
 
75
75
  def escape_path(params)
76
- Hash[params.map { |k, v| [k, Journey::Router::Utils.escape_path(v)] }]
76
+ params.transform_values { |v| Journey::Router::Utils.escape_path(v) }
77
77
  end
78
78
  end
79
79