actionpack 6.1.7.3 → 7.0.8

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +320 -390
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +4 -5
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +13 -26
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +17 -12
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +9 -11
  16. data/lib/abstract_controller/translation.rb +5 -4
  17. data/lib/abstract_controller/url_for.rb +4 -6
  18. data/lib/action_controller/api.rb +7 -7
  19. data/lib/action_controller/base.rb +5 -4
  20. data/lib/action_controller/form_builder.rb +2 -2
  21. data/lib/action_controller/log_subscriber.rb +4 -3
  22. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  23. data/lib/action_controller/metal/conditional_get.rb +137 -102
  24. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  25. data/lib/action_controller/metal/cookies.rb +1 -1
  26. data/lib/action_controller/metal/data_streaming.rb +23 -31
  27. data/lib/action_controller/metal/etag_with_flash.rb +1 -1
  28. data/lib/action_controller/metal/exceptions.rb +19 -30
  29. data/lib/action_controller/metal/flash.rb +6 -2
  30. data/lib/action_controller/metal/head.rb +1 -1
  31. data/lib/action_controller/metal/helpers.rb +2 -2
  32. data/lib/action_controller/metal/http_authentication.rb +66 -39
  33. data/lib/action_controller/metal/instrumentation.rb +57 -52
  34. data/lib/action_controller/metal/live.rb +43 -2
  35. data/lib/action_controller/metal/mime_responds.rb +3 -3
  36. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  37. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  38. data/lib/action_controller/metal/redirecting.rb +111 -19
  39. data/lib/action_controller/metal/renderers.rb +12 -13
  40. data/lib/action_controller/metal/rendering.rb +121 -9
  41. data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
  42. data/lib/action_controller/metal/rescue.rb +5 -4
  43. data/lib/action_controller/metal/streaming.rb +7 -9
  44. data/lib/action_controller/metal/strong_parameters.rb +138 -115
  45. data/lib/action_controller/metal/testing.rb +9 -2
  46. data/lib/action_controller/metal/url_for.rb +3 -5
  47. data/lib/action_controller/metal.rb +10 -13
  48. data/lib/action_controller/railtie.rb +50 -6
  49. data/lib/action_controller/renderer.rb +1 -20
  50. data/lib/action_controller/test_case.rb +28 -7
  51. data/lib/action_controller.rb +2 -5
  52. data/lib/action_dispatch/http/cache.rb +20 -13
  53. data/lib/action_dispatch/http/content_security_policy.rb +113 -36
  54. data/lib/action_dispatch/http/filter_parameters.rb +4 -19
  55. data/lib/action_dispatch/http/headers.rb +1 -1
  56. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  57. data/lib/action_dispatch/http/mime_type.rb +9 -11
  58. data/lib/action_dispatch/http/parameters.rb +5 -5
  59. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  60. data/lib/action_dispatch/http/request.rb +27 -37
  61. data/lib/action_dispatch/http/response.rb +3 -20
  62. data/lib/action_dispatch/http/upload.rb +13 -2
  63. data/lib/action_dispatch/http/url.rb +11 -19
  64. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  65. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  67. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  68. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  69. data/lib/action_dispatch/journey/route.rb +6 -13
  70. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  71. data/lib/action_dispatch/journey/router.rb +1 -1
  72. data/lib/action_dispatch/journey/routes.rb +3 -3
  73. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  74. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  75. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +20 -13
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  78. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  79. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  80. data/lib/action_dispatch/middleware/executor.rb +3 -0
  81. data/lib/action_dispatch/middleware/flash.rb +17 -18
  82. data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
  83. data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
  84. data/lib/action_dispatch/middleware/request_id.rb +3 -3
  85. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
  89. data/lib/action_dispatch/middleware/stack.rb +27 -9
  90. data/lib/action_dispatch/middleware/static.rb +5 -9
  91. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  92. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  93. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  94. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  95. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  99. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  101. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  102. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  104. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
  105. data/lib/action_dispatch/railtie.rb +8 -2
  106. data/lib/action_dispatch/request/session.rb +43 -13
  107. data/lib/action_dispatch/routing/inspector.rb +1 -1
  108. data/lib/action_dispatch/routing/mapper.rb +82 -83
  109. data/lib/action_dispatch/routing/redirection.rb +5 -2
  110. data/lib/action_dispatch/routing/route_set.rb +17 -7
  111. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  112. data/lib/action_dispatch/routing/url_for.rb +24 -25
  113. data/lib/action_dispatch/routing.rb +5 -6
  114. data/lib/action_dispatch/system_test_case.rb +5 -5
  115. data/lib/action_dispatch/system_testing/browser.rb +3 -13
  116. data/lib/action_dispatch/system_testing/driver.rb +34 -10
  117. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  118. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  119. data/lib/action_dispatch/testing/assertions/response.rb +1 -1
  120. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  121. data/lib/action_dispatch/testing/assertions.rb +2 -5
  122. data/lib/action_dispatch/testing/integration.rb +6 -8
  123. data/lib/action_dispatch/testing/test_process.rb +3 -29
  124. data/lib/action_dispatch/testing/test_response.rb +20 -2
  125. data/lib/action_dispatch.rb +1 -0
  126. data/lib/action_pack/gem_version.rb +5 -5
  127. data/lib/action_pack/version.rb +1 -1
  128. metadata +16 -15
