actionpack 6.1.7.5 → 7.0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -399
  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 +27 -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 +95 -22
  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