omg-actionpack 8.0.0.alpha1

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.
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