@@ -4,7 +4,6 @@ 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"
8
7
  require "action_dispatch/routing/redirection"
9
8
  require "action_dispatch/routing/endpoint"
10
9
 
@@ -13,7 +12,7 @@ module ActionDispatch
13
12
  class Mapper
14
13
  URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
15
14
 
16
- class Constraints < Routing::Endpoint #:nodoc:
15
+ class Constraints < Routing::Endpoint # :nodoc:
17
16
  attr_reader :app, :constraints
18
17
 
19
18
  SERVE = ->(app, req) { app.serve req }
@@ -67,11 +66,11 @@ module ActionDispatch
67
66
  end
68
67
  end
69
68
 
70
- class Mapping #:nodoc:
69
+ class Mapping # :nodoc:
71
70
  ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
72
71
  OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
73
72
 
74
- attr_reader :requirements, :defaults, :to, :default_controller,
73
+ attr_reader :path, :requirements, :defaults, :to, :default_controller,
75
74
  :default_action, :required_defaults, :ast, :scope_options
76
75
 
77
76
  def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@@ -122,31 +121,17 @@ module ActionDispatch
122
121
  @to = intern(to)
123
122
  @default_controller = intern(controller)
124
123
  @default_action = intern(default_action)
125
- @ast = ast
126
124
  @anchor = anchor
127
125
  @via = via
128
126
  @internal = options.delete(:internal)
129
127
  @scope_options = scope_params[:options]
128
+ ast = Journey::Ast.new(ast, formatted)
130
129
 
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
130
+ options = ast.wildcard_options.merge!(options)
144
131
 
145
- options = wildcard_options.merge!(options)
132
+ options = normalize_options!(options, ast.path_params, scope_params[:module])
146
133
 
147
- options = normalize_options!(options, path_params, scope_params[:module])
148
-
149
- split_options = constraints(options, path_params)
134
+ split_options = constraints(options, ast.path_params)
150
135
 
151
136
  constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
152
137
 
@@ -160,8 +145,8 @@ module ActionDispatch
160
145
  @blocks = blocks(options_constraints)
161
146
  end
162
147
 
163
- requirements, conditions = split_constraints path_params, constraints
164
- verify_regexp_requirements requirements.map(&:last).grep(Regexp)
148
+ requirements, conditions = split_constraints ast.path_params, constraints
149
+ verify_regexp_requirements requirements, ast.wildcard_options
165
150
 
166
151
  formats = normalize_format(formatted)
167
152
 
@@ -169,13 +154,18 @@ module ActionDispatch
169
154
  @conditions = Hash[conditions]
170
155
  @defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
171
156
 
172
- if path_params.include?(:action) && !@requirements.key?(:action)
157
+ if ast.path_params.include?(:action) && !@requirements.key?(:action)
173
158
  @defaults[:action] ||= "index"
