omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  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 +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  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 +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  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 +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  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 +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  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 +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  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 +691 -0
  65. data/lib/action_controller.rb +80 -0
  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 +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  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 +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  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 +284 -0
  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 +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d33d474bea49cdb46669b68c6760931543882ecf8e71a763115450ad027e8c6e
4
+ data.tar.gz: 411bc4578648aff7ce836bf341dbcac37c2b1430627c2b462b694a191913fc37
5
+ SHA512:
6
+ metadata.gz: 861232c9ddecbe87551fe6cf00b464cedba542bdb6c8be84be424ae3c082985ea1c84b8e5404b5d0be71137250582434df802774f1359e7626df1bdb415974df
7
+ data.tar.gz: c670f13c1bfc3a8ea58e3f7be5abbb986406d0c3f404bbe0f41c9f5dc01df2ccd301e1155f69d37dad4ff63648b4c108646b139cf70e9023c3593421702fdbe5
data/CHANGELOG.md ADDED
@@ -0,0 +1,129 @@
1
+ * Update `ActionController::Live` to use a thread-pool to reuse threads across requests.
2
+
3
+ *Adam Renberg Tamm*
4
+
5
+ * Introduce safer, more explicit params handling method with `params#expect` such that
6
+ `params.expect(table: [ :attr ])` replaces `params.require(:table).permit(:attr)`
7
+
8
+ Ensures params are filtered with consideration for the expected
9
+ types of values, improving handling of params and avoiding ignorable
10
+ errors caused by params tampering.
11
+
12
+ ```ruby
13
+ # If the url is altered to ?person=hacked
14
+ # Before
15
+ params.require(:person).permit(:name, :age, pets: [:name])
16
+ # raises NoMethodError, causing a 500 and potential error reporting
17
+
18
+ # After
19
+ params.expect(person: [ :name, :age, pets: [[:name]] ])
20
+ # raises ActionController::ParameterMissing, correctly returning a 400 error
21
+ ```
22
+
23
+ You may also notice the new double array `[[:name]]`. In order to
24
+ declare when a param is expected to be an array of parameter hashes,
25
+ this new double array syntax is used to explicitly declare an array.
26
+ `expect` requires you to declare expected arrays in this way, and will
27
+ ignore arrays that are passed when, for example, `pet: [:name]` is used.
28
+
29
+ In order to preserve compatibility, `permit` does not adopt the new
30
+ double array syntax and is therefore more permissive about unexpected
31
+ types. Using `expect` everywhere is recommended.
32
+
33
+ We suggest replacing `params.require(:person).permit(:name, :age)`
34
+ with the direct replacement `params.expect(person: [:name, :age])`
35
+ to prevent external users from manipulating params to trigger 500
36
+ errors. A 400 error will be returned instead, using public/400.html
37
+
38
+ Usage of `params.require(:id)` should likewise be replaced with
39
+ `params.expect(:id)` which is designed to ensure that `params[:id]`
40
+ is a scalar and not an array or hash, also requiring the param.
41
+
42
+ ```ruby
43
+ # Before
44
+ User.find(params.require(:id)) # allows an array, altering behavior
45
+
46
+ # After
47
+ User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
48
+ ```
49
+
50
+ *Martin Emde*
51
+
52
+ * System Testing: Disable Chrome's search engine choice by default in system tests.
53
+
54
+ *glaszig*
55
+
56
+ * Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
57
+
58
+ *Hartley McGuire*
59
+
60
+ * Remove `racc` dependency by manually writing `ActionDispatch::Journey::Scanner`.
61
+
62
+ *Gannon McGibbon*
63
+
64
+ * Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
65
+
66
+ *Gannon McGibbon*
67
+
68
+ * Allow bots to ignore `allow_browser`.
69
+
70
+ *Matthew Nguyen*
71
+
72
+ * Deprecate drawing routes with multiple paths to make routing faster.
73
+ You may use `with_options` or a loop to make drawing multiple paths easier.
74
+
75
+ ```ruby
76
+ # Before
77
+ get "/users", "/other_path", to: "users#index"
78
+
79
+ # After
80
+ get "/users", to: "users#index"
81
+ get "/other_path", to: "users#index"
82
+ ```
83
+
84
+ *Gannon McGibbon*
85
+
86
+ * Make `http_cache_forever` use `immutable: true`
87
+
88
+ *Nate Matykiewicz*
89
+
90
+ * Add `config.action_dispatch.strict_freshness`.
91
+
92
+ When set to `true`, the `ETag` header takes precedence over the `Last-Modified` header when both are present,
93
+ as specified by RFC 7232, Section 6.
94
+
95
+ Defaults to `false` to maintain compatibility with previous versions of Rails, but is enabled as part of
96
+ Rails 8.0 defaults.
97
+
98
+ *heka1024*
99
+
100
+ * Support `immutable` directive in Cache-Control
101
+
102
+ ```ruby
103
+ expires_in 1.minute, public: true, immutable: true
104
+ # Cache-Control: public, max-age=60, immutable
105
+ ```
106
+
107
+ *heka1024*
108
+
109
+ * Add `:wasm_unsafe_eval` mapping for `content_security_policy`
110
+
111
+ ```ruby
112
+ # Before
113
+ policy.script_src "'wasm-unsafe-eval'"
114
+
115
+ # After
116
+ policy.script_src :wasm_unsafe_eval
117
+ ```
118
+
119
+ *Joe Haig*
120
+
121
+ * Add `display_capture` and `keyboard_map` in `permissions_policy`
122
+
123
+ *Cyril Blaecke*
124
+
125
+ * Add `connect` route helper.
126
+
127
+ *Samuel Williams*
128
+
129
+ Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionpack/CHANGELOG.md) for previous changes.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) David Heinemeier Hansson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.rdoc ADDED
@@ -0,0 +1,57 @@
1
+ = Action Pack -- From request to response
2
+
3
+ Action Pack is a framework for handling and responding to web requests. It
4
+ provides mechanisms for *routing* (mapping request URLs to actions), defining
5
+ *controllers* that implement actions, and generating responses. In short, Action Pack
6
+ provides the controller layer in the MVC paradigm.
7
+
8
+ It consists of several modules:
9
+
10
+ * Action Dispatch, which parses information about the web request, handles
11
+ routing as defined by the user, and does advanced processing related to HTTP
12
+ such as MIME-type negotiation, decoding parameters in POST, PATCH, or PUT bodies,
13
+ handling HTTP caching logic, cookies and sessions.
14
+
15
+ * Action Controller, which provides a base controller class that can be
16
+ subclassed to implement filters and actions to handle requests. The result
17
+ of an action is typically content generated from views.
18
+
19
+ With the Ruby on \Rails framework, users only directly interface with the
20
+ Action Controller module. Necessary Action Dispatch functionality is activated
21
+ by default and Action View rendering is implicitly triggered by Action
22
+ Controller. However, these modules are designed to function on their own and
23
+ can be used outside of \Rails.
24
+
25
+ You can read more about Action Pack in the {Action Controller Overview}[https://guides.rubyonrails.org/action_controller_overview.html] guide.
26
+
27
+ == Download and installation
28
+
29
+ The latest version of Action Pack can be installed with RubyGems:
30
+
31
+ $ gem install actionpack
32
+
33
+ Source code can be downloaded as part of the \Rails project on GitHub:
34
+
35
+ * https://github.com/rails/rails/tree/main/actionpack
36
+
37
+
38
+ == License
39
+
40
+ Action Pack is released under the MIT license:
41
+
42
+ * https://opensource.org/licenses/MIT
43
+
44
+
45
+ == Support
46
+
47
+ API documentation is at:
48
+
49
+ * https://api.rubyonrails.org
50
+
51
+ Bug reports for the Ruby on \Rails project can be filed here:
52
+
53
+ * https://github.com/rails/rails/issues
54
+
55
+ Feature requests should be discussed on the rails-core mailing list here:
56
+
57
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module AbstractController
6
+ module AssetPaths # :nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ config_accessor :asset_host, :assets_dir, :javascripts_dir,
11
+ :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,299 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "abstract_controller/error"
6
+ require "active_support/configurable"
7
+ require "active_support/descendants_tracker"
8
+ require "active_support/core_ext/module/anonymous"
9
+ require "active_support/core_ext/module/attr_internal"
10
+
11
+ module AbstractController
12
+ # Raised when a non-existing controller action is triggered.
13
+ class ActionNotFound < StandardError
14
+ attr_reader :controller, :action # :nodoc:
15
+
16
+ def initialize(message = nil, controller = nil, action = nil) # :nodoc:
17
+ @controller = controller
18
+ @action = action
19
+ super(message)
20
+ end
21
+
22
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
23
+ include DidYouMean::Correctable # :nodoc:
24
+
25
+ def corrections # :nodoc:
26
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: controller.class.action_methods).correct(action)
27
+ end
28
+ end
29
+ end
30
+
31
+ # # Abstract Controller Base
32
+ #
33
+ # AbstractController::Base is a low-level API. Nobody should be using it
34
+ # directly, and subclasses (like ActionController::Base) are expected to provide
35
+ # their own `render` method, since rendering means different things depending on
36
+ # the context.
37
+ class Base
38
+ ##
39
+ # Returns the body of the HTTP response sent by the controller.
40
+ attr_internal :response_body
41
+
42
+ ##
43
+ # Returns the name of the action this controller is processing.
44
+ attr_internal :action_name
45
+
46
+ ##
47
+ # Returns the formats that can be processed by the controller.
48
+ attr_internal :formats
49
+
50
+ include ActiveSupport::Configurable
51
+ extend ActiveSupport::DescendantsTracker
52
+
53
+ class << self
54
+ attr_reader :abstract
55
+ alias_method :abstract?, :abstract
56
+
57
+ # Define a controller as abstract. See internal_methods for more details.
58
+ def abstract!
59
+ @abstract = true
60
+ end
61
+
62
+ def inherited(klass) # :nodoc:
63
+ # Define the abstract ivar on subclasses so that we don't get uninitialized ivar
64
+ # warnings
65
+ unless klass.instance_variable_defined?(:@abstract)
66
+ klass.instance_variable_set(:@abstract, false)
67
+ end
68
+ super
69
+ end
70
+
71
+ # A list of all internal methods for a controller. This finds the first abstract
72
+ # superclass of a controller, and gets a list of all public instance methods on
73
+ # that abstract class. Public instance methods of a controller would normally be
74
+ # considered action methods, so methods declared on abstract classes are being
75
+ # removed. (ActionController::Metal and ActionController::Base are defined as
76
+ # abstract)
77
+ def internal_methods
78
+ controller = self
79
+ methods = []
80
+
81
+ until controller.abstract?
82
+ methods += controller.public_instance_methods(false)
83
+ controller = controller.superclass
84
+ end
85
+
86
+ controller.public_instance_methods(true) - methods
87
+ end
88
+
89
+ # A list of method names that should be considered actions. This includes all
90
+ # public instance methods on a controller, less any internal methods (see
91
+ # internal_methods), adding back in any methods that are internal, but still
92
+ # exist on the class itself.
93
+ #
94
+ # #### Returns
95
+ # * `Set` - A set of all methods that should be considered actions.
96
+ #
97
+ def action_methods
98
+ @action_methods ||= begin
99
+ # All public instance methods of this class, including ancestors except for
100
+ # public instance methods of Base and its ancestors.
101
+ methods = public_instance_methods(true) - internal_methods
102
+ # Be sure to include shadowed public instance methods of this class.
103
+ methods.concat(public_instance_methods(false))
104
+ methods.map!(&:to_s)
105
+ methods.to_set
106
+ end
107
+ end
108
+
109
+ # action_methods are cached and there is sometimes a need to refresh them.
110
+ # ::clear_action_methods! allows you to do that, so next time you run
111
+ # action_methods, they will be recalculated.
112
+ def clear_action_methods!
113
+ @action_methods = nil
114
+ end
115
+
116
+ # Returns the full controller name, underscored, without the ending Controller.
117
+ #
118
+ # class MyApp::MyPostsController < AbstractController::Base
119
+ #
120
+ # end
121
+ #
122
+ # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
123
+ #
124
+ # #### Returns
125
+ # * `String`
126
+ #
127
+ def controller_path
128
+ @controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
129
+ end
130
+
131
+ # Refresh the cached action_methods when a new action_method is added.
132
+ def method_added(name)
133
+ super
134
+ clear_action_methods!
135
+ end
136
+
137
+ def eager_load! # :nodoc:
138
+ action_methods
139
+ nil
140
+ end
141
+ end
142
+
143
+ abstract!
144
+
145
+ # Calls the action going through the entire Action Dispatch stack.
146
+ #
147
+ # The actual method that is called is determined by calling #method_for_action.
148
+ # If no method can handle the action, then an AbstractController::ActionNotFound
149
+ # error is raised.
150
+ #
151
+ # #### Returns
152
+ # * `self`
153
+ #
154
+ def process(action, ...)
155
+ @_action_name = action.to_s
156
+
157
+ unless action_name = _find_action_name(@_action_name)
158
+ raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
159
+ end
160
+
161
+ @_response_body = nil
162
+
163
+ process_action(action_name, ...)
164
+ end
165
+
166
+ # Delegates to the class's ::controller_path.
167
+ def controller_path
168
+ self.class.controller_path
169
+ end
170
+
171
+ # Delegates to the class's ::action_methods.
172
+ def action_methods
173
+ self.class.action_methods
174
+ end
175
+
176
+ # Returns true if a method for the action is available and can be dispatched,
177
+ # false otherwise.
178
+ #
179
+ # Notice that `action_methods.include?("foo")` may return false and
180
+ # `available_action?("foo")` returns true because this method considers actions
181
+ # that are also available through other means, for example, implicit render
182
+ # ones.
183
+ #
184
+ # #### Parameters
185
+ # * `action_name` - The name of an action to be tested
186
+ #
187
+ def available_action?(action_name)
188
+ _find_action_name(action_name)
189
+ end
190
+
191
+ # Tests if a response body is set. Used to determine if the `process_action`
192
+ # callback needs to be terminated in AbstractController::Callbacks.
193
+ def performed?
194
+ response_body
195
+ end
196
+
197
+ # Returns true if the given controller is capable of rendering a path. A
198
+ # subclass of `AbstractController::Base` may return false. An Email controller
199
+ # for example does not support paths, only full URLs.
200
+ def self.supports_path?
201
+ true
202
+ end
203
+
204
+ def inspect # :nodoc:
205
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
206
+ end
207
+
208
+ private
209
+ # Returns true if the name can be considered an action because it has a method
210
+ # defined in the controller.
211
+ #
212
+ # #### Parameters
213
+ # * `name` - The name of an action to be tested
214
+ #
215
+ def action_method?(name)
216
+ self.class.action_methods.include?(name)
217
+ end
218
+
219
+ # Call the action. Override this in a subclass to modify the behavior around
220
+ # processing an action. This, and not #process, is the intended way to override
221
+ # action dispatching.
222
+ #
223
+ # Notice that the first argument is the method to be dispatched which is **not**
224
+ # necessarily the same as the action name.
225
+ def process_action(...)
226
+ send_action(...)
227
+ end
228
+
229
+ # Actually call the method associated with the action. Override this method if
230
+ # you wish to change how action methods are called, not to add additional
231
+ # behavior around it. For example, you would override #send_action if you want
232
+ # to inject arguments into the method.
233
+ alias send_action send
234
+
235
+ # If the action name was not found, but a method called "action_missing" was
236
+ # found, #method_for_action will return "_handle_action_missing". This method
237
+ # calls #action_missing with the current action name.
238
+ def _handle_action_missing(*args)
239
+ action_missing(@_action_name, *args)
240
+ end
241
+
242
+ # Takes an action name and returns the name of the method that will handle the
243
+ # action.
244
+ #
245
+ # It checks if the action name is valid and returns false otherwise.
246
+ #
247
+ # See method_for_action for more information.
248
+ #
249
+ # #### Parameters
250
+ # * `action_name` - An action name to find a method name for
251
+ #
252
+ #
253
+ # #### Returns
254
+ # * `string` - The name of the method that handles the action
255
+ # * false - No valid method name could be found.
256
+ #
257
+ # Raise `AbstractController::ActionNotFound`.
258
+ def _find_action_name(action_name)
259
+ _valid_action_name?(action_name) && method_for_action(action_name)
260
+ end
261
+
262
+ # Takes an action name and returns the name of the method that will handle the
263
+ # action. In normal cases, this method returns the same name as it receives. By
264
+ # default, if #method_for_action receives a name that is not an action, it will
265
+ # look for an #action_missing method and return "_handle_action_missing" if one
266
+ # is found.
267
+ #
268
+ # Subclasses may override this method to add additional conditions that should
269
+ # be considered an action. For instance, an HTTP controller with a template
270
+ # matching the action name is considered to exist.
271
+ #
272
+ # If you override this method to handle additional cases, you may also provide a
273
+ # method (like `_handle_method_missing`) to handle the case.
274
+ #
275
+ # If none of these conditions are true, and `method_for_action` returns `nil`,
276
+ # an `AbstractController::ActionNotFound` exception will be raised.
277
+ #
278
+ # #### Parameters
279
+ # * `action_name` - An action name to find a method name for
280
+ #
281
+ #
282
+ # #### Returns
283
+ # * `string` - The name of the method that handles the action
284
+ # * `nil` - No method name could be found.
285
+ #
286
+ def method_for_action(action_name)
287
+ if action_method?(action_name)
288
+ action_name
289
+ elsif respond_to?(:action_missing, true)
290
+ "_handle_action_missing"
291
+ end
292
+ end
293
+
294
+ # Checks if the action name is valid and returns false otherwise.
295
+ def _valid_action_name?(action_name)
296
+ !action_name.to_s.include? File::SEPARATOR
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module AbstractController
6
+ module Caching
7
+ # # Abstract Controller Caching Fragments
8
+ #
9
+ # Fragment caching is used for caching various blocks within views without
10
+ # caching the entire action as a whole. This is useful when certain elements of
11
+ # an action change frequently or depend on complicated state while other parts
12
+ # rarely change or can be shared amongst multiple parties. The caching is done
13
+ # using the `cache` helper available in the Action View. See
14
+ # ActionView::Helpers::CacheHelper for more information.
15
+ #
16
+ # While it's strongly recommended that you use key-based cache expiration (see
17
+ # links in CacheHelper for more information), it is also possible to manually
18
+ # expire caches. For example:
19
+ #
20
+ # expire_fragment('name_of_cache')
21
+ module Fragments
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ if respond_to?(:class_attribute)
26
+ class_attribute :fragment_cache_keys
27
+ else
28
+ mattr_writer :fragment_cache_keys
29
+ end
30
+
31
+ self.fragment_cache_keys = []
32
+
33
+ if respond_to?(:helper_method)
34
+ helper_method :combined_fragment_cache_key
35
+ end
36
+ end
37
+
38
+ module ClassMethods
39
+ # Allows you to specify controller-wide key prefixes for cache fragments. Pass
40
+ # either a constant `value`, or a block which computes a value each time a cache
41
+ # key is generated.
42
+ #
43
+ # For example, you may want to prefix all fragment cache keys with a global
44
+ # version identifier, so you can easily invalidate all caches.
45
+ #
46
+ # class ApplicationController
47
+ # fragment_cache_key "v1"
48
+ # end
49
+ #
50
+ # When it's time to invalidate all fragments, simply change the string constant.
51
+ # Or, progressively roll out the cache invalidation using a computed value:
52
+ #
53
+ # class ApplicationController
54
+ # fragment_cache_key do
55
+ # @account.id.odd? ? "v1" : "v2"
56
+ # end
57
+ # end
58
+ def fragment_cache_key(value = nil, &key)
59
+ self.fragment_cache_keys += [key || -> { value }]
60
+ end
61
+ end
62
+
63
+ # Given a key (as described in `expire_fragment`), returns a key array suitable
64
+ # for use in reading, writing, or expiring a cached fragment. All keys begin
65
+ # with `:views`, followed by `ENV["RAILS_CACHE_ID"]` or
66
+ # `ENV["RAILS_APP_VERSION"]` if set, followed by any controller-wide key prefix
67
+ # values, ending with the specified `key` value.
68
+ def combined_fragment_cache_key(key)
69
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
70
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
71
+
72
+ cache_key = [:views, ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"], head, tail]
73
+ cache_key.flatten!(1)
74
+ cache_key.compact!
75
+ cache_key
76
+ end
77
+
78
+ # Writes `content` to the location signified by `key` (see `expire_fragment` for
79
+ # acceptable formats).
80
+ def write_fragment(key, content, options = nil)
81
+ return content unless cache_configured?
82
+
83
+ key = combined_fragment_cache_key(key)
84
+ instrument_fragment_cache :write_fragment, key do
85
+ content = content.to_str
86
+ cache_store.write(key, content, options)
87
+ end
88
+ content
89
+ end
90
+
91
+ # Reads a cached fragment from the location signified by `key` (see
92
+ # `expire_fragment` for acceptable formats).
93
+ def read_fragment(key, options = nil)
94
+ return unless cache_configured?
95
+
96
+ key = combined_fragment_cache_key(key)
97
+ instrument_fragment_cache :read_fragment, key do
98
+ result = cache_store.read(key, options)
99
+ result.respond_to?(:html_safe) ? result.html_safe : result
100
+ end
101
+ end
102
+
103
+ # Check if a cached fragment from the location signified by `key` exists (see
104
+ # `expire_fragment` for acceptable formats).
105
+ def fragment_exist?(key, options = nil)
106
+ return unless cache_configured?
107
+ key = combined_fragment_cache_key(key)
108
+
109
+ instrument_fragment_cache :exist_fragment?, key do
110
+ cache_store.exist?(key, options)
111
+ end
112
+ end
113
+
114
+ # Removes fragments from the cache.
115
+ #
116
+ # `key` can take one of three forms:
117
+ #
118
+ # * String - This would normally take the form of a path, like
119
+ # `pages/45/notes`.
120
+ # * Hash - Treated as an implicit call to `url_for`, like `{ controller:
121
+ # 'pages', action: 'notes', id: 45}`
122
+ # * Regexp - Will remove any fragment that matches, so `%r{pages/\d*/notes}`
123
+ # might remove all notes. Make sure you don't use anchors in the regex (`^`
124
+ # or `$`) because the actual filename matched looks like
125
+ # `./cache/filename/path.cache`. Note: Regexp expiration is only supported
126
+ # on caches that can iterate over all keys (unlike memcached).
127
+ #
128
+ #
129
+ # `options` is passed through to the cache store's `delete` method (or
130
+ # `delete_matched`, for Regexp keys).
131
+ def expire_fragment(key, options = nil)
132
+ return unless cache_configured?
133
+ key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
134
+
135
+ instrument_fragment_cache :expire_fragment, key do
136
+ if key.is_a?(Regexp)
137
+ cache_store.delete_matched(key, options)
138
+ else
139
+ cache_store.delete(key, options)
140
+ end
141
+ end
142
+ end
143
+
144
+ def instrument_fragment_cache(name, key, &block) # :nodoc:
145
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
146
+ end
147
+ end
148
+ end
149
+ end