actionpack 6.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +311 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +58 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +267 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +150 -0
  10. data/lib/abstract_controller/callbacks.rb +224 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +32 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +67 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +271 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +81 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +280 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +151 -0
  32. data/lib/action_controller/metal/default_headers.rb +17 -0
  33. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  35. data/lib/action_controller/metal/exceptions.rb +74 -0
  36. data/lib/action_controller/metal/flash.rb +61 -0
  37. data/lib/action_controller/metal/force_ssl.rb +58 -0
  38. data/lib/action_controller/metal/head.rb +60 -0
  39. data/lib/action_controller/metal/helpers.rb +122 -0
  40. data/lib/action_controller/metal/http_authentication.rb +518 -0
  41. data/lib/action_controller/metal/implicit_render.rb +63 -0
  42. data/lib/action_controller/metal/instrumentation.rb +105 -0
  43. data/lib/action_controller/metal/live.rb +314 -0
  44. data/lib/action_controller/metal/mime_responds.rb +324 -0
  45. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  46. data/lib/action_controller/metal/params_wrapper.rb +297 -0
  47. data/lib/action_controller/metal/redirecting.rb +133 -0
  48. data/lib/action_controller/metal/renderers.rb +181 -0
  49. data/lib/action_controller/metal/rendering.rb +122 -0
  50. data/lib/action_controller/metal/request_forgery_protection.rb +456 -0
  51. data/lib/action_controller/metal/rescue.rb +28 -0
  52. data/lib/action_controller/metal/streaming.rb +223 -0
  53. data/lib/action_controller/metal/strong_parameters.rb +1105 -0
  54. data/lib/action_controller/metal/testing.rb +16 -0
  55. data/lib/action_controller/metal/url_for.rb +58 -0
  56. data/lib/action_controller/railtie.rb +89 -0
  57. data/lib/action_controller/railties/helpers.rb +24 -0
  58. data/lib/action_controller/renderer.rb +130 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +626 -0
  61. data/lib/action_dispatch.rb +114 -0
  62. data/lib/action_dispatch/http/cache.rb +226 -0
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +284 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +86 -0
  66. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  67. data/lib/action_dispatch/http/headers.rb +132 -0
  68. data/lib/action_dispatch/http/mime_negotiation.rb +177 -0
  69. data/lib/action_dispatch/http/mime_type.rb +350 -0
  70. data/lib/action_dispatch/http/mime_types.rb +50 -0
  71. data/lib/action_dispatch/http/parameter_filter.rb +12 -0
  72. data/lib/action_dispatch/http/parameters.rb +136 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  74. data/lib/action_dispatch/http/request.rb +427 -0
  75. data/lib/action_dispatch/http/response.rb +534 -0
  76. data/lib/action_dispatch/http/upload.rb +92 -0
  77. data/lib/action_dispatch/http/url.rb +350 -0
  78. data/lib/action_dispatch/journey.rb +7 -0
  79. data/lib/action_dispatch/journey/formatter.rb +189 -0
  80. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  81. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  82. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  83. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  84. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  85. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  86. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +141 -0
  88. data/lib/action_dispatch/journey/parser.rb +199 -0
  89. data/lib/action_dispatch/journey/parser.y +50 -0
  90. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +203 -0
  92. data/lib/action_dispatch/journey/route.rb +204 -0
  93. data/lib/action_dispatch/journey/router.rb +153 -0
  94. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  95. data/lib/action_dispatch/journey/routes.rb +81 -0
  96. data/lib/action_dispatch/journey/scanner.rb +71 -0
  97. data/lib/action_dispatch/journey/visitors.rb +268 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  102. data/lib/action_dispatch/middleware/callbacks.rb +34 -0
  103. data/lib/action_dispatch/middleware/cookies.rb +663 -0
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +185 -0
  105. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  106. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -0
  108. data/lib/action_dispatch/middleware/executor.rb +21 -0
  109. data/lib/action_dispatch/middleware/flash.rb +300 -0
  110. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +61 -0
  112. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  113. data/lib/action_dispatch/middleware/remote_ip.rb +181 -0
  114. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +113 -0
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  120. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  121. data/lib/action_dispatch/middleware/stack.rb +148 -0
  122. data/lib/action_dispatch/middleware/static.rb +129 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +24 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +29 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +38 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +165 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  148. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  149. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +203 -0
  150. data/lib/action_dispatch/railtie.rb +58 -0
  151. data/lib/action_dispatch/request/session.rb +242 -0
  152. data/lib/action_dispatch/request/utils.rb +78 -0
  153. data/lib/action_dispatch/routing.rb +261 -0
  154. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  155. data/lib/action_dispatch/routing/inspector.rb +274 -0
  156. data/lib/action_dispatch/routing/mapper.rb +2289 -0
  157. data/lib/action_dispatch/routing/polymorphic_routes.rb +351 -0
  158. data/lib/action_dispatch/routing/redirection.rb +201 -0
  159. data/lib/action_dispatch/routing/route_set.rb +887 -0
  160. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  161. data/lib/action_dispatch/routing/url_for.rb +237 -0
  162. data/lib/action_dispatch/system_test_case.rb +168 -0
  163. data/lib/action_dispatch/system_testing/browser.rb +80 -0
  164. data/lib/action_dispatch/system_testing/driver.rb +68 -0
  165. data/lib/action_dispatch/system_testing/server.rb +31 -0
  166. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +97 -0
  167. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +33 -0
  168. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  169. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  170. data/lib/action_dispatch/testing/assertions.rb +24 -0
  171. data/lib/action_dispatch/testing/assertions/response.rb +106 -0
  172. data/lib/action_dispatch/testing/assertions/routing.rb +234 -0
  173. data/lib/action_dispatch/testing/integration.rb +659 -0
  174. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  175. data/lib/action_dispatch/testing/test_process.rb +50 -0
  176. data/lib/action_dispatch/testing/test_request.rb +71 -0
  177. data/lib/action_dispatch/testing/test_response.rb +25 -0
  178. data/lib/action_pack.rb +26 -0
  179. data/lib/action_pack/gem_version.rb +17 -0
  180. data/lib/action_pack/version.rb +10 -0
  181. metadata +329 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ module Caching