174
159
  end
175
160
 
176
161
  @required_defaults = (split_options[:required_defaults] || []).map(&:first)
162
+
163
+ ast.requirements = @requirements
164
+ @path = Journey::Path::Pattern.new(ast, @requirements, JOINED_SEPARATORS, @anchor)
177
165
  end
178
166
 
167
+ JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
168
+
179
169
  def make_route(name, precedence)
180
170
  Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
181
171
  required_defaults: required_defaults, defaults: defaults,
@@ -187,12 +177,6 @@ module ActionDispatch
187
177
  app(@blocks)
188
178
  end
189
179
 
190
- JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
191
-
192
- def path
193
- Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
194
- end
195
-
196
180
  def conditions
197
181
  build_conditions @conditions, @set.request_class
198
182
  end
@@ -212,24 +196,6 @@ module ActionDispatch
212
196
  private :request_method
213
197
 
214
198
  private
215
- # Find all the symbol nodes that are adjacent to literal nodes and alter
216
- # the regexp so that Journey will partition them into custom routes.
217
- def alter_regex_for_custom_routes(node)
218
- if node.left.literal? && node.right.symbol?
219
- symbol = node.right
220
- elsif node.left.literal? && node.right.cat? && node.right.left.symbol?
221
- symbol = node.right.left
222
- elsif node.left.symbol? && node.right.literal?
223
- symbol = node.left
224
- elsif node.left.symbol? && node.right.cat? && node.right.left.literal?
225
- symbol = node.left
226
- end
227
-
228
- if symbol
229
- symbol.regexp = /(?:#{Regexp.union(symbol.regexp, '-')})+/
230
- end
231
- end
232
-
233
199
  def intern(object)
234
200
  object.is_a?(String) ? -object : object
235
201
  end
@@ -280,14 +246,18 @@ module ActionDispatch
280
246
  end
281
247
  end
282
248
 
283
- def verify_regexp_requirements(requirements)
284
- requirements.each do |requirement|
285
- if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
249
+ def verify_regexp_requirements(requirements, wildcard_options)
250
+ requirements.each do |requirement, regex|
251
+ next unless regex.is_a? Regexp
252
+
253
+ if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
286
254
  raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
287
255
  end
288
256
 
289
- if requirement.multiline?
290
- raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
257
+ if regex.multiline?
258
+ next if wildcard_options.key?(requirement)
259
+
260
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.inspect}"
291
261
  end
292
262
  end
293
263
  end
@@ -420,10 +390,10 @@ module ActionDispatch
420
390
  #
421
391
  # If you want to expose your action to both GET and POST, use:
422
392
  #
423
- # # sets :controller, :action and :id in params
393
+ # # sets :controller, :action, and :id in params
424
394
  # match ':controller/:action/:id', via: [:get, :post]
425
395
  #
426
- # Note that +:controller+, +:action+ and +:id+ are interpreted as URL
396
+ # Note that +:controller+, +:action+, and +:id+ are interpreted as URL
427
397
  # query parameters and thus available through +params+ in an action.
428
398
  #
429
399
  # If you want to expose your action to GET, use +get+ in the router:
@@ -639,7 +609,7 @@ module ActionDispatch
639
609
  target_as = name_for_action(options[:as], path)
640
610
  options[:via] ||= :all
641
611
 
642
- match(path, options.merge(to: app, anchor: false, format: false))
612
+ match(path, { to: app, anchor: false, format: false }.merge(options))
643
613
 
644
614
  define_generate_prefix(app, target_as) if rails_app
645
615
  self
@@ -936,7 +906,7 @@ module ActionDispatch
936
906
  #
937
907
  # === Options
938
908
  #
939
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
909
+ # The +:path+, +:as+, +:module+, +:shallow_path+, and +:shallow_prefix+
940
910
  # options all default to the name of the namespace.
941
911
  #
942
912
  # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
@@ -956,7 +926,7 @@ module ActionDispatch
956
926
  # namespace :admin, as: "sekret" do
