actionpack 6.1.7.5 → 7.1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -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,10 @@ module ActionDispatch
13
12
  class Mapper
14
13
  URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
15
14
 
16
- class Constraints < Routing::Endpoint #:nodoc:
15
+ cattr_accessor :route_source_locations, instance_accessor: false, default: false
16
+ cattr_accessor :backtrace_cleaner, instance_accessor: false, default: ActiveSupport::BacktraceCleaner.new
17
+
18
+ class Constraints < Routing::Endpoint # :nodoc:
17
19
  attr_reader :app, :constraints
18
20
 
19
21
  SERVE = ->(app, req) { app.serve req }
@@ -44,7 +46,7 @@ module ActionDispatch
44
46
  end
45
47
 
46
48
  def serve(req)
47
- return [ 404, { "X-Cascade" => "pass" }, [] ] unless matches?(req)
49
+ return [ 404, { Constants::X_CASCADE => "pass" }, [] ] unless matches?(req)
48
50
 
49
51
  @strategy.call @app, req
50
52
  end
@@ -67,11 +69,11 @@ module ActionDispatch
67
69
  end
68
70
  end
69
71
 
70
- class Mapping #:nodoc:
72
+ class Mapping # :nodoc:
71
73
  ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
72
74
  OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
73
75
 
74
- attr_reader :requirements, :defaults, :to, :default_controller,
76
+ attr_reader :path, :requirements, :defaults, :to, :default_controller,
75
77
  :default_action, :required_defaults, :ast, :scope_options
76
78
 
77
79
  def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@@ -122,31 +124,17 @@ module ActionDispatch
122
124
  @to = intern(to)
123
125
  @default_controller = intern(controller)
124
126
  @default_action = intern(default_action)
125
- @ast = ast
126
127
  @anchor = anchor
127
128
  @via = via
128
129
  @internal = options.delete(:internal)
129
130
  @scope_options = scope_params[:options]
131
+ ast = Journey::Ast.new(ast, formatted)
130
132
 
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
144
-
145
- options = wildcard_options.merge!(options)
133
+ options = ast.wildcard_options.merge!(options)
146
134
 
147
- options = normalize_options!(options, path_params, scope_params[:module])
135
+ options = normalize_options!(options, ast.path_params, scope_params[:module])
148
136
 
149
- split_options = constraints(options, path_params)
137
+ split_options = constraints(options, ast.path_params)
150
138
 
151
139
  constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
152
140
 
@@ -160,8 +148,8 @@ module ActionDispatch
160
148
  @blocks = blocks(options_constraints)
161
149
  end
162
150
 
163
- requirements, conditions = split_constraints path_params, constraints
164
- verify_regexp_requirements requirements.map(&:last).grep(Regexp)
151
+ requirements, conditions = split_constraints ast.path_params, constraints
152
+ verify_regexp_requirements requirements, ast.wildcard_options
165
153
 
166
154
  formats = normalize_format(formatted)
167
155
 
@@ -169,30 +157,29 @@ module ActionDispatch
169
157
  @conditions = Hash[conditions]
170
158
  @defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
171
159
 
172
- if path_params.include?(:action) && !@requirements.key?(:action)
160
+ if ast.path_params.include?(:action) && !@requirements.key?(:action)
173
161
  @defaults[:action] ||= "index"
174
162
  end
175
163
 
176
164
  @required_defaults = (split_options[:required_defaults] || []).map(&:first)
165
+
166
+ ast.requirements = @requirements
167
+ @path = Journey::Path::Pattern.new(ast, @requirements, JOINED_SEPARATORS, @anchor)
177
168
  end
178
169
 
170
+ JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
171
+
179
172
  def make_route(name, precedence)
180
173
  Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
181
174
  required_defaults: required_defaults, defaults: defaults,
182
175
  request_method_match: request_method, precedence: precedence,
183
- scope_options: scope_options, internal: @internal)
176
+ scope_options: scope_options, internal: @internal, source_location: route_source_location)
184
177
  end
185
178
 
186
179
  def application