5
+ extend ActiveSupport::Concern
6
+ extend ActiveSupport::Autoload
7
+
8
+ eager_autoload do
9
+ autoload :Fragments
10
+ end
11
+
12
+ module ConfigMethods
13
+ def cache_store
14
+ config.cache_store
15
+ end
16
+
17
+ def cache_store=(store)
18
+ config.cache_store = ActiveSupport::Cache.lookup_store(store)
19
+ end
20
+
21
+ private
22
+ def cache_configured?
23
+ perform_caching && cache_store
24
+ end
25
+ end
26
+
27
+ include ConfigMethods
28
+ include AbstractController::Caching::Fragments
29
+
30
+ included do
31
+ extend ConfigMethods
32
+
33
+ config_accessor :default_static_extension
34
+ self.default_static_extension ||= ".html"
35
+
36
+ config_accessor :perform_caching
37
+ self.perform_caching = true if perform_caching.nil?
38
+
39
+ config_accessor :enable_fragment_cache_logging
40
+ self.enable_fragment_cache_logging = false
41
+
42
+ class_attribute :_view_cache_dependencies, default: []
43
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
44
+ end
45
+
46
+ module ClassMethods
47
+ def view_cache_dependency(&dependency)
48
+ self._view_cache_dependencies += [dependency]
49
+ end
50
+ end
51
+
52
+ def view_cache_dependencies
53
+ self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
54
+ end
55
+
56
+ private
57
+ # Convenience accessor.
58
+ def cache(key, options = {}, &block) # :doc:
59
+ if cache_configured?
60
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
61
+ else
62
+ yield
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ module Caching
5
+ # Fragment caching is used for caching various blocks within
6
+ # views without caching the entire action as a whole. This is
7
+ # useful when certain elements of an action change frequently or
8
+ # depend on complicated state while other parts rarely change or
9
+ # can be shared amongst multiple parties. The caching is done using
10
+ # the +cache+ helper available in the Action View. See
11
+ # ActionView::Helpers::CacheHelper for more information.
12
+ #
13
+ # While it's strongly recommended that you use key-based cache
14
+ # expiration (see links in CacheHelper for more information),
15
+ # it is also possible to manually expire caches. For example:
16
+ #
17
+ # expire_fragment('name_of_cache')
18
+ module Fragments
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ if respond_to?(:class_attribute)
23
+ class_attribute :fragment_cache_keys
24
+ else
25
+ mattr_writer :fragment_cache_keys
26
+ end
27
+
28
+ self.fragment_cache_keys = []
29
+
30
+ if respond_to?(:helper_method)
31
+ helper_method :combined_fragment_cache_key
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ # Allows you to specify controller-wide key prefixes for
37
+ # cache fragments. Pass either a constant +value+, or a block
38
+ # which computes a value each time a cache key is generated.
39
+ #
40
+ # For example, you may want to prefix all fragment cache keys
41
+ # with a global version identifier, so you can easily
42
+ # invalidate all caches.
43
+ #
44
+ # class ApplicationController
45
+ # fragment_cache_key "v1"
46
+ # end
47
+ #
48
+ # When it's time to invalidate all fragments, simply change
49
+ # the string constant. Or, progressively roll out the cache
50
+ # invalidation using a computed value:
51
+ #
52
+ # class ApplicationController
53
+ # fragment_cache_key do
54
+ # @account.id.odd? ? "v1" : "v2"
55
+ # end
56
+ # end
57
+ def fragment_cache_key(value = nil, &key)
58
+ self.fragment_cache_keys += [key || -> { value }]
59
+ end
60
+ end
61
+
62
+ # Given a key (as described in +expire_fragment+), returns
63
+ # a key array suitable for use in reading, writing, or expiring a
64
+ # cached fragment. All keys begin with <tt>:views</tt>,
65
+ # followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set,
66
+ # followed by any controller-wide key prefix values, ending
67
+ # 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
79
+ # +key+ (see +expire_fragment+ for 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+
92
+ # (see +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
104
+ # +key+ exists (see +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
+ # <tt>pages/45/notes</tt>.
120
+ # * Hash - Treated as an implicit call to +url_for+, like
121
+ # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
122
+ # * Regexp - Will remove any fragment that matches, so
123
+ # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
124
+ # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
125
+ # the actual filename matched looks like
126
+ # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
127
+ # only supported on caches that can iterate over all keys (unlike
128
+ # memcached).
129
+ #
130
+ # +options+ is passed through to the cache store's +delete+
131
+ # method (or <tt>delete_matched</tt>, for Regexp keys).
132
+ def expire_fragment(key, options = nil)
133
+ return unless cache_configured?
134
+ key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
135
+
136
+ instrument_fragment_cache :expire_fragment, key do
137
+ if key.is_a?(Regexp)
138
+ cache_store.delete_matched(key, options)
139
+ else
140
+ cache_store.delete(key, options)
141
+ end
142
+ end
143
+ end
144
+
145
+ def instrument_fragment_cache(name, key) # :nodoc:
146
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ # = Abstract Controller Callbacks
5
+ #
6
+ # Abstract Controller provides hooks during the life cycle of a controller action.
7
+ # Callbacks allow you to trigger logic during this cycle. Available callbacks are:
8
+ #
9
+ # * <tt>after_action</tt>
10
+ # * <tt>append_after_action</tt>
11
+ # * <tt>append_around_action</tt>
12
+ # * <tt>append_before_action</tt>
13
+ # * <tt>around_action</tt>
14
+ # * <tt>before_action</tt>
15
+ # * <tt>prepend_after_action</tt>
16
+ # * <tt>prepend_around_action</tt>
17
+ # * <tt>prepend_before_action</tt>
18
+ # * <tt>skip_after_action</tt>
19
+ # * <tt>skip_around_action</tt>
20
+ # * <tt>skip_before_action</tt>
21
+ #
22
+ # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
23
+ #
24
+ module Callbacks
25
+ extend ActiveSupport::Concern
26
+
27
+ # Uses ActiveSupport::Callbacks as the base functionality. For
28
+ # more details on the whole callback system, read the documentation
29
+ # for ActiveSupport::Callbacks.
30
+ include ActiveSupport::Callbacks
31
+
32
+ included do
33
+ define_callbacks :process_action,
34
+ terminator: ->(controller, result_lambda) { result_lambda.call; controller.performed? },
35
+ skip_after_callbacks_if_terminated: true
36
+ end
37
+
38
+ # Override <tt>AbstractController::Base#process_action</tt> to run the
39
+ # <tt>process_action</tt> callbacks around the normal behavior.
40
+ def process_action(*args)
41
+ run_callbacks(:process_action) do
42
+ super
43
+ end
44
+ end
45
+
46
+ module ClassMethods
47
+ # If +:only+ or +:except+ are used, convert the options into the
48
+ # +:if+ and +:unless+ options of ActiveSupport::Callbacks.
49
+ #
50
+ # The basic idea is that <tt>:only => :index</tt> gets converted to
51
+ # <tt>:if => proc {|c| c.action_name == "index" }</tt>.
52
+ #
53
+ # Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
54
+ # are used together.
55
+ #
56
+ # only: :index, if: -> { true } # the :if option will be ignored.
57
+ #
58
+ # Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
59
+ # are used together.
60
+ #
61
+ # except: :index, if: -> { true } # the :except option will be ignored.
62
+ #
63
+ # ==== Options
64
+ # * <tt>only</tt> - The callback should be run only for this action.
65
+ # * <tt>except</tt> - The callback should be run for all actions except this action.
66
+ def _normalize_callback_options(options)
67
+ _normalize_callback_option(options, :only, :if)
68
+ _normalize_callback_option(options, :except, :unless)
69
+ end
70
+
71
+ def _normalize_callback_option(options, from, to) # :nodoc:
72
+ if from = options[from]
73
+ _from = Array(from).map(&:to_s).to_set
74
+ from = proc { |c| _from.include? c.action_name }
75
+ options[to] = Array(options[to]).unshift(from)
76
+ end
77
+ end
78
+
79
+ # Take callback names and an optional callback proc, normalize them,
80
+ # then call the block with each callback. This allows us to abstract
81
+ # the normalization across several methods that use it.
82
+ #
83
+ # ==== Parameters
84
+ # * <tt>callbacks</tt> - An array of callbacks, with an optional
85
+ # options hash as the last parameter.
86
+ # * <tt>block</tt> - A proc that should be added to the callbacks.
87
+ #
88
+ # ==== Block Parameters
89
+ # * <tt>name</tt> - The callback to be added.
90
+ # * <tt>options</tt> - A hash of options to be used when adding the callback.
91
+ def _insert_callbacks(callbacks, block = nil)
92
+ options = callbacks.extract_options!
93
+ _normalize_callback_options(options)
94
+ callbacks.push(block) if block
95
+ callbacks.each do |callback|
96
+ yield callback, options
97
+ end
98
+ end
99
+
100
+ ##
101
+ # :method: before_action
102
+ #
103
+ # :call-seq: before_action(names, block)
104
+ #
105
+ # Append a callback before actions. See _insert_callbacks for parameter details.
106
+ #
107
+ # If the callback renders or redirects, the action will not run. If there
108
+ # are additional callbacks scheduled to run after that callback, they are
109
+ # also cancelled.
110
+
111
+ ##
112
+ # :method: prepend_before_action
113
+ #
114
+ # :call-seq: prepend_before_action(names, block)
115
+ #
116
+ # Prepend a callback before actions. See _insert_callbacks for parameter details.
117
+ #
118
+ # If the callback renders or redirects, the action will not run. If there
119
+ # are additional callbacks scheduled to run after that callback, they are
120
+ # also cancelled.
121
+
122
+ ##
123
+ # :method: skip_before_action
124
+ #
125
+ # :call-seq: skip_before_action(names)
126
+ #
127
+ # Skip a callback before actions. See _insert_callbacks for parameter details.
128
+
129
+ ##
130
+ # :method: append_before_action
131
+ #
132
+ # :call-seq: append_before_action(names, block)
133
+ #
134
+ # Append a callback before actions. See _insert_callbacks for parameter details.
135
+ #
136
+ # If the callback renders or redirects, the action will not run. If there
137
+ # are additional callbacks scheduled to run after that callback, they are
138
+ # also cancelled.
139
+
140
+ ##
141
+ # :method: after_action
142
+ #
143
+ # :call-seq: after_action(names, block)
144
+ #
145
+ # Append a callback after actions. See _insert_callbacks for parameter details.
146
+
147
+ ##
148
+ # :method: prepend_after_action
149
+ #
150
+ # :call-seq: prepend_after_action(names, block)
151
+ #
152
+ # Prepend a callback after actions. See _insert_callbacks for parameter details.
153
+
154
+ ##
155
+ # :method: skip_after_action
156
+ #
157
+ # :call-seq: skip_after_action(names)
158
+ #
159
+ # Skip a callback after actions. See _insert_callbacks for parameter details.
160
+
161
+ ##
162
+ # :method: append_after_action
163
+ #
164
+ # :call-seq: append_after_action(names, block)
165
+ #
166
+ # Append a callback after actions. See _insert_callbacks for parameter details.
167
+
168
+ ##
169
+ # :method: around_action
170
+ #
171
+ # :call-seq: around_action(names, block)
172
+ #
173
+ # Append a callback around actions. See _insert_callbacks for parameter details.
174
+
175
+ ##
176
+ # :method: prepend_around_action
177
+ #
178
+ # :call-seq: prepend_around_action(names, block)
179
+ #
180
+ # Prepend a callback around actions. See _insert_callbacks for parameter details.
181
+
182
+ ##
183
+ # :method: skip_around_action
184
+ #
185
+ # :call-seq: skip_around_action(names)
186
+ #
187
+ # Skip a callback around actions. See _insert_callbacks for parameter details.
188
+
189
+ ##
190
+ # :method: append_around_action
191
+ #
192
+ # :call-seq: append_around_action(names, block)
193
+ #
194
+ # Append a callback around actions. See _insert_callbacks for parameter details.
195
+
196
+ # set up before_action, prepend_before_action, skip_before_action, etc.
197
+ # for each of before, after, and around.
198
+ [:before, :after, :around].each do |callback|
199
+ define_method "#{callback}_action" do |*names, &blk|
200
+ _insert_callbacks(names, blk) do |name, options|
201
+ set_callback(:process_action, callback, name, options)
202
+ end
203
+ end
204
+
205
+ define_method "prepend_#{callback}_action" do |*names, &blk|
206
+ _insert_callbacks(names, blk) do |name, options|
207
+ set_callback(:process_action, callback, name, options.merge(prepend: true))
208
+ end
209
+ end
210
+
211
+ # Skip a before, after or around callback. See _insert_callbacks
212
+ # for details on the allowed parameters.
213
+ define_method "skip_#{callback}_action" do |*names|
214
+ _insert_callbacks(names) do |name, options|
215
+ skip_callback(:process_action, callback, name, options)
216
+ end
217
+ end
218
+
219
+ # *_action is the same as append_*_action
220
+ alias_method :"append_#{callback}_action", :"#{callback}_action"
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/http/mime_type"
4
+
5
+ module AbstractController
6
+ module Collector
7
+ def self.generate_method_for_mime(mime)
8
+ sym = mime.is_a?(Symbol) ? mime : mime.to_sym
9
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
10
+ def #{sym}(*args, &block)
11
+ custom(Mime[:#{sym}], *args, &block)
12
+ end
13
+ RUBY
14
+ end
15
+
16
+ Mime::SET.each do |mime|
17
+ generate_method_for_mime(mime)
18
+ end
19
+
20
+ Mime::Type.register_callback do |mime|
21
+ generate_method_for_mime(mime) unless instance_methods.include?(mime.to_sym)
22
+ end
23
+
24
+ private
25
+
26
+ def method_missing(symbol, &block)
27
+ unless mime_constant = Mime[symbol]
28
+ raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
29
+ "https://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
30
+ "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
31
+ "be sure to nest your variant response within a format response: " \
32
+ "format.html { |html| html.tablet { ... } }"
33
+ end
34
+
35
+ if Mime::SET.include?(mime_constant)
36
+ AbstractController::Collector.generate_method_for_mime(mime_constant)
37
+ send(symbol, &block)
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end