957
927
  # resources :posts
958
928
  # end
959
- def namespace(path, options = {})
929
+ def namespace(path, options = {}, &block)
960
930
  path = path.to_s
961
931
 
962
932
  defaults = {
@@ -967,7 +937,7 @@ module ActionDispatch
967
937
  }
968
938
 
969
939
  path_scope(options.delete(:path) { path }) do
970
- scope(defaults.merge!(options)) { yield }
940
+ scope(defaults.merge!(options), &block)
971
941
  end
972
942
  end
973
943
 
@@ -1026,8 +996,8 @@ module ActionDispatch
1026
996
  # constraints(Iphone) do
1027
997
  # resources :iphones
1028
998
  # end
1029
- def constraints(constraints = {})
1030
- scope(constraints: constraints) { yield }
999
+ def constraints(constraints = {}, &block)
1000
+ scope(constraints: constraints, &block)
1031
1001
  end
1032
1002
 
1033
1003
  # Allows you to set default parameters for a route, such as this:
@@ -1112,7 +1082,7 @@ module ActionDispatch
1112
1082
 
1113
1083
  # Resource routing allows you to quickly declare all of the common routes
1114
1084
  # for a given resourceful controller. Instead of declaring separate routes
1115
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
1085
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
1116
1086
  # actions, a resourceful route declares them in a single line of code:
1117
1087
  #
1118
1088
  # resources :photos
@@ -1156,7 +1126,7 @@ module ActionDispatch
1156
1126
  RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
1157
1127
  CANONICAL_ACTIONS = %w(index create new show update destroy)
1158
1128
 
1159
- class Resource #:nodoc:
1129
+ class Resource # :nodoc:
1160
1130
  attr_reader :controller, :path, :param
1161
1131
 
1162
1132
  def initialize(entities, api_only, shallow, options = {})
@@ -1251,7 +1221,7 @@ module ActionDispatch
1251
1221
  def singleton?; false; end
1252
1222
  end
1253
1223
 
1254
- class SingletonResource < Resource #:nodoc:
1224
+ class SingletonResource < Resource # :nodoc:
1255
1225
  def initialize(entities, api_only, shallow, options)
1256
1226
  super
1257
1227
  @as = nil
@@ -1307,6 +1277,16 @@ module ActionDispatch
1307
1277
  # DELETE /profile
1308
1278
  # POST /profile
1309
1279
  #
1280
+ # If you want instances of a model to work with this resource via
1281
+ # record identification (e.g. in +form_with+ or +redirect_to+), you
1282
+ # will need to call resolve[rdoc-ref:CustomUrls#resolve]:
1283
+ #
1284
+ # resource :profile
1285
+ # resolve('Profile') { [:profile] }
1286
+ #
1287
+ # # Enables this to work with singular routes:
1288
+ # form_with(model: @profile) {}
1289
+ #
1310
1290
  # === Options
1311
1291
  # Takes same options as resources[rdoc-ref:#resources]
1312
1292
  def resource(*resources, &block)
@@ -1517,15 +1497,13 @@ module ActionDispatch
1517
1497
  # with GET, and route to the search action of +PhotosController+. It will also
1518
1498
  # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
1519
1499
  # route helpers.
1520
- def collection
1500
+ def collection(&block)
1521
1501
  unless resource_scope?
1522
1502
  raise ArgumentError, "can't use collection outside resource(s) scope"
1523
1503
  end
1524
1504
 
1525
1505
  with_scope_level(:collection) do
1526
- path_scope(parent_resource.collection_scope) do
1527
- yield
1528
- end
1506
+ path_scope(parent_resource.collection_scope, &block)
1529
1507
  end
1530
1508
  end
1531
1509
 
@@ -1540,7 +1518,7 @@ module ActionDispatch
1540
1518
  # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
1541
1519
  # preview action of +PhotosController+. It will also create the
1542
1520
  # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
1543
- def member
1521
+ def member(&block)
1544
1522
  unless resource_scope?
1545
1523
  raise ArgumentError, "can't use member outside resource(s) scope"
