activesupport 6.0.0

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 (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/duplicable"
4
+ require "active_support/core_ext/string/inflections"
5
+ require "active_support/per_thread_registry"
6
+
7
+ module ActiveSupport
8
+ module Cache
9
+ module Strategy
10
+ # Caches that implement LocalCache will be backed by an in-memory cache for the
11
+ # duration of a block. Repeated calls to the cache for the same key will hit the
12
+ # in-memory cache for faster access.
13
+ module LocalCache
14
+ autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
15
+
16
+ # Class for storing and registering the local caches.
17
+ class LocalCacheRegistry # :nodoc:
18
+ extend ActiveSupport::PerThreadRegistry
19
+
20
+ def initialize
21
+ @registry = {}
22
+ end
23
+
24
+ def cache_for(local_cache_key)
25
+ @registry[local_cache_key]
26
+ end
27
+
28
+ def set_cache_for(local_cache_key, value)
29
+ @registry[local_cache_key] = value
30
+ end
31
+
32
+ def self.set_cache_for(l, v); instance.set_cache_for l, v; end
33
+ def self.cache_for(l); instance.cache_for l; end
34
+ end
35
+
36
+ # Simple memory backed cache. This cache is not thread safe and is intended only
37
+ # for serving as a temporary memory cache for a single thread.
38
+ class LocalStore < Store
39
+ def initialize
40
+ super
41
+ @data = {}
42
+ end
43
+
44
+ # Don't allow synchronizing since it isn't thread safe.
45
+ def synchronize # :nodoc:
46
+ yield
47
+ end
48
+
49
+ def clear(options = nil)
50
+ @data.clear
51
+ end
52
+
53
+ def read_entry(key, options)
54
+ @data[key]
55
+ end
56
+
57
+ def read_multi_entries(keys, options)
58
+ values = {}
59
+
60
+ keys.each do |name|
61
+ entry = read_entry(name, options)
62
+ values[name] = entry.value if entry
63
+ end
64
+
65
+ values
66
+ end
67
+
68
+ def write_entry(key, value, options)
69
+ @data[key] = value
70
+ true
71
+ end
72
+
73
+ def delete_entry(key, options)
74
+ !!@data.delete(key)
75
+ end
76
+
77
+ def fetch_entry(key, options = nil) # :nodoc:
78
+ @data.fetch(key) { @data[key] = yield }
79
+ end
80
+ end
81
+
82
+ # Use a local cache for the duration of block.
83
+ def with_local_cache
84
+ use_temporary_local_cache(LocalStore.new) { yield }
85
+ end
86
+
87
+ # Middleware class can be inserted as a Rack handler to be local cache for the
88
+ # duration of request.
89
+ def middleware
90
+ @middleware ||= Middleware.new(
91
+ "ActiveSupport::Cache::Strategy::LocalCache",
92
+ local_cache_key)
93
+ end
94
+
95
+ def clear(options = nil) # :nodoc:
96
+ return super unless cache = local_cache
97
+ cache.clear(options)
98
+ super
99
+ end
100
+
101
+ def cleanup(options = nil) # :nodoc:
102
+ return super unless cache = local_cache
103
+ cache.clear
104
+ super
105
+ end
106
+
107
+ def increment(name, amount = 1, options = nil) # :nodoc:
108
+ return super unless local_cache
109
+ value = bypass_local_cache { super }
110
+ write_cache_value(name, value, options)
111
+ value
112
+ end
113
+
114
+ def decrement(name, amount = 1, options = nil) # :nodoc:
115
+ return super unless local_cache
116
+ value = bypass_local_cache { super }
117
+ write_cache_value(name, value, options)
118
+ value
119
+ end
120
+
121
+ private
122
+ def read_entry(key, options)
123
+ if cache = local_cache
124
+ cache.fetch_entry(key) { super }
125
+ else
126
+ super
127
+ end
128
+ end
129
+
130
+ def read_multi_entries(keys, options)
131
+ return super unless local_cache
132
+
133
+ local_entries = local_cache.read_multi_entries(keys, options)
134
+ missed_keys = keys - local_entries.keys
135
+
136
+ if missed_keys.any?
137
+ local_entries.merge!(super(missed_keys, options))
138
+ else
139
+ local_entries
140
+ end
141
+ end
142
+
143
+ def write_entry(key, entry, options)
144
+ if options[:unless_exist]
145
+ local_cache.delete_entry(key, options) if local_cache
146
+ else
147
+ local_cache.write_entry(key, entry, options) if local_cache
148
+ end
149
+
150
+ super
151
+ end
152
+
153
+ def delete_entry(key, options)
154
+ local_cache.delete_entry(key, options) if local_cache
155
+ super
156
+ end
157
+
158
+ def write_cache_value(name, value, options)
159
+ name = normalize_key(name, options)
160
+ cache = local_cache
161
+ cache.mute do
162
+ if value
163
+ cache.write(name, value, options)
164
+ else
165
+ cache.delete(name, options)
166
+ end
167
+ end
168
+ end
169
+
170
+ def local_cache_key
171
+ @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
172
+ end
173
+
174
+ def local_cache
175
+ LocalCacheRegistry.cache_for(local_cache_key)
176
+ end
177
+
178
+ def bypass_local_cache
179
+ use_temporary_local_cache(nil) { yield }
180
+ end
181
+
182
+ def use_temporary_local_cache(temporary_cache)
183
+ save_cache = LocalCacheRegistry.cache_for(local_cache_key)
184
+ begin
185
+ LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
186
+ yield
187
+ ensure
188
+ LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/body_proxy"
4
+ require "rack/utils"
5
+
6
+ module ActiveSupport
7
+ module Cache
8
+ module Strategy
9
+ module LocalCache
10
+ #--
11
+ # This class wraps up local storage for middlewares. Only the middleware method should
12
+ # construct them.
13
+ class Middleware # :nodoc:
14
+ attr_reader :name, :local_cache_key
15
+
16
+ def initialize(name, local_cache_key)
17
+ @name = name
18
+ @local_cache_key = local_cache_key
19
+ @app = nil
20
+ end
21
+
22
+ def new(app)
23
+ @app = app
24
+ self
25
+ end
26
+
27
+ def call(env)
28
+ LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
29
+ response = @app.call(env)
30
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
31
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil)
32
+ end
33
+ cleanup_on_body_close = true
34
+ response
35
+ rescue Rack::Utils::InvalidParameterError
36
+ [400, {}, []]
37
+ ensure
38
+ LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless
39
+ cleanup_on_body_close
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,856 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/descendants_tracker"
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "active_support/core_ext/class/attribute"
7
+ require "active_support/core_ext/kernel/reporting"
8
+ require "active_support/core_ext/kernel/singleton_class"
9
+ require "active_support/core_ext/string/filters"
10
+ require "active_support/deprecation"
11
+ require "thread"
12
+
13
+ module ActiveSupport
14
+ # Callbacks are code hooks that are run at key points in an object's life cycle.
15
+ # The typical use case is to have a base class define a set of callbacks
16
+ # relevant to the other functionality it supplies, so that subclasses can
17
+ # install callbacks that enhance or modify the base functionality without
18
+ # needing to override or redefine methods of the base class.
19
+ #
20
+ # Mixing in this module allows you to define the events in the object's
21
+ # life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
22
+ # set the instance methods, procs, or callback objects to be called (via
23
+ # +ClassMethods.set_callback+), and run the installed callbacks at the
24
+ # appropriate times (via +run_callbacks+).
25
+ #
26
+ # By default callbacks are halted by throwing +:abort+.
27
+ # See +ClassMethods.define_callbacks+ for details.
28
+ #
29
+ # Three kinds of callbacks are supported: before callbacks, run before a
30
+ # certain event; after callbacks, run after the event; and around callbacks,
31
+ # blocks that surround the event, triggering it when they yield. Callback code
32
+ # can be contained in instance methods, procs or lambdas, or callback objects
33
+ # that respond to certain predetermined methods. See +ClassMethods.set_callback+
34
+ # for details.
35
+ #
36
+ # class Record
37
+ # include ActiveSupport::Callbacks
38
+ # define_callbacks :save
39
+ #
40
+ # def save
41
+ # run_callbacks :save do
42
+ # puts "- save"
43
+ # end
44
+ # end
45
+ # end
46
+ #
47
+ # class PersonRecord < Record
48
+ # set_callback :save, :before, :saving_message
49
+ # def saving_message
50
+ # puts "saving..."
51
+ # end
52
+ #
53
+ # set_callback :save, :after do |object|
54
+ # puts "saved"
55
+ # end
56
+ # end
57
+ #
58
+ # person = PersonRecord.new
59
+ # person.save
60
+ #
61
+ # Output:
62
+ # saving...
63
+ # - save
64
+ # saved
65
+ module Callbacks
66
+ extend Concern
67
+
68
+ included do
69
+ extend ActiveSupport::DescendantsTracker
70
+ class_attribute :__callbacks, instance_writer: false, default: {}
71
+ end
72
+
73
+ CALLBACK_FILTER_TYPES = [:before, :after, :around]
74
+
75
+ # Runs the callbacks for the given event.
76
+ #
77
+ # Calls the before and around callbacks in the order they were set, yields
78
+ # the block (if given one), and then runs the after callbacks in reverse
79
+ # order.
80
+ #
81
+ # If the callback chain was halted, returns +false+. Otherwise returns the
82
+ # result of the block, +nil+ if no callbacks have been set, or +true+
83
+ # if callbacks have been set but no block is given.
84
+ #
85
+ # run_callbacks :save do
86
+ # save
87
+ # end
88
+ #
89
+ #--
90
+ #
91
+ # As this method is used in many places, and often wraps large portions of
92
+ # user code, it has an additional design goal of minimizing its impact on
93
+ # the visible call stack. An exception from inside a :before or :after
94
+ # callback can be as noisy as it likes -- but when control has passed
95
+ # smoothly through and into the supplied block, we want as little evidence
96
+ # as possible that we were here.
97
+ def run_callbacks(kind)
98
+ callbacks = __callbacks[kind.to_sym]
99
+
100
+ if callbacks.empty?
101
+ yield if block_given?
102
+ else
103
+ env = Filters::Environment.new(self, false, nil)
104
+ next_sequence = callbacks.compile
105
+
106
+ invoke_sequence = Proc.new do
107
+ skipped = nil
108
+ while true
109
+ current = next_sequence
110
+ current.invoke_before(env)
111
+ if current.final?
112
+ env.value = !env.halted && (!block_given? || yield)
113
+ elsif current.skip?(env)
114
+ (skipped ||= []) << current
115
+ next_sequence = next_sequence.nested
116
+ next
117
+ else
118
+ next_sequence = next_sequence.nested
119
+ begin
120
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
121
+ target.send(method, *arguments, &block)
122
+ ensure
123
+ next_sequence = current
124
+ end
125
+ end
126
+ current.invoke_after(env)
127
+ skipped.pop.invoke_after(env) while skipped && skipped.first
128
+ break env.value
129
+ end
130
+ end
131
+
132
+ # Common case: no 'around' callbacks defined
133
+ if next_sequence.final?
134
+ next_sequence.invoke_before(env)
135
+ env.value = !env.halted && (!block_given? || yield)
136
+ next_sequence.invoke_after(env)
137
+ env.value
138
+ else
139
+ invoke_sequence.call
140
+ end
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ # A hook invoked every time a before callback is halted.
147
+ # This can be overridden in ActiveSupport::Callbacks implementors in order
148
+ # to provide better debugging/logging.
149
+ def halted_callback_hook(filter)
150
+ end
151
+
152
+ module Conditionals # :nodoc:
153
+ class Value
154
+ def initialize(&block)
155
+ @block = block
156
+ end
157
+ def call(target, value); @block.call(value); end
158
+ end
159
+ end
160
+
161
+ module Filters
162
+ Environment = Struct.new(:target, :halted, :value)
163
+
164
+ class Before
165
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
166
+ halted_lambda = chain_config[:terminator]
167
+
168
+ if user_conditions.any?
169
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
170
+ else
171
+ halting(callback_sequence, user_callback, halted_lambda, filter)
172
+ end
173
+ end
174
+
175
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
176
+ callback_sequence.before do |env|
177
+ target = env.target
178
+ value = env.value
179
+ halted = env.halted
180
+
181
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
182
+ result_lambda = -> { user_callback.call target, value }
183
+ env.halted = halted_lambda.call(target, result_lambda)
184
+ if env.halted
185
+ target.send :halted_callback_hook, filter
186
+ end
187
+ end
188
+
189
+ env
190
+ end
191
+ end
192
+ private_class_method :halting_and_conditional
193
+
194
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter)
195
+ callback_sequence.before do |env|
196
+ target = env.target
197
+ value = env.value
198
+ halted = env.halted
199
+
200
+ unless halted
201
+ result_lambda = -> { user_callback.call target, value }
202
+ env.halted = halted_lambda.call(target, result_lambda)
203
+
204
+ if env.halted
205
+ target.send :halted_callback_hook, filter
206
+ end
207
+ end
208
+
209
+ env
210
+ end
211
+ end
212
+ private_class_method :halting
213
+ end
214
+
215
+ class After
216
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config)
217
+ if chain_config[:skip_after_callbacks_if_terminated]
218
+ if user_conditions.any?
219
+ halting_and_conditional(callback_sequence, user_callback, user_conditions)
220
+ else
221
+ halting(callback_sequence, user_callback)
222
+ end
223
+ else
224
+ if user_conditions.any?
225
+ conditional callback_sequence, user_callback, user_conditions
226
+ else
227
+ simple callback_sequence, user_callback
228
+ end
229
+ end
230
+ end
231
+
232
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
233
+ callback_sequence.after do |env|
234
+ target = env.target
235
+ value = env.value
236
+ halted = env.halted
237
+
238
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
239
+ user_callback.call target, value
240
+ end
241
+
242
+ env
243
+ end
244
+ end
245
+ private_class_method :halting_and_conditional
246
+
247
+ def self.halting(callback_sequence, user_callback)
248
+ callback_sequence.after do |env|
249
+ unless env.halted
250
+ user_callback.call env.target, env.value
251
+ end
252
+
253
+ env
254
+ end
255
+ end
256
+ private_class_method :halting
257
+
258
+ def self.conditional(callback_sequence, user_callback, user_conditions)
259
+ callback_sequence.after do |env|
260
+ target = env.target
261
+ value = env.value
262
+
263
+ if user_conditions.all? { |c| c.call(target, value) }
264
+ user_callback.call target, value
265
+ end
266
+
267
+ env
268
+ end
269
+ end
270
+ private_class_method :conditional
271
+
272
+ def self.simple(callback_sequence, user_callback)
273
+ callback_sequence.after do |env|
274
+ user_callback.call env.target, env.value
275
+
276
+ env
277
+ end
278
+ end
279
+ private_class_method :simple
280
+ end
281
+ end
282
+
283
+ class Callback #:nodoc:#
284
+ def self.build(chain, filter, kind, options)
285
+ if filter.is_a?(String)
286
+ raise ArgumentError, <<-MSG.squish
287
+ Passing string to define a callback is not supported. See the `.set_callback`
288
+ documentation to see supported values.
289
+ MSG
290
+ end
291
+
292
+ new chain.name, filter, kind, options, chain.config
293
+ end
294
+
295
+ attr_accessor :kind, :name
296
+ attr_reader :chain_config
297
+
298
+ def initialize(name, filter, kind, options, chain_config)
299
+ @chain_config = chain_config
300
+ @name = name
301
+ @kind = kind
302
+ @filter = filter
303
+ @key = compute_identifier filter
304
+ @if = check_conditionals(Array(options[:if]))
305
+ @unless = check_conditionals(Array(options[:unless]))
306
+ end
307
+
308
+ def filter; @key; end
309
+ def raw_filter; @filter; end
310
+
311
+ def merge_conditional_options(chain, if_option:, unless_option:)
312
+ options = {
313
+ if: @if.dup,
314
+ unless: @unless.dup
315
+ }
316
+
317
+ options[:if].concat Array(unless_option)
318
+ options[:unless].concat Array(if_option)
319
+
320
+ self.class.build chain, @filter, @kind, options
321
+ end
322
+
323
+ def matches?(_kind, _filter)
324
+ @kind == _kind && filter == _filter
325
+ end
326
+
327
+ def duplicates?(other)
328
+ case @filter
329
+ when Symbol
330
+ matches?(other.kind, other.filter)
331
+ else
332
+ false
333
+ end
334
+ end
335
+
336
+ # Wraps code with filter
337
+ def apply(callback_sequence)
338
+ user_conditions = conditions_lambdas
339
+ user_callback = CallTemplate.build(@filter, self)
340
+
341
+ case kind
342
+ when :before
343
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
344
+ when :after
345
+ Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
346
+ when :around
347
+ callback_sequence.around(user_callback, user_conditions)
348
+ end
349
+ end
350
+
351
+ def current_scopes
352
+ Array(chain_config[:scope]).map { |s| public_send(s) }
353
+ end
354
+
355
+ private
356
+ def check_conditionals(conditionals)
357
+ if conditionals.any? { |c| c.is_a?(String) }
358
+ raise ArgumentError, <<-MSG.squish
359
+ Passing string to be evaluated in :if and :unless conditional
360
+ options is not supported. Pass a symbol for an instance method,
361
+ or a lambda, proc or block, instead.
362
+ MSG
363
+ end
364
+
365
+ conditionals
366
+ end
367
+
368
+ def compute_identifier(filter)
369
+ case filter
370
+ when ::Proc
371
+ filter.object_id
372
+ else
373
+ filter
374
+ end
375
+ end
376
+
377
+ def conditions_lambdas
378
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
379
+ @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
380
+ end
381
+ end
382
+
383
+ # A future invocation of user-supplied code (either as a callback,
384
+ # or a condition filter).
385
+ class CallTemplate # :nodoc:
386
+ def initialize(target, method, arguments, block)
387
+ @override_target = target
388
+ @method_name = method
389
+ @arguments = arguments
390
+ @override_block = block
391
+ end
392
+
393
+ # Return the parts needed to make this call, with the given
394
+ # input values.
395
+ #
396
+ # Returns an array of the form:
397
+ #
398
+ # [target, block, method, *arguments]
399
+ #
400
+ # This array can be used as such:
401
+ #
402
+ # target.send(method, *arguments, &block)
403
+ #
404
+ # The actual invocation is left up to the caller to minimize
405
+ # call stack pollution.
406
+ def expand(target, value, block)
407
+ result = @arguments.map { |arg|
408
+ case arg
409
+ when :value; value
410
+ when :target; target
411
+ when :block; block || raise(ArgumentError)
412
+ end
413
+ }
414
+
415
+ result.unshift @method_name
416
+ result.unshift @override_block || block
417
+ result.unshift @override_target || target
418
+
419
+ # target, block, method, *arguments = result
420
+ # target.send(method, *arguments, &block)
421
+ result
422
+ end
423
+
424
+ # Return a lambda that will make this call when given the input
425
+ # values.
426
+ def make_lambda
427
+ lambda do |target, value, &block|
428
+ target, block, method, *arguments = expand(target, value, block)
429
+ target.send(method, *arguments, &block)
430
+ end
431
+ end
432
+
433
+ # Return a lambda that will make this call when given the input
434
+ # values, but then return the boolean inverse of that result.
435
+ def inverted_lambda
436
+ lambda do |target, value, &block|
437
+ target, block, method, *arguments = expand(target, value, block)
438
+ ! target.send(method, *arguments, &block)
439
+ end
440
+ end
441
+
442
+ # Filters support:
443
+ #
444
+ # Symbols:: A method to call.
445
+ # Procs:: A proc to call with the object.
446
+ # Objects:: An object with a <tt>before_foo</tt> method on it to call.
447
+ #
448
+ # All of these objects are converted into a CallTemplate and handled
449
+ # the same after this point.
450
+ def self.build(filter, callback)
451
+ case filter
452
+ when Symbol
453
+ new(nil, filter, [], nil)
454
+ when Conditionals::Value
455
+ new(filter, :call, [:target, :value], nil)
456
+ when ::Proc
457
+ if filter.arity > 1
458
+ new(nil, :instance_exec, [:target, :block], filter)
459
+ elsif filter.arity > 0
460
+ new(nil, :instance_exec, [:target], filter)
461
+ else
462
+ new(nil, :instance_exec, [], filter)
463
+ end
464
+ else
465
+ method_to_call = callback.current_scopes.join("_")
466
+
467
+ new(filter, method_to_call, [:target], nil)
468
+ end
469
+ end
470
+ end
471
+
472
+ # Execute before and after filters in a sequence instead of
473
+ # chaining them with nested lambda calls, see:
474
+ # https://github.com/rails/rails/issues/18011
475
+ class CallbackSequence # :nodoc:
476
+ def initialize(nested = nil, call_template = nil, user_conditions = nil)
477
+ @nested = nested
478
+ @call_template = call_template
479
+ @user_conditions = user_conditions
480
+
481
+ @before = []
482
+ @after = []
483
+ end
484
+
485
+ def before(&before)
486
+ @before.unshift(before)
487
+ self
488
+ end
489
+
490
+ def after(&after)
491
+ @after.push(after)
492
+ self
493
+ end
494
+
495
+ def around(call_template, user_conditions)
496
+ CallbackSequence.new(self, call_template, user_conditions)
497
+ end
498
+
499
+ def skip?(arg)
500
+ arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
501
+ end
502
+
503
+ attr_reader :nested
504
+
505
+ def final?
506
+ !@call_template
507
+ end
508
+
509
+ def expand_call_template(arg, block)
510
+ @call_template.expand(arg.target, arg.value, block)
511
+ end
512
+
513
+ def invoke_before(arg)
514
+ @before.each { |b| b.call(arg) }
515
+ end
516
+
517
+ def invoke_after(arg)
518
+ @after.each { |a| a.call(arg) }
519
+ end
520
+ end
521
+
522
+ class CallbackChain #:nodoc:#
523
+ include Enumerable
524
+
525
+ attr_reader :name, :config
526
+
527
+ def initialize(name, config)
528
+ @name = name
529
+ @config = {
530
+ scope: [:kind],
531
+ terminator: default_terminator
532
+ }.merge!(config)
533
+ @chain = []
534
+ @callbacks = nil
535
+ @mutex = Mutex.new
536
+ end
537
+
538
+ def each(&block); @chain.each(&block); end
539
+ def index(o); @chain.index(o); end
540
+ def empty?; @chain.empty?; end
541
+
542
+ def insert(index, o)
543
+ @callbacks = nil
544
+ @chain.insert(index, o)
545
+ end
546
+
547
+ def delete(o)
548
+ @callbacks = nil
549
+ @chain.delete(o)
550
+ end
551
+
552
+ def clear
553
+ @callbacks = nil
554
+ @chain.clear
555
+ self
556
+ end
557
+
558
+ def initialize_copy(other)
559
+ @callbacks = nil
560
+ @chain = other.chain.dup
561
+ @mutex = Mutex.new
562
+ end
563
+
564
+ def compile
565
+ @callbacks || @mutex.synchronize do
566
+ final_sequence = CallbackSequence.new
567
+ @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
568
+ callback.apply callback_sequence
569
+ end
570
+ end
571
+ end
572
+
573
+ def append(*callbacks)
574
+ callbacks.each { |c| append_one(c) }
575
+ end
576
+
577
+ def prepend(*callbacks)
578
+ callbacks.each { |c| prepend_one(c) }
579
+ end
580
+
581
+ protected
582
+ attr_reader :chain
583
+
584
+ private
585
+
586
+ def append_one(callback)
587
+ @callbacks = nil
588
+ remove_duplicates(callback)
589
+ @chain.push(callback)
590
+ end
591
+
592
+ def prepend_one(callback)
593
+ @callbacks = nil
594
+ remove_duplicates(callback)
595
+ @chain.unshift(callback)
596
+ end
597
+
598
+ def remove_duplicates(callback)
599
+ @callbacks = nil
600
+ @chain.delete_if { |c| callback.duplicates?(c) }
601
+ end
602
+
603
+ def default_terminator
604
+ Proc.new do |target, result_lambda|
605
+ terminate = true
606
+ catch(:abort) do
607
+ result_lambda.call
608
+ terminate = false
609
+ end
610
+ terminate
611
+ end
612
+ end
613
+ end
614
+
615
+ module ClassMethods
616
+ def normalize_callback_params(filters, block) # :nodoc:
617
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
618
+ options = filters.extract_options!
619
+ filters.unshift(block) if block
620
+ [type, filters, options.dup]
621
+ end
622
+
623
+ # This is used internally to append, prepend and skip callbacks to the
624
+ # CallbackChain.
625
+ def __update_callbacks(name) #:nodoc:
626
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
627
+ chain = target.get_callbacks name
628
+ yield target, chain.dup
629
+ end
630
+ end
631
+
632
+ # Install a callback for the given event.
633
+ #
634
+ # set_callback :save, :before, :before_method
635
+ # set_callback :save, :after, :after_method, if: :condition
636
+ # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
637
+ #
638
+ # The second argument indicates whether the callback is to be run +:before+,
639
+ # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
640
+ # means the first example above can also be written as:
641
+ #
642
+ # set_callback :save, :before_method
643
+ #
644
+ # The callback can be specified as a symbol naming an instance method; as a
645
+ # proc, lambda, or block; or as an object that responds to a certain method
646
+ # determined by the <tt>:scope</tt> argument to +define_callbacks+.
647
+ #
648
+ # If a proc, lambda, or block is given, its body is evaluated in the context
649
+ # of the current object. It can also optionally accept the current object as
650
+ # an argument.
651
+ #
652
+ # Before and around callbacks are called in the order that they are set;
653
+ # after callbacks are called in the reverse order.
654
+ #
655
+ # Around callbacks can access the return value from the event, if it
656
+ # wasn't halted, from the +yield+ call.
657
+ #
658
+ # ===== Options
659
+ #
660
+ # * <tt>:if</tt> - A symbol or an array of symbols, each naming an instance
661
+ # method or a proc; the callback will be called only when they all return
662
+ # a true value.
663
+ #
664
+ # If a proc is given, its body is evaluated in the context of the
665
+ # current object. It can also optionally accept the current object as
666
+ # an argument.
667
+ # * <tt>:unless</tt> - A symbol or an array of symbols, each naming an
668
+ # instance method or a proc; the callback will be called only when they
669
+ # all return a false value.
670
+ #
671
+ # If a proc is given, its body is evaluated in the context of the
672
+ # current object. It can also optionally accept the current object as
673
+ # an argument.
674
+ # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
675
+ # existing chain rather than appended.
676
+ def set_callback(name, *filter_list, &block)
677
+ type, filters, options = normalize_callback_params(filter_list, block)
678
+
679
+ self_chain = get_callbacks name
680
+ mapped = filters.map do |filter|
681
+ Callback.build(self_chain, filter, type, options)
682
+ end
683
+
684
+ __update_callbacks(name) do |target, chain|
685
+ options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
686
+ target.set_callbacks name, chain
687
+ end
688
+ end
689
+
690
+ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
691
+ # <tt>:unless</tt> options may be passed in order to control when the
692
+ # callback is skipped.
693
+ #
694
+ # class Writer < Person
695
+ # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
696
+ # end
697
+ #
698
+ # An <tt>ArgumentError</tt> will be raised if the callback has not
699
+ # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
700
+ def skip_callback(name, *filter_list, &block)
701
+ type, filters, options = normalize_callback_params(filter_list, block)
702
+
703
+ options[:raise] = true unless options.key?(:raise)
704
+
705
+ __update_callbacks(name) do |target, chain|
706
+ filters.each do |filter|
707
+ callback = chain.find { |c| c.matches?(type, filter) }
708
+
709
+ if !callback && options[:raise]
710
+ raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
711
+ end
712
+
713
+ if callback && (options.key?(:if) || options.key?(:unless))
714
+ new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
715
+ chain.insert(chain.index(callback), new_callback)
716
+ end
717
+
718
+ chain.delete(callback)
719
+ end
720
+ target.set_callbacks name, chain
721
+ end
722
+ end
723
+
724
+ # Remove all set callbacks for the given event.
725
+ def reset_callbacks(name)
726
+ callbacks = get_callbacks name
727
+
728
+ ActiveSupport::DescendantsTracker.descendants(self).each do |target|
729
+ chain = target.get_callbacks(name).dup
730
+ callbacks.each { |c| chain.delete(c) }
731
+ target.set_callbacks name, chain
732
+ end
733
+
734
+ set_callbacks(name, callbacks.dup.clear)
735
+ end
736
+
737
+ # Define sets of events in the object life cycle that support callbacks.
738
+ #
739
+ # define_callbacks :validate
740
+ # define_callbacks :initialize, :save, :destroy
741
+ #
742
+ # ===== Options
743
+ #
744
+ # * <tt>:terminator</tt> - Determines when a before filter will halt the
745
+ # callback chain, preventing following before and around callbacks from
746
+ # being called and the event from being triggered.
747
+ # This should be a lambda to be executed.
748
+ # The current object and the result lambda of the callback will be provided
749
+ # to the terminator lambda.
750
+ #
751
+ # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
752
+ #
753
+ # In this example, if any before validate callbacks returns +false+,
754
+ # any successive before and around callback is not executed.
755
+ #
756
+ # The default terminator halts the chain when a callback throws +:abort+.
757
+ #
758
+ # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
759
+ # callbacks should be terminated by the <tt>:terminator</tt> option. By
760
+ # default after callbacks are executed no matter if callback chain was
761
+ # terminated or not. This option has no effect if <tt>:terminator</tt>
762
+ # option is set to +nil+.
763
+ #
764
+ # * <tt>:scope</tt> - Indicates which methods should be executed when an
765
+ # object is used as a callback.
766
+ #
767
+ # class Audit
768
+ # def before(caller)
769
+ # puts 'Audit: before is called'
770
+ # end
771
+ #
772
+ # def before_save(caller)
773
+ # puts 'Audit: before_save is called'
774
+ # end
775
+ # end
776
+ #
777
+ # class Account
778
+ # include ActiveSupport::Callbacks
779
+ #
780
+ # define_callbacks :save
781
+ # set_callback :save, :before, Audit.new
782
+ #
783
+ # def save
784
+ # run_callbacks :save do
785
+ # puts 'save in main'
786
+ # end
787
+ # end
788
+ # end
789
+ #
790
+ # In the above case whenever you save an account the method
791
+ # <tt>Audit#before</tt> will be called. On the other hand
792
+ #
793
+ # define_callbacks :save, scope: [:kind, :name]
794
+ #
795
+ # would trigger <tt>Audit#before_save</tt> instead. That's constructed
796
+ # by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
797
+ # case "kind" is "before" and "name" is "save". In this context +:kind+
798
+ # and +:name+ have special meanings: +:kind+ refers to the kind of
799
+ # callback (before/after/around) and +:name+ refers to the method on
800
+ # which callbacks are being defined.
801
+ #
802
+ # A declaration like
803
+ #
804
+ # define_callbacks :save, scope: [:name]
805
+ #
806
+ # would call <tt>Audit#save</tt>.
807
+ #
808
+ # ===== Notes
809
+ #
810
+ # +names+ passed to +define_callbacks+ must not end with
811
+ # <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
812
+ #
813
+ # Calling +define_callbacks+ multiple times with the same +names+ will
814
+ # overwrite previous callbacks registered with +set_callback+.
815
+ def define_callbacks(*names)
816
+ options = names.extract_options!
817
+
818
+ names.each do |name|
819
+ name = name.to_sym
820
+
821
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
822
+ target.set_callbacks name, CallbackChain.new(name, options)
823
+ end
824
+
825
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
826
+ def _run_#{name}_callbacks(&block)
827
+ run_callbacks #{name.inspect}, &block
828
+ end
829
+
830
+ def self._#{name}_callbacks
831
+ get_callbacks(#{name.inspect})
832
+ end
833
+
834
+ def self._#{name}_callbacks=(value)
835
+ set_callbacks(#{name.inspect}, value)
836
+ end
837
+
838
+ def _#{name}_callbacks
839
+ __callbacks[#{name.inspect}]
840
+ end
841
+ RUBY
842
+ end
843
+ end
844
+
845
+ protected
846
+
847
+ def get_callbacks(name) # :nodoc:
848
+ __callbacks[name.to_sym]
849
+ end
850
+
851
+ def set_callbacks(name, callbacks) # :nodoc:
852
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
853
+ end
854
+ end
855
+ end
856
+ end