187
180
  app(@blocks)
188
181
  end
189
182
 
190
- JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
191
-
192
- def path
193
- Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
194
- end
195
-
196
183
  def conditions
197
184
  build_conditions @conditions, @set.request_class
198
185
  end
@@ -212,24 +199,6 @@ module ActionDispatch
212
199
  private :request_method
213
200
 
214
201
  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
202
  def intern(object)
234
203
  object.is_a?(String) ? -object : object
235
204
  end
@@ -248,9 +217,16 @@ module ActionDispatch
248
217
  if to.respond_to?(:action) || to.respond_to?(:call)
249
218
  options
250
219
  else
251
- to_endpoint = split_to to
252
- controller = to_endpoint[0] || default_controller
253
- action = to_endpoint[1] || default_action
220
+ if to.nil?
221
+ controller = default_controller
222
+ action = default_action
223
+ elsif to.is_a?(String) && to.include?("#")
224
+ to_endpoint = to.split("#").map!(&:-@)
225
+ controller = to_endpoint[0]
226
+ action = to_endpoint[1]
227
+ else
228
+ raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#'"
229
+ end
254
230
 
255
231
  controller = add_controller_module(controller, modyoule)
256
232
 
@@ -280,14 +256,18 @@ module ActionDispatch
280
256
  end
281
257
  end
282
258
 
283
- def verify_regexp_requirements(requirements)
284
- requirements.each do |requirement|
285
- if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
259
+ def verify_regexp_requirements(requirements, wildcard_options)
260
+ requirements.each do |requirement, regex|
261
+ next unless regex.is_a? Regexp
262
+
263
+ if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
286
264
  raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
287
265
  end
288
266
 
289
- if requirement.multiline?
290
- raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
267
+ if regex.multiline?
268
+ next if wildcard_options.key?(requirement)
269
+
270
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.inspect}"
291
271
  end
292
272
  end
293
273
  end
@@ -335,14 +315,6 @@ module ActionDispatch
335
315
  hash
336
316
  end
337
317
 
338
- def split_to(to)
339
- if /#/.match?(to)
340
- to.split("#").map!(&:-@)
341
- else
342
- []
343
- end
344
- end
345
-
346
318
  def add_controller_module(controller, modyoule)
347
319
  if modyoule && !controller.is_a?(Regexp)
348
320
  if controller&.start_with?("/")
@@ -386,6 +358,15 @@ module ActionDispatch
386
358
  def dispatcher(raise_on_name_error)
387
359
  Routing::RouteSet::Dispatcher.new raise_on_name_error
388
360
  end
361
+
362
+ def route_source_location
363
+ if Mapper.route_source_locations
364
+ action_dispatch_dir = File.expand_path("..", __dir__)
365
+ caller_location = caller_locations.find { |location| !location.path.include?(action_dispatch_dir) }
366
+ cleaned_path = Mapper.backtrace_cleaner.clean([caller_location.path]).first
367
+ "#{cleaned_path}:#{caller_location.lineno}" if cleaned_path
368
+ end
369
+ end
389
370
  end
390
371
 
391
372
  # Invokes Journey::Router::Utils.normalize_path, then ensures that
@@ -420,10 +401,10 @@ module ActionDispatch
420
401
  #
421
402
  # If you want to expose your action to both GET and POST, use:
422
403
  #
423
- # # sets :controller, :action and :id in params
404
+ # # sets :controller, :action, and :id in params
424
405
  # match ':controller/:action/:id', via: [:get, :post]
425
406
  #
426
- # Note that +:controller+, +:action+ and +:id+ are interpreted as URL
407
+ # Note that +:controller+, +:action+, and +:id+ are interpreted as URL
427
408
  # query parameters and thus available through +params+ in an action.
428
409
  #
429
410
  # If you want to expose your action to GET, use +get+ in the router:
@@ -639,7 +620,7 @@ module ActionDispatch
639
620
  target_as = name_for_action(options[:as], path)
640
621
  options[:via] ||= :all
641
622
 