1546
1524
  end
@@ -1548,27 +1526,25 @@ module ActionDispatch
1548
1526
  with_scope_level(:member) do
1549
1527
  if shallow?
1550
1528
  shallow_scope {
1551
- path_scope(parent_resource.member_scope) { yield }
1529
+ path_scope(parent_resource.member_scope, &block)
1552
1530
  }
1553
1531
  else
1554
- path_scope(parent_resource.member_scope) { yield }
1532
+ path_scope(parent_resource.member_scope, &block)
1555
1533
  end
1556
1534
  end
1557
1535
  end
1558
1536
 
1559
- def new
1537
+ def new(&block)
1560
1538
  unless resource_scope?
1561
1539
  raise ArgumentError, "can't use new outside resource(s) scope"
1562
1540
  end
1563
1541
 
1564
1542
  with_scope_level(:new) do
1565
- path_scope(parent_resource.new_scope(action_path(:new))) do
1566
- yield
1567
- end
1543
+ path_scope(parent_resource.new_scope(action_path(:new)), &block)
1568
1544
  end
1569
1545
  end
1570
1546
 
1571
- def nested
1547
+ def nested(&block)
1572
1548
  unless resource_scope?
1573
1549
  raise ArgumentError, "can't use nested outside resource(s) scope"
1574
1550
  end
@@ -1577,12 +1553,12 @@ module ActionDispatch
1577
1553
  if shallow? && shallow_nesting_depth >= 1
1578
1554
  shallow_scope do
1579
1555
  path_scope(parent_resource.nested_scope) do
1580
- scope(nested_options) { yield }
1556
+ scope(nested_options, &block)
1581
1557
  end
1582
1558
  end
1583
1559
  else
1584
1560
  path_scope(parent_resource.nested_scope) do
1585
- scope(nested_options) { yield }
1561
+ scope(nested_options, &block)
1586
1562
  end
1587
1563
  end
1588
1564
  end
@@ -1608,6 +1584,29 @@ module ActionDispatch
1608
1584
  !parent_resource.singleton? && @scope[:shallow]
1609
1585
  end
1610
1586
 
1587
+ # Loads another routes file with the given +name+ located inside the
1588
+ # +config/routes+ directory. In that file, you can use the normal
1589
+ # routing DSL, but <i>do not</i> surround it with a
1590
+ # +Rails.application.routes.draw+ block.
1591
+ #
1592
+ # # config/routes.rb
1593
+ # Rails.application.routes.draw do
1594
+ # draw :admin # Loads `config/routes/admin.rb`
1595
+ # draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
1596
+ # end
1597
+ #
1598
+ # # config/routes/admin.rb
1599
+ # namespace :admin do
1600
+ # resources :accounts
1601
+ # end
1602
+ #
1603
+ # # config/routes/third_party/some_gem.rb
1604
+ # mount SomeGem::Engine, at: "/some_gem"
1605
+ #
1606
+ # <b>CAUTION:</b> Use this feature with care. Having multiple routes
1607
+ # files can negatively impact discoverability and readability. For most
1608
+ # applications — even those with a few hundred routes — it's easier for
1609
+ # developers to have a single routes file.
1611
1610
  def draw(name)
1612
1611
  path = @draw_paths.find do |_path|
1613
1612
  File.exist? "#{_path}/#{name}.rb"
@@ -1768,10 +1767,10 @@ module ActionDispatch
1768
1767
  @scope = @scope.parent
1769
1768
  end
1770
1769
 
1771
- def resource_scope(resource)
1770
+ def resource_scope(resource, &block)
1772
1771
  @scope = @scope.new(scope_level_resource: resource)
1773
1772
 
1774
- controller(resource.resource_scope) { yield }
1773
+ controller(resource.resource_scope, &block)
1775
1774
  ensure
1776
1775
  @scope = @scope.parent
1777
1776
  end
@@ -1889,7 +1888,7 @@ module ActionDispatch
1889
1888
  end
1890
1889
 
1891
1890
  def map_match(paths, options)
