actionpack 4.2.10 → 7.2.0.rc1

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 (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +425 -497
  65. data/lib/action_controller.rb +44 -22
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,18 +1,18 @@
1
- module ActionController
2
- module Testing
3
- extend ActiveSupport::Concern
1
+ # frozen_string_literal: true
4
2
 
5
- include RackDelegation
6
-
7
- # TODO : Rewrite tests using controller.headers= to use Rack env
8
- def headers=(new_headers)
9
- @_response ||= ActionDispatch::Response.new
10
- @_response.headers.replace(new_headers)
11
- end
3
+ # :markup: markdown
12
4
 
5
+ module ActionController
6
+ module Testing
13
7
  # Behavior specific to functional tests
14
8
  module Functional # :nodoc:
15
- def set_response!(request)
9
+ def clear_instance_variables_between_requests
10
+ if defined?(@_ivars)
11
+ new_ivars = instance_variables - @_ivars
12
+ new_ivars.each { |ivar| remove_instance_variable(ivar) }
13
+ end
14
+
15
+ @_ivars = instance_variables
16
16
  end
17
17
 
18
18
  def recycle!
@@ -21,11 +21,5 @@ module ActionController
21
21
  self.params = nil
22
22
  end
23
23
  end
24
-
25
- module ClassMethods
26
- def before_filters
27
- _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
28
- end
29
- end
30
24
  end
31
25
  end
@@ -1,44 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
1
5
  module ActionController
2
- # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
3
- # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
6
+ # # Action Controller UrlFor
7
+ #
8
+ # Includes `url_for` into the host class. The class has to provide a `RouteSet`
9
+ # by implementing the `_routes` method. Otherwise, an exception will be raised.
4
10
  #
5
- # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
6
- # url options like the +host+. In order to do so, this module requires the host class
7
- # to implement +env+ and +request+, which need to be a Rack-compatible.
11
+ # In addition to AbstractController::UrlFor, this module accesses the HTTP layer
12
+ # to define URL options like the `host`. In order to do so, this module requires
13
+ # the host class to implement `env` which needs to be Rack-compatible, and
14
+ # `request` which returns an ActionDispatch::Request instance.
8
15
  #
9
- # class RootUrl
10
- # include ActionController::UrlFor
11
- # include Rails.application.routes.url_helpers
16
+ # class RootUrl
17
+ # include ActionController::UrlFor
18
+ # include Rails.application.routes.url_helpers
12
19
  #
13
- # delegate :env, :request, to: :controller
20
+ # delegate :env, :request, to: :controller
14
21
  #
15
- # def initialize(controller)
16
- # @controller = controller
17
- # @url = root_path # named route from the application.
22
+ # def initialize(controller)
23
+ # @controller = controller
24
+ # @url = root_path # named route from the application.
25
+ # end
18
26
  # end
19
- # end
20
27
  module UrlFor
21
28
  extend ActiveSupport::Concern
22
29
 
23
30
  include AbstractController::UrlFor
24
31
 
32
+ def initialize(...)
33
+ super
34
+ @_url_options = nil
35
+ end
36
+
25
37
  def url_options
26
38
  @_url_options ||= {
27
- :host => request.host,
28
- :port => request.optional_port,
29
- :protocol => request.protocol,
30
- :_recall => request.path_parameters
39
+ host: request.host,
40
+ port: request.optional_port,
41
+ protocol: request.protocol,
42
+ _recall: request.path_parameters
31
43
  }.merge!(super).freeze
32
44
 
33
- if (same_origin = _routes.equal?(env["action_dispatch.routes".freeze])) ||
34
- (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
35
- (original_script_name = env['ORIGINAL_SCRIPT_NAME'.freeze])
45
+ if (same_origin = _routes.equal?(request.routes)) ||
46
+ (script_name = request.engine_script_name(_routes)) ||
47
+ (original_script_name = request.original_script_name)
36
48
 
37
49
  options = @_url_options.dup
38
50
  if original_script_name
39
51
  options[:original_script_name] = original_script_name
40
52
  else
41
- options[:script_name] = same_origin ? request.script_name.dup : script_name
53
+ if same_origin
54
+ options[:script_name] = request.script_name.empty? ? "" : request.script_name.dup
55
+ else
56
+ options[:script_name] = script_name
57
+ end
42
58
  end
43
59
  options.freeze
44
60
  else
@@ -1,139 +1,218 @@
1
- require 'active_support/core_ext/array/extract_options'
2
- require 'action_dispatch/middleware/stack'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "action_dispatch/middleware/stack"
3
7
 
4
8
  module ActionController
5
- # Extend ActionDispatch middleware stack to make it aware of options
6
- # allowing the following syntax in controllers:
9
+ # # Action Controller MiddlewareStack
7
10
  #
8
- # class PostsController < ApplicationController
9
- # use AuthenticationMiddleware, except: [:index, :show]
10
- # end
11
+ # Extend ActionDispatch middleware stack to make it aware of options allowing
12
+ # the following syntax in controllers:
11
13
  #
12
- class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
13
- class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
14
- def initialize(klass, *args, &block)
15
- options = args.extract_options!
16
- @only = Array(options.delete(:only)).map(&:to_s)
17
- @except = Array(options.delete(:except)).map(&:to_s)
18
- args << options unless options.empty?
19
- super
14
+ # class PostsController < ApplicationController
15
+ # use AuthenticationMiddleware, except: [:index, :show]
16
+ # end
17
+ #
18
+ class MiddlewareStack < ActionDispatch::MiddlewareStack # :nodoc:
19
+ class Middleware < ActionDispatch::MiddlewareStack::Middleware # :nodoc:
20
+ def initialize(klass, args, actions, strategy, block)
21
+ @actions = actions
22
+ @strategy = strategy
23
+ super(klass, args, block)
20
24
  end
21
25
 
22
26
  def valid?(action)
23
- if @only.present?
24
- @only.include?(action)
25
- elsif @except.present?
26
- !@except.include?(action)
27
- else
28
- true
29
- end
27
+ @strategy.call @actions, action
30
28
  end
31
29
  end
32
30
 
33
- def build(action, app = Proc.new)
31
+ def build(action, app = nil, &block)
34
32
  action = action.to_s
35
33
 
36
- middlewares.reverse.inject(app) do |a, middleware|
34
+ middlewares.reverse.inject(app || block) do |a, middleware|
37
35
  middleware.valid?(action) ? middleware.build(a) : a
38
36
  end
39
37
  end
38
+
39
+ private
40
+ INCLUDE = ->(list, action) { list.include? action }
41
+ EXCLUDE = ->(list, action) { !list.include? action }
42
+ NULL = ->(list, action) { true }
43
+
44
+ def build_middleware(klass, args, block)
45
+ options = args.extract_options!
46
+ only = Array(options.delete(:only)).map(&:to_s)
47
+ except = Array(options.delete(:except)).map(&:to_s)
48
+ args << options unless options.empty?
49
+
50
+ strategy = NULL
51
+ list = nil
52
+
53
+ if only.any?
54
+ strategy = INCLUDE
55
+ list = only
56
+ elsif except.any?
57
+ strategy = EXCLUDE
58
+ list = except
59
+ end
60
+
61
+ Middleware.new(klass, args, list, strategy, block)
62
+ end
40
63
  end
41
64
 
42
- # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
65
+ # # Action Controller Metal
66
+ #
67
+ # `ActionController::Metal` is the simplest possible controller, providing a
43
68
  # valid Rack interface without the additional niceties provided by
44
- # <tt>ActionController::Base</tt>.
69
+ # ActionController::Base.
45
70
  #
46
71
  # A sample metal controller might look like this:
47
72
  #
48
- # class HelloController < ActionController::Metal
49
- # def index
50
- # self.response_body = "Hello World!"
73
+ # class HelloController < ActionController::Metal
74
+ # def index
75
+ # self.response_body = "Hello World!"
76
+ # end
51
77
  # end
52
- # end
53
78
  #
54
- # And then to route requests to your metal controller, you would add
55
- # something like this to <tt>config/routes.rb</tt>:
79
+ # And then to route requests to your metal controller, you would add something
80
+ # like this to `config/routes.rb`:
56
81
  #
57
- # get 'hello', to: HelloController.action(:index)
82
+ # get 'hello', to: HelloController.action(:index)
58
83
  #
59
- # The +action+ method returns a valid Rack application for the \Rails
60
- # router to dispatch to.
84
+ # The ::action method returns a valid Rack application for the Rails router to
85
+ # dispatch to.
61
86
  #
62
- # == Rendering Helpers
87
+ # ## Rendering Helpers
63
88
  #
64
- # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
65
- # views, partials, or other responses aside from explicitly calling of
66
- # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
67
- # add the render helpers you're used to having in a normal controller, you
68
- # can do the following:
89
+ # By default, `ActionController::Metal` provides no utilities for rendering
90
+ # views, partials, or other responses aside from some low-level setters such
91
+ # as #response_body=, #content_type=, and #status=. To add the render helpers
92
+ # you're used to having in a normal controller, you can do the following:
69
93
  #
70
- # class HelloController < ActionController::Metal
71
- # include AbstractController::Rendering
72
- # include ActionView::Layouts
73
- # append_view_path "#{Rails.root}/app/views"
94
+ # class HelloController < ActionController::Metal
95
+ # include AbstractController::Rendering
96
+ # include ActionView::Layouts
97
+ # append_view_path "#{Rails.root}/app/views"
74
98
  #
75
- # def index
76
- # render "hello/index"
99
+ # def index
100
+ # render "hello/index"
101
+ # end
77
102
  # end
78
- # end
79
103
  #
80
- # == Redirection Helpers
104
+ # ## Redirection Helpers
81
105
  #
82
106
  # To add redirection helpers to your metal controller, do the following:
83
107
  #
84
- # class HelloController < ActionController::Metal
85
- # include ActionController::Redirecting
86
- # include Rails.application.routes.url_helpers
108
+ # class HelloController < ActionController::Metal
109
+ # include ActionController::Redirecting
110
+ # include Rails.application.routes.url_helpers
87
111
  #
88
- # def index
89
- # redirect_to root_url
112
+ # def index
113
+ # redirect_to root_url
114
+ # end
90
115
  # end
91
- # end
92
116
  #
93
- # == Other Helpers
94
- #
95
- # You can refer to the modules included in <tt>ActionController::Base</tt> to see
96
- # other features you can bring into your metal controller.
117
+ # ## Other Helpers
97
118
  #
119
+ # You can refer to the modules included in ActionController::Base to see other
120
+ # features you can bring into your metal controller.
98
121
  class Metal < AbstractController::Base
99
122
  abstract!
100
123
 
101
- attr_internal_writer :env
124
+ # Returns the last part of the controller's name, underscored, without the
125
+ # ending `Controller`. For instance, `PostsController` returns `posts`.
126
+ # Namespaces are left out, so `Admin::PostsController` returns `posts` as well.
127
+ #
128
+ # #### Returns
129
+ # * `string`
130
+ def self.controller_name
131
+ @controller_name ||= (name.demodulize.delete_suffix("Controller").underscore unless anonymous?)
132
+ end
102
133
 
103
- def env
104
- @_env ||= {}
134
+ def self.make_response!(request)
135
+ ActionDispatch::Response.new.tap do |res|
136
+ res.request = request
137
+ end
105
138
  end
106
139
 
107
- # Returns the last part of the controller's name, underscored, without the ending
108
- # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
109
- # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
110
- #
111
- # ==== Returns
112
- # * <tt>string</tt>
113
- def self.controller_name
114
- @controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
140
+ def self.action_encoding_template(action) # :nodoc:
141
+ false
115
142
  end
116
143
 
117
- # Delegates to the class' <tt>controller_name</tt>
144
+ class << self
145
+ private
146
+ def inherited(subclass)
147
+ super
148
+ subclass.middleware_stack = middleware_stack.dup
149
+ subclass.class_eval do
150
+ @controller_name = nil
151
+ end
152
+ end
153
+ end
154
+
155
+ # Delegates to the class's ::controller_name.
118
156
  def controller_name
119
157
  self.class.controller_name
120
158
  end
121
159
 
122
- # The details below can be overridden to support a specific
123
- # Request and Response object. The default ActionController::Base
124
- # implementation includes RackDelegation, which makes a request
125
- # and response object available. You might wish to control the
126
- # environment and response manually for performance reasons.
160
+ ##
161
+ # :attr_reader: request
162
+ #
163
+ # The ActionDispatch::Request instance for the current request.
164
+ attr_internal :request
165
+
166
+ ##
167
+ # :attr_reader: response
168
+ #
169
+ # The ActionDispatch::Response instance for the current response.
170
+ attr_internal_reader :response
171
+
172
+ ##
173
+ # The ActionDispatch::Request::Session instance for the current request.
174
+ # See further details in the
175
+ # [Active Controller Session guide](https://guides.rubyonrails.org/action_controller_overview.html#session).
176
+ delegate :session, to: "@_request"
177
+
178
+ ##
179
+ # Delegates to ActionDispatch::Response#headers.
180
+ delegate :headers, to: "@_response"
181
+
182
+ ##
183
+ # Delegates to ActionDispatch::Response#status=
184
+ delegate :status=, to: "@_response"
185
+
186
+ ##
187
+ # Delegates to ActionDispatch::Response#location=
188
+ delegate :location=, to: "@_response"
189
+
190
+ ##
191
+ # Delegates to ActionDispatch::Response#content_type=
192
+ delegate :content_type=, to: "@_response"
193
+
194
+ ##
195
+ # Delegates to ActionDispatch::Response#status
196
+ delegate :status, to: "@_response"
197
+
198
+ ##
199
+ # Delegates to ActionDispatch::Response#location
200
+ delegate :location, to: "@_response"
201
+
202
+ ##
203
+ # Delegates to ActionDispatch::Response#content_type
204
+ delegate :content_type, to: "@_response"
127
205
 
128
- attr_internal :headers, :response, :request
129
- delegate :session, :to => "@_request"
206
+ ##
207
+ # Delegates to ActionDispatch::Response#media_type
208
+ delegate :media_type, to: "@_response"
130
209
 
131
210
  def initialize
132
- @_headers = {"Content-Type" => "text/html"}
133
- @_status = 200
134
211
  @_request = nil
135
212
  @_response = nil
213
+ @_response_body = nil
136
214
  @_routes = nil
215
+ @_params = nil
137
216
  super
138
217
  end
139
218
 
@@ -145,96 +224,115 @@ module ActionController
145
224
  @_params = val
146
225
  end
147
226
 
148
- # Basic implementations for content_type=, location=, and headers are
149
- # provided to reduce the dependency on the RackDelegation module
150
- # in Renderer and Redirector.
227
+ alias :response_code :status # :nodoc:
151
228
 
152
- def content_type=(type)
153
- headers["Content-Type"] = type.to_s
229
+ # Basic `url_for` that can be overridden for more robust functionality.
230
+ def url_for(string)
231
+ string
154
232
  end
155
233
 
156
- def content_type
157
- headers["Content-Type"]
234
+ def response_body=(body)
235
+ if body
236
+ body = [body] if body.is_a?(String)
237
+ response.body = body
238
+ super
239
+ else
240
+ response.reset_body!
241
+ end
158
242
  end
159
243
 
160
- def location
161
- headers["Location"]
244
+ # Tests if render or redirect has already happened.
245
+ def performed?
246
+ response_body || response.committed?
162
247
  end
163
248
 
164
- def location=(url)
165
- headers["Location"] = url
249
+ def dispatch(name, request, response) # :nodoc:
250
+ set_request!(request)
251
+ set_response!(response)
252
+ process(name)
253
+ request.commit_flash
254
+ to_a
166
255
  end
167
256
 
168
- # Basic url_for that can be overridden for more robust functionality
169
- def url_for(string)
170
- string
171
- end
257
+ def set_response!(response) # :nodoc:
258
+ if @_response
259
+ _, _, body = @_response
260
+ body.close if body.respond_to?(:close)
261
+ end
172
262
 
173
- def status
174
- @_status
263
+ @_response = response
175
264
  end
176
- alias :response_code :status # :nodoc:
177
265
 
178
- def status=(status)
179
- @_status = Rack::Utils.status_code(status)
180
- end
266
+ # Assign the response and mark it as committed. No further processing will
267
+ # occur.
268
+ def response=(response)
269
+ set_response!(response)
181
270
 
182
- def response_body=(body)
183
- body = [body] unless body.nil? || body.respond_to?(:each)
184
- super
271
+ # Force `performed?` to return true:
272
+ @_response_body = true
185
273
  end
186
274
 
187
- # Tests if render or redirect has already happened.
188
- def performed?
189
- response_body || (response && response.committed?)
190
- end
191
-
192
- def dispatch(name, request) #:nodoc:
275
+ def set_request!(request) # :nodoc:
193
276
  @_request = request
194
- @_env = request.env
195
- @_env['action_controller.instance'] = self
196
- process(name)
197
- to_a
277
+ @_request.controller_instance = self
198
278
  end
199
279
 
200
- def to_a #:nodoc:
201
- response ? response.to_a : [status, headers, response_body]
280
+ def to_a # :nodoc:
281
+ response.to_a
202
282
  end
203
283
 
204
- class_attribute :middleware_stack
205
- self.middleware_stack = ActionController::MiddlewareStack.new
206
-
207
- def self.inherited(base) # :nodoc:
208
- base.middleware_stack = middleware_stack.dup
209
- super
284
+ def reset_session
285
+ @_request.reset_session
210
286
  end
211
287
 
212
- # Pushes the given Rack middleware and its arguments to the bottom of the
213
- # middleware stack.
214
- def self.use(*args, &block)
215
- middleware_stack.use(*args, &block)
288
+ class_attribute :middleware_stack, default: ActionController::MiddlewareStack.new
289
+
290
+ class << self
291
+ # Pushes the given Rack middleware and its arguments to the bottom of the
292
+ # middleware stack.
293
+ def use(...)
294
+ middleware_stack.use(...)
295
+ end
216
296
  end
217
297
 
218
- # Alias for +middleware_stack+.
298
+ # The middleware stack used by this controller.
299
+ #
300
+ # By default uses a variation of ActionDispatch::MiddlewareStack which allows
301
+ # for the following syntax:
302
+ #
303
+ # class PostsController < ApplicationController
304
+ # use AuthenticationMiddleware, except: [:index, :show]
305
+ # end
306
+ #
307
+ # Read more about [Rails middleware stack]
308
+ # (https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack)
309
+ # in the guides.
219
310
  def self.middleware
220
311
  middleware_stack
221
312
  end
222
313
 
223
- # Makes the controller a Rack endpoint that runs the action in the given
224
- # +env+'s +action_dispatch.request.path_parameters+ key.
225
- def self.call(env)
226
- req = ActionDispatch::Request.new env
227
- action(req.path_parameters[:action]).call(env)
314
+ # Returns a Rack endpoint for the given action name.
315
+ def self.action(name)
316
+ app = lambda { |env|
317
+ req = ActionDispatch::Request.new(env)
318
+ res = make_response! req
319
+ new.dispatch(name, req, res)
320
+ }
321
+
322
+ if middleware_stack.any?
323
+ middleware_stack.build(name, app)
324
+ else
325
+ app
326
+ end
228
327
  end
229
328
 
230
- # Returns a Rack endpoint for the given action name.
231
- def self.action(name, klass = ActionDispatch::Request)
329
+ # Direct dispatch to the controller. Instantiates the controller, then executes
330
+ # the action named `name`.
331
+ def self.dispatch(name, req, res)
232
332
  if middleware_stack.any?
233
- middleware_stack.build(name) do |env|
234
- new.dispatch(name, klass.new(env))
235
- end
333
+ middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
236
334
  else
237
- lambda { |env| new.dispatch(name, klass.new(env)) }
335
+ new.dispatch(name, req, res)
238
336
  end
239
337
  end
240
338
  end