642
- match(path, options.merge(to: app, anchor: false, format: false))
623
+ match(path, { to: app, anchor: false, format: false }.merge(options))
643
624
 
644
625
  define_generate_prefix(app, target_as) if rails_app
645
626
  self
@@ -682,7 +663,7 @@ module ActionDispatch
682
663
 
683
664
  script_namer = ->(options) do
684
665
  prefix_options = options.slice(*_route.segment_keys)
685
- prefix_options[:relative_url_root] = ""
666
+ prefix_options[:script_name] = "" if options[:original_script_name]
686
667
 
687
668
  if options[:_recall]
688
669
  prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
@@ -778,7 +759,7 @@ module ActionDispatch
778
759
  # end
779
760
  #
780
761
  # This will create a number of routes for each of the posts and comments
781
- # controller. For <tt>Admin::PostsController</tt>, Rails will create:
762
+ # controller. For +Admin::PostsController+, \Rails will create:
782
763
  #
783
764
  # GET /admin/posts
784
765
  # GET /admin/posts/new
@@ -789,7 +770,7 @@ module ActionDispatch
789
770
  # DELETE /admin/posts/1
790
771
  #
791
772
  # If you want to route /posts (without the prefix /admin) to
792
- # <tt>Admin::PostsController</tt>, you could use
773
+ # +Admin::PostsController+, you could use
793
774
  #
794
775
  # scope module: "admin" do
795
776
  # resources :posts
@@ -838,7 +819,7 @@ module ActionDispatch
838
819
  #
839
820
  # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
840
821
  #
841
- # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
822
+ # # route /posts (without the prefix /admin) to +Admin::PostsController+
842
823
  # scope module: "admin" do
843
824
  # resources :posts
844
825
  # end
@@ -936,7 +917,7 @@ module ActionDispatch
936
917
  #
937
918
  # === Options
938
919
  #
939
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
920
+ # The +:path+, +:as+, +:module+, +:shallow_path+, and +:shallow_prefix+
940
921
  # options all default to the name of the namespace.
941
922
  #
942
923
  # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
@@ -947,7 +928,7 @@ module ActionDispatch
947
928
  # resources :posts
948
929
  # end
949
930
  #
950
- # # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
931
+ # # maps to +Sekret::PostsController+ rather than +Admin::PostsController+
951
932
  # namespace :admin, module: "sekret" do
952
933
  # resources :posts
953
934
  # end
@@ -956,7 +937,7 @@ module ActionDispatch
956
937
  # namespace :admin, as: "sekret" do
957
938
  # resources :posts
958
939
  # end
959
- def namespace(path, options = {})
940
+ def namespace(path, options = {}, &block)
960
941
  path = path.to_s
961
942
 
962
943
  defaults = {
@@ -967,7 +948,7 @@ module ActionDispatch
967
948
  }
968
949
 
969
950
  path_scope(options.delete(:path) { path }) do
970
- scope(defaults.merge!(options)) { yield }
951
+ scope(defaults.merge!(options), &block)
971
952
  end
972
953
  end
973
954
 
@@ -1026,8 +1007,8 @@ module ActionDispatch
1026
1007
  # constraints(Iphone) do
1027
1008
  # resources :iphones
1028
1009
  # end
1029
- def constraints(constraints = {})
1030
- scope(constraints: constraints) { yield }
1010
+ def constraints(constraints = {}, &block)
1011
+ scope(constraints: constraints, &block)
1031
1012
  end
1032
1013
 
1033
1014
  # Allows you to set default parameters for a route, such as this:
@@ -1112,7 +1093,7 @@ module ActionDispatch
1112
1093
 
1113
1094
  # Resource routing allows you to quickly declare all of the common routes
1114
1095
  # for a given resourceful controller. Instead of declaring separate routes
1115
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
1096
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
1116
1097
  # actions, a resourceful route declares them in a single line of code:
1117
1098
  #
1118
1099
  # resources :photos
@@ -1156,7 +1137,7 @@ module ActionDispatch
1156
1137
  RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