1892
- if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
1891
+ if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
1893
1892
  raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
1894
1893
  end
1895
1894
 
@@ -2300,7 +2299,7 @@ module ActionDispatch
2300
2299
  NULL = Scope.new(nil, nil)
2301
2300
  end
2302
2301
 
2303
- def initialize(set) #:nodoc:
2302
+ def initialize(set) # :nodoc:
2304
2303
  @set = set
2305
2304
  @draw_paths = set.draw_paths
2306
2305
  @scope = Scope.new(path_names: @set.resources_path_names)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_dispatch/http/request"
4
- require "active_support/core_ext/uri"
5
3
  require "active_support/core_ext/array/extract_options"
6
4
  require "rack/utils"
7
5
  require "action_controller/metal/exceptions"
@@ -144,6 +142,11 @@ module ActionDispatch
144
142
  # This will redirect the user, while ignoring certain parts of the request, including query string, etc.
145
143
  # <tt>/stories</tt>, <tt>/stories?foo=bar</tt>, etc all redirect to <tt>/posts</tt>.
146
144
  #
145
+ # The redirect will use a <tt>301 Moved Permanently</tt> status code by
146
+ # default. This can be overridden with the +:status+ option:
147
+ #
148
+ # get "/stories" => redirect("/posts", status: 307)
149
+ #
147
150
  # You can also use interpolation in the supplied redirect argument:
148
151
  #
149
152
  # get 'docs/:article', to: redirect('/wiki/%{article}')
@@ -6,7 +6,6 @@ require "active_support/core_ext/module/redefine_method"
6
6
  require "active_support/core_ext/module/remove_method"
7
7
  require "active_support/core_ext/array/extract_options"
8
8
  require "action_controller/metal/exceptions"
9
- require "action_dispatch/http/request"
10
9
  require "action_dispatch/routing/endpoint"
11
10
 
12
11
  module ActionDispatch
@@ -132,8 +131,8 @@ module ActionDispatch
132
131
  alias [] get
133
132
  alias clear clear!
134
133
 
135
- def each
136
- routes.each { |name, route| yield name, route }
134
+ def each(&block)
135
+ routes.each(&block)
137
136
  self
138
137
  end
139
138
 
@@ -197,7 +196,9 @@ module ActionDispatch
197
196
  def call(t, method_name, args, inner_options, url_strategy)
198
197
  if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
199
198
  options = t.url_options.merge @options
200
- options[:path] = optimized_helper(args)
199
+ path = optimized_helper(args)
200
+ path << "/" if options[:trailing_slash] && !path.end_with?("/")
201
+ options[:path] = path
201
202
 
202
203
  original_script_name = options.delete(:original_script_name)
203
204
  script_name = t._routes.find_script_name(options)
@@ -597,14 +598,14 @@ module ActionDispatch
597
598
  if route.segment_keys.include?(:controller)
598
599
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
599
600
  Using a dynamic :controller segment in a route is deprecated and
600
- will be removed in Rails 7.0.
601
+ will be removed in Rails 7.1.
601
602
  MSG
602
603
  end
603
604
 
604
605
  if route.segment_keys.include?(:action)
605
606
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
606
607
  Using a dynamic :action segment in a route is deprecated and
607
- will be removed in Rails 7.0.
608
+ will be removed in Rails 7.1.
608
609
  MSG
609
610
  end
610
611
 
@@ -821,10 +822,19 @@ module ActionDispatch
821
822
 
822
823
  route_with_params = generate(route_name, path_options, recall)
823
824
  path = route_with_params.path(method_name)
825
+
826
+ if options[:trailing_slash] && !options[:format] && !path.end_with?("/")
827
+ path += "/"
828
+ end
829
+
824
830
  params = route_with_params.params
825
831
 
826
832
  if options.key? :params
827
- params.merge! options[:params]
833
+ if options[:params]&.respond_to?(:to_hash)
834
+ params.merge! options[:params]
835
+ else
836
+ params[:params] = options[:params]
837
+ end
828
838
  end
829
839
 
830
840
  options[:path] = path
@@ -4,7 +4,7 @@ require "active_support/core_ext/array/extract_options"
4
4
 
5
5
  module ActionDispatch
6
6
  module Routing
7
- class RoutesProxy #:nodoc:
7
+ class RoutesProxy # :nodoc:
8
8
  include ActionDispatch::Routing::UrlFor
9
9
 
10
10
  attr_accessor :scope, :routes
@@ -6,16 +6,16 @@ module ActionDispatch
6
6
  # is also possible: a URL can be generated from one of your routing definitions.
7
7
  # URL generation functionality is centralized in this module.
8
8
  #
9
- # See ActionDispatch::Routing for general information about routing and routes.rb.
9
+ # See ActionDispatch::Routing for general information about routing and <tt>config/routes.rb</tt>.
10
10
  #
11
11
  # <b>Tip:</b> If you need to generate URLs from your models or some other place,
12
- # then ActionController::UrlFor is what you're looking for. Read on for
12
+ # then ActionDispatch::Routing::UrlFor is what you're looking for. Read on for
13
13
  # an introduction. In general, this module should not be included on its own,
14
- # as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
14
+ # as it is usually included by +url_helpers+ (as in <tt>Rails.application.routes.url_helpers</tt>).
15
15
  #
16
16
  # == URL generation from parameters
17
17
  #
18
- # As you may know, some functions, such as ActionController::Base#url_for
18
+ # As you may know, some functions, such as <tt>ActionController::Base#url_for</tt>
19
19
  # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
20
20
  # of parameters. For example, you've probably had the chance to write code
21
21
  # like this in one of your views:
@@ -24,12 +24,12 @@ module ActionDispatch
24
24
  # action: 'new', message: 'Welcome!') %>
25
25
  # # => <a href="/users/new?message=Welcome%21">Click here</a>
26
26
  #
27
- # link_to, and all other functions that require URL generation functionality,
28
- # actually use ActionController::UrlFor under the hood. And in particular,
29
- # they use the ActionController::UrlFor#url_for method. One can generate
27
+ # +link_to+, and all other functions that require URL generation functionality,
28
+ # actually use ActionDispatch::Routing::UrlFor under the hood. And in particular,
29
+ # they use the ActionDispatch::Routing::UrlFor#url_for method. One can generate
30
30
  # the same path as the above example by using the following code:
31
31
  #
32
- # include UrlFor
32
+ # include ActionDispatch::Routing::UrlFor
33
33
  # url_for(controller: 'users',
34
34
  # action: 'new',
35
35
  # message: 'Welcome!',
@@ -48,17 +48,17 @@ module ActionDispatch
48
48
  # host: 'www.example.com')
49
49
  # # => "http://www.example.com/users/new?message=Welcome%21"
50
50
  #
51
- # By default, all controllers and views have access to a special version of url_for,
52
- # that already knows what the current hostname is. So if you use url_for in your
51
+ # By default, all controllers and views have access to a special version of +url_for+,
52
+ # that already knows what the current hostname is. So if you use +url_for+ in your
53
53
  # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
54
54
  # argument.
55
55
  #
56
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
57
- # So within mailers, you only have to type +url_for+ instead of 'ActionController::UrlFor#url_for'
58
- # in full. However, mailers don't have hostname information, and you still have to provide
59
- # the +:host+ argument or set the default host that will be used in all mailers using the
60
- # configuration option +config.action_mailer.default_url_options+. For more information on
61
- # url_for in mailers read the ActionMailer#Base documentation.
56
+ # For convenience, mailers also include ActionDispatch::Routing::UrlFor. So
57
+ # within mailers, you can use url_for. However, mailers cannot access
58
+ # incoming web requests in order to derive hostname information, so you have
59
+ # to provide the +:host+ option or set the default host using
60
+ # +default_url_options+. For more information on url_for in mailers see the
61
+ # ActionMailer::Base documentation.
62
62
  #
63
63
  #
64
64
  # == URL generation for named routes
@@ -70,9 +70,9 @@ module ActionDispatch
70
70
  # resources :users
71
71
  #