1157
1138
  CANONICAL_ACTIONS = %w(index create new show update destroy)
1158
1139
 
1159
- class Resource #:nodoc:
1140
+ class Resource # :nodoc:
1160
1141
  attr_reader :controller, :path, :param
1161
1142
 
1162
1143
  def initialize(entities, api_only, shallow, options = {})
@@ -1251,7 +1232,7 @@ module ActionDispatch
1251
1232
  def singleton?; false; end
1252
1233
  end
1253
1234
 
1254
- class SingletonResource < Resource #:nodoc:
1235
+ class SingletonResource < Resource # :nodoc:
1255
1236
  def initialize(entities, api_only, shallow, options)
1256
1237
  super
1257
1238
  @as = nil
@@ -1307,6 +1288,16 @@ module ActionDispatch
1307
1288
  # DELETE /profile
1308
1289
  # POST /profile
1309
1290
  #
1291
+ # If you want instances of a model to work with this resource via
1292
+ # record identification (e.g. in +form_with+ or +redirect_to+), you
1293
+ # will need to call resolve[rdoc-ref:CustomUrls#resolve]:
1294
+ #
1295
+ # resource :profile
1296
+ # resolve('Profile') { [:profile] }
1297
+ #
1298
+ # # Enables this to work with singular routes:
1299
+ # form_with(model: @profile) {}
1300
+ #
1310
1301
  # === Options
1311
1302
  # Takes same options as resources[rdoc-ref:#resources]
1312
1303
  def resource(*resources, &block)
@@ -1338,7 +1329,7 @@ module ActionDispatch
1338
1329
  self
1339
1330
  end
1340
1331
 
1341
- # In Rails, a resourceful route provides a mapping between HTTP verbs
1332
+ # In \Rails, a resourceful route provides a mapping between HTTP verbs
1342
1333
  # and URLs and controller actions. By convention, each action also maps
1343
1334
  # to particular CRUD operations in a database. A single entry in the
1344
1335
  # routing file, such as
@@ -1470,7 +1461,7 @@ module ActionDispatch
1470
1461
  #
1471
1462
  # === Examples
1472
1463
  #
1473
- # # routes call <tt>Admin::PostsController</tt>
1464
+ # # routes call +Admin::PostsController+
1474
1465
  # resources :posts, module: "admin"
1475
1466
  #
1476
1467
  # # resource actions are at /admin/posts.
@@ -1513,19 +1504,17 @@ module ActionDispatch
1513
1504
  # end
1514
1505
  # end
1515
1506
  #
1516
- # This will enable Rails to recognize paths such as <tt>/photos/search</tt>
1507
+ # This will enable \Rails to recognize paths such as <tt>/photos/search</tt>
1517
1508
  # with GET, and route to the search action of +PhotosController+. It will also
1518
1509
  # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
1519
1510
  # route helpers.
1520
- def collection
1511
+ def collection(&block)
1521
1512
  unless resource_scope?
1522
1513
  raise ArgumentError, "can't use collection outside resource(s) scope"
1523
1514
  end
1524
1515
 
1525
1516
  with_scope_level(:collection) do
1526
- path_scope(parent_resource.collection_scope) do
1527
- yield
1528
- end
1517
+ path_scope(parent_resource.collection_scope, &block)
1529
1518
  end
1530
1519
  end
1531
1520
 
@@ -1540,7 +1529,7 @@ module ActionDispatch
1540
1529
  # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
1541
1530
  # preview action of +PhotosController+. It will also create the
1542
1531
  # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
1543
- def member
1532
+ def member(&block)
1544
1533
  unless resource_scope?
1545
1534
  raise ArgumentError, "can't use member outside resource(s) scope"
1546
1535
  end
@@ -1548,27 +1537,25 @@ module ActionDispatch
1548
1537
  with_scope_level(:member) do
1549
1538
  if shallow?
1550
1539
  shallow_scope {
1551
- path_scope(parent_resource.member_scope) { yield }
1540
+ path_scope(parent_resource.member_scope, &block)
1552
1541
  }
1553
1542
  else
1554
- path_scope(parent_resource.member_scope) { yield }
1543
+ path_scope(parent_resource.member_scope, &block)
1555
1544
  end
1556
1545
  end
1557
1546
  end
1558
1547
 
1559
- def new
1548
+ def new(&block)
1560
1549
  unless resource_scope?
1561
1550
  raise ArgumentError, "can't use new outside resource(s) scope"
1562
1551
  end
1563
1552
 
1564
1553
  with_scope_level(:new) do
1565
- path_scope(parent_resource.new_scope(action_path(:new))) do
1566
- yield
1567
- end
1554
+ path_scope(parent_resource.new_scope(action_path(:new)), &block)
1568
1555
  end
1569
1556
  end
1570
1557
 
1571
- def nested
1558
+ def nested(&block)
1572
1559
  unless resource_scope?
1573
1560
  raise ArgumentError, "can't use nested outside resource(s) scope"
1574
1561
  end
@@ -1577,12 +1564,12 @@ module ActionDispatch
1577
1564
  if shallow? && shallow_nesting_depth >= 1
1578
1565
  shallow_scope do
1579
1566
  path_scope(parent_resource.nested_scope) do
1580
- scope(nested_options) { yield }
1567
+ scope(nested_options, &block)
1581
1568
  end
1582
1569
  end
1583
1570
  else
1584
1571
  path_scope(parent_resource.nested_scope) do
1585
- scope(nested_options) { yield }
1572
+ scope(nested_options, &block)
1586
1573
  end
1587
1574
  end
1588
1575
  end
@@ -1608,6 +1595,29 @@ module ActionDispatch
1608
1595
  !parent_resource.singleton? && @scope[:shallow]
1609
1596
  end
1610
1597
 
1598
+ # Loads another routes file with the given +name+ located inside the
1599
+ # +config/routes+ directory. In that file, you can use the normal
1600
+ # routing DSL, but <i>do not</i> surround it with a
1601
+ # +Rails.application.routes.draw+ block.
1602
+ #
1603
+ # # config/routes.rb
1604
+ # Rails.application.routes.draw do
1605
+ # draw :admin # Loads `config/routes/admin.rb`
1606
+ # draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
1607
+ # end
1608
+ #
1609
+ # # config/routes/admin.rb
1610
+ # namespace :admin do
1611
+ # resources :accounts
1612
+ # end
1613
+ #
1614
+ # # config/routes/third_party/some_gem.rb
1615
+ # mount SomeGem::Engine, at: "/some_gem"
1616
+ #
1617
+ # <b>CAUTION:</b> Use this feature with care. Having multiple routes
1618
+ # files can negatively impact discoverability and readability. For most
1619
+ # applications — even those with a few hundred routes — it's easier for
1620
+ # developers to have a single routes file.
1611
1621
  def draw(name)
1612
1622
  path = @draw_paths.find do |_path|
1613
1623
  File.exist? "#{_path}/#{name}.rb"
@@ -1641,7 +1651,7 @@ module ActionDispatch
1641
1651
  when Symbol
1642
1652
  options[:action] = to
1643
1653
  when String
1644
- if /#/.match?(to)
1654
+ if to.include?("#")
1645
1655
  options[:to] = to
1646
1656
  else
1647
1657
  options[:controller] = to
@@ -1664,7 +1674,7 @@ module ActionDispatch
1664
1674
  end
1665
1675
  end
1666
1676
 
1667
- # You can specify what Rails should route "/" to with the root method:
1677
+ # You can specify what \Rails should route "/" to with the root method:
1668
1678
  #
1669
1679
  # root to: 'pages#main'
1670
1680
  #
@@ -1676,7 +1686,7 @@ module ActionDispatch
1676
1686
  #
1677
1687
  # You should put the root route at the top of <tt>config/routes.rb</tt>,
1678
1688
  # because this means it will be matched first. As this is the most popular route
1679
- # of most Rails applications, this is beneficial.
1689
+ # of most \Rails applications, this is beneficial.
1680
1690
  def root(path, options = {})