72
72
  # This generates, among other things, the method <tt>users_path</tt>. By default,
73
- # this method is accessible from your controllers, views and mailers. If you need
73
+ # this method is accessible from your controllers, views, and mailers. If you need
74
74
  # to access this auto-generated method from other places (such as a model), then
75
- # you can do that by including Rails.application.routes.url_helpers in your class:
75
+ # you can do that by including <tt>Rails.application.routes.url_helpers</tt> in your class:
76
76
  #
77
77
  # class User < ActiveRecord::Base
78
78
  # include Rails.application.routes.url_helpers
@@ -103,11 +103,10 @@ module ActionDispatch
103
103
  include(*_url_for_modules) if respond_to?(:_url_for_modules)
104
104
  end
105
105
 
106
- def initialize(*)
106
+ def initialize(...)
107
107
  @_routes = nil
108
108
  super
109
109
  end
110
- ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
111
110
 
112
111
  # Hook overridden in controller to add request information
113
112
  # with +default_url_options+. Application logic should not
@@ -116,11 +115,11 @@ module ActionDispatch
116
115
  default_url_options
117
116
  end
118
117
 
119
- # Generate a URL based on the options provided, default_url_options and the
120
- # routes defined in routes.rb. The following options are supported:
118
+ # Generate a URL based on the options provided, +default_url_options+, and the
119
+ # routes defined in <tt>config/routes.rb</tt>. The following options are supported:
121
120
  #
122
121
  # * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
123
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
122
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to <tt>"http"</tt>.
124
123
  # * <tt>:host</tt> - Specifies the host the link should be targeted at.
125
124
  # If <tt>:only_path</tt> is false, this option must be
126
125
  # provided either explicitly, or via +default_url_options+.
@@ -135,7 +134,7 @@ module ActionDispatch
135
134
  # * <tt>:port</tt> - Optionally specify the port to connect to.
136
135
  # * <tt>:anchor</tt> - An anchor name to be appended to the path.
137
136
  # * <tt>:params</tt> - The query parameters to be appended to the path.
138
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
137
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in <tt>"/archive/2009/"</tt>.
139
138
  # * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
140
139
  #
141
140
  # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
@@ -155,7 +154,7 @@ module ActionDispatch
155
154
  # # => '/myapp/tasks/testing'
156
155
  #
157
156
  # Missing routes keys may be filled in from the current request's parameters
158
- # (e.g. +:controller+, +:action+, +:id+ and any other parameters that are
157
+ # (e.g. +:controller+, +:action+, +:id+, and any other parameters that are
159
158
  # placed in the path). Given that the current action has been reached
160
159
  # through <tt>GET /users/1</tt>:
161
160
  #
@@ -28,7 +28,7 @@ module ActionDispatch
28
28
  #
29
29
  # Resource routing allows you to quickly declare all of the common routes
30
30
  # for a given resourceful controller. Instead of declaring separate routes
31
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
31
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
32
32
  # actions, a resourceful route declares them in a single line of code:
33
33
  #
34
34
  # resources :photos
@@ -65,9 +65,8 @@ module ActionDispatch
65
65
  # resources :posts, :comments
66
66
  # end
67
67
  #
68
- # For more, see <tt>Routing::Mapper::Resources#resources</tt>,
69
- # <tt>Routing::Mapper::Scoping#namespace</tt>, and
70
- # <tt>Routing::Mapper::Scoping#scope</tt>.
68
+ # For more, see Routing::Mapper::Resources#resources,
69
+ # Routing::Mapper::Scoping#namespace, and Routing::Mapper::Scoping#scope.
71
70
  #
72
71
  # == Non-resourceful routes
73
72
  #
@@ -255,7 +254,7 @@ module ActionDispatch
255
254
  autoload :UrlFor
256
255
  autoload :PolymorphicRoutes
257
256
 
258
- SEPARATORS = %w( / . ? ) #:nodoc:
259
- HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
257
+ SEPARATORS = %w( / . ? ) # :nodoc:
258
+ HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] # :nodoc:
260
259
  end
261
260
  end