1681
1691
  if path.is_a?(String)
1682
1692
  options[:to] = path
@@ -1768,10 +1778,10 @@ module ActionDispatch
1768
1778
  @scope = @scope.parent
1769
1779
  end
1770
1780
 
1771
- def resource_scope(resource)
1781
+ def resource_scope(resource, &block)
1772
1782
  @scope = @scope.new(scope_level_resource: resource)
1773
1783
 
1774
- controller(resource.resource_scope) { yield }
1784
+ controller(resource.resource_scope, &block)
1775
1785
  ensure
1776
1786
  @scope = @scope.parent
1777
1787
  end
@@ -1889,7 +1899,7 @@ module ActionDispatch
1889
1899
  end
1890
1900
 
1891
1901
  def map_match(paths, options)
1892
- if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
1902
+ if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
1893
1903
  raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
1894
1904
  end
1895
1905
 
@@ -2300,7 +2310,7 @@ module ActionDispatch
2300
2310
  NULL = Scope.new(nil, nil)
2301
2311
  end
2302
2312
 
2303
- def initialize(set) #:nodoc:
2313
+ def initialize(set) # :nodoc:
2304
2314
  @set = set
2305
2315
  @draw_paths = set.draw_paths
2306
2316
  @scope = Scope.new(path_names: @set.resources_path_names)
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Routing
5
+ # = Action Dispatch Routing \PolymorphicRoutes
6
+ #
5
7
  # Polymorphic URL helpers are methods for smart resolution to a named route call when
6
8
  # given an Active Record model instance. They are to be used in combination with
7
9
  # ActionController::Resources.
@@ -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"
@@ -20,10 +18,19 @@ module ActionDispatch
20
18
  def redirect?; true; end
21
19
 
22
20
  def call(env)
23
- serve Request.new env
21
+ ActiveSupport::Notifications.instrument("redirect.action_dispatch") do |payload|
22
+ request = Request.new(env)
23
+ response = build_response(request)
24
+
25
+ payload[:status] = @status
26
+ payload[:location] = response.headers["Location"]
27
+ payload[:request] = request
28
+
29
+ response.to_a
30
+ end
24
31
  end
25
32
 
26
- def serve(req)
33
+ def build_response(req)
27
34
  uri = URI.parse(path(req.path_parameters, req))
28
35
 
29
36
  unless uri.host
@@ -40,15 +47,15 @@ module ActionDispatch
40
47
 
41
48
  req.commit_flash
42
49
 
43
- body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
50
+ body = ""
44
51
 
45
52
  headers = {
46
53
  "Location" => uri.to_s,
47
- "Content-Type" => "text/html",
54
+ "Content-Type" => "text/html; charset=#{ActionDispatch::Response.default_charset}",
48
55
  "Content-Length" => body.length.to_s
49
56
  }
50
57
 
51
- [ status, headers, [body] ]
58
+ ActionDispatch::Response.new(status, headers, body)
52
59
  end
53
60
 
54
61
  def path(params, request)
@@ -61,7 +68,7 @@ module ActionDispatch
61
68
 
62
69
  private
63
70
  def relative_path?(path)
64
- path && !path.empty? && path[0] != "/"
71
+ path && !path.empty? && !path.start_with?("/")
65
72
  end
66
73
 
67
74
  def escape(params)
@@ -144,6 +151,11 @@ module ActionDispatch
144
151
  # This will redirect the user, while ignoring certain parts of the request, including query string, etc.
145
152
  # <tt>/stories</tt>, <tt>/stories?foo=bar</tt>, etc all redirect to <tt>/posts</tt>.
146
153
  #
154
+ # The redirect will use a <tt>301 Moved Permanently</tt> status code by
155
+ # default. This can be overridden with the +:status+ option:
156
+ #
157
+ # get "/stories" => redirect("/posts", status: 307)
158
+ #
147
159
  # You can also use interpolation in the supplied redirect argument:
148
160
  #
149
161
  # get 'docs/:article', to: redirect('/wiki/%{article}')