activesupport 6.0.6.1 → 7.1.3.2

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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +865 -438
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +30 -10
  8. data/lib/active_support/benchmarkable.rb +4 -3
  9. data/lib/active_support/broadcast_logger.rb +250 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +208 -63
  15. data/lib/active_support/cache/memory_store.rb +120 -38
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +201 -208
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +73 -66
  20. data/lib/active_support/cache.rb +539 -261
  21. data/lib/active_support/callbacks.rb +273 -142
  22. data/lib/active_support/code_generator.rb +65 -0
  23. data/lib/active_support/concern.rb +53 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +19 -6
  28. data/lib/active_support/configuration_file.rb +51 -0
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/benchmark.rb +2 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  36. data/lib/active_support/core_ext/class/subclasses.rb +19 -29
  37. data/lib/active_support/core_ext/date/blank.rb +1 -1
  38. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  39. data/lib/active_support/core_ext/date/conversions.rb +18 -16
  40. data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -4
  41. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  44. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  45. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  46. data/lib/active_support/core_ext/enumerable.rb +146 -72
  47. data/lib/active_support/core_ext/erb/util.rb +196 -0
  48. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  49. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  50. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +4 -4
  52. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  53. data/lib/active_support/core_ext/hash/keys.rb +5 -5
  54. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  55. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  56. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  57. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  58. data/lib/active_support/core_ext/load_error.rb +1 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +31 -29
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +51 -20
  62. data/lib/active_support/core_ext/module/concerning.rb +14 -8
  63. data/lib/active_support/core_ext/module/delegation.rb +75 -42
  64. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  65. data/lib/active_support/core_ext/module/introspection.rb +1 -26
  66. data/lib/active_support/core_ext/name_error.rb +23 -2
  67. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  68. data/lib/active_support/core_ext/numeric/conversions.rb +82 -73
  69. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  70. data/lib/active_support/core_ext/object/blank.rb +2 -2
  71. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  72. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  73. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  74. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  75. data/lib/active_support/core_ext/object/json.rb +52 -28
  76. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  77. data/lib/active_support/core_ext/object/try.rb +20 -20
  78. data/lib/active_support/core_ext/object/with.rb +44 -0
  79. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  80. data/lib/active_support/core_ext/object.rb +1 -0
  81. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  82. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  83. data/lib/active_support/core_ext/pathname.rb +4 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  85. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  86. data/lib/active_support/core_ext/range/each.rb +1 -1
  87. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  88. data/lib/active_support/core_ext/range.rb +1 -2
  89. data/lib/active_support/core_ext/regexp.rb +8 -1
  90. data/lib/active_support/core_ext/securerandom.rb +25 -13
  91. data/lib/active_support/core_ext/string/access.rb +5 -24
  92. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  93. data/lib/active_support/core_ext/string/filters.rb +21 -15
  94. data/lib/active_support/core_ext/string/indent.rb +1 -1
  95. data/lib/active_support/core_ext/string/inflections.rb +51 -10
  96. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  97. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  98. data/lib/active_support/core_ext/string/output_safety.rb +85 -194
  99. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  100. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  101. data/lib/active_support/core_ext/symbol.rb +3 -0
  102. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  103. data/lib/active_support/core_ext/time/calculations.rb +46 -8
  104. data/lib/active_support/core_ext/time/conversions.rb +16 -13
  105. data/lib/active_support/core_ext/time/zones.rb +12 -28
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  108. data/lib/active_support/current_attributes.rb +54 -22
  109. data/lib/active_support/deep_mergeable.rb +53 -0
  110. data/lib/active_support/dependencies/autoload.rb +17 -12
  111. data/lib/active_support/dependencies/interlock.rb +10 -18
  112. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  113. data/lib/active_support/dependencies.rb +58 -769
  114. data/lib/active_support/deprecation/behaviors.rb +77 -38
  115. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  116. data/lib/active_support/deprecation/deprecators.rb +104 -0
  117. data/lib/active_support/deprecation/disallowed.rb +54 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +31 -5
  119. data/lib/active_support/deprecation/method_wrappers.rb +12 -28
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +40 -25
  121. data/lib/active_support/deprecation/reporting.rb +76 -16
  122. data/lib/active_support/deprecation.rb +36 -4
  123. data/lib/active_support/deprecator.rb +7 -0
  124. data/lib/active_support/descendants_tracker.rb +150 -68
  125. data/lib/active_support/digest.rb +5 -3
  126. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  127. data/lib/active_support/duration/iso8601_serializer.rb +24 -12
  128. data/lib/active_support/duration.rb +136 -56
  129. data/lib/active_support/encrypted_configuration.rb +72 -9
  130. data/lib/active_support/encrypted_file.rb +46 -13
  131. data/lib/active_support/environment_inquirer.rb +40 -0
  132. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  133. data/lib/active_support/error_reporter.rb +203 -0
  134. data/lib/active_support/evented_file_update_checker.rb +86 -137
  135. data/lib/active_support/execution_context/test_helper.rb +13 -0
  136. data/lib/active_support/execution_context.rb +53 -0
  137. data/lib/active_support/execution_wrapper.rb +31 -12
  138. data/lib/active_support/executor/test_helper.rb +7 -0
  139. data/lib/active_support/file_update_checker.rb +4 -2
  140. data/lib/active_support/fork_tracker.rb +79 -0
  141. data/lib/active_support/gem_version.rb +5 -5
  142. data/lib/active_support/gzip.rb +2 -0
  143. data/lib/active_support/hash_with_indifferent_access.rb +86 -42
  144. data/lib/active_support/html_safe_translation.rb +53 -0
  145. data/lib/active_support/i18n.rb +2 -1
  146. data/lib/active_support/i18n_railtie.rb +29 -27
  147. data/lib/active_support/inflector/inflections.rb +26 -9
  148. data/lib/active_support/inflector/methods.rb +54 -64
  149. data/lib/active_support/inflector/transliterate.rb +7 -5
  150. data/lib/active_support/isolated_execution_state.rb +76 -0
  151. data/lib/active_support/json/decoding.rb +6 -5
  152. data/lib/active_support/json/encoding.rb +31 -45
  153. data/lib/active_support/key_generator.rb +32 -7
  154. data/lib/active_support/lazy_load_hooks.rb +33 -7
  155. data/lib/active_support/locale/en.yml +10 -4
  156. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  157. data/lib/active_support/log_subscriber.rb +101 -32
  158. data/lib/active_support/logger.rb +9 -60
  159. data/lib/active_support/logger_silence.rb +2 -26
  160. data/lib/active_support/logger_thread_safe_level.rb +24 -25
  161. data/lib/active_support/message_encryptor.rb +205 -58
  162. data/lib/active_support/message_encryptors.rb +141 -0
  163. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  164. data/lib/active_support/message_pack/extensions.rb +292 -0
  165. data/lib/active_support/message_pack/serializer.rb +63 -0
  166. data/lib/active_support/message_pack.rb +50 -0
  167. data/lib/active_support/message_verifier.rb +237 -86
  168. data/lib/active_support/message_verifiers.rb +135 -0
  169. data/lib/active_support/messages/codec.rb +65 -0
  170. data/lib/active_support/messages/metadata.rb +112 -46
  171. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  172. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  173. data/lib/active_support/messages/rotator.rb +35 -32
  174. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  175. data/lib/active_support/multibyte/chars.rb +15 -52
  176. data/lib/active_support/multibyte/unicode.rb +8 -122
  177. data/lib/active_support/multibyte.rb +1 -1
  178. data/lib/active_support/notifications/fanout.rb +310 -105
  179. data/lib/active_support/notifications/instrumenter.rb +113 -48
  180. data/lib/active_support/notifications.rb +56 -29
  181. data/lib/active_support/number_helper/number_converter.rb +15 -8
  182. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  183. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  184. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  185. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -5
  186. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  187. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  188. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  189. data/lib/active_support/number_helper.rb +379 -304
  190. data/lib/active_support/option_merger.rb +11 -18
  191. data/lib/active_support/ordered_hash.rb +4 -4
  192. data/lib/active_support/ordered_options.rb +23 -3
  193. data/lib/active_support/parameter_filter.rb +104 -75
  194. data/lib/active_support/proxy_object.rb +2 -0
  195. data/lib/active_support/rails.rb +1 -4
  196. data/lib/active_support/railtie.rb +90 -6
  197. data/lib/active_support/reloader.rb +12 -4
  198. data/lib/active_support/rescuable.rb +18 -16
  199. data/lib/active_support/ruby_features.rb +7 -0
  200. data/lib/active_support/secure_compare_rotator.rb +58 -0
  201. data/lib/active_support/security_utils.rb +19 -12
  202. data/lib/active_support/string_inquirer.rb +5 -3
  203. data/lib/active_support/subscriber.rb +23 -47
  204. data/lib/active_support/syntax_error_proxy.rb +70 -0
  205. data/lib/active_support/tagged_logging.rb +84 -23
  206. data/lib/active_support/test_case.rb +166 -27
  207. data/lib/active_support/testing/assertions.rb +73 -20
  208. data/lib/active_support/testing/autorun.rb +0 -2
  209. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  210. data/lib/active_support/testing/deprecation.rb +53 -2
  211. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  212. data/lib/active_support/testing/isolation.rb +30 -29
  213. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  214. data/lib/active_support/testing/parallelization/server.rb +82 -0
  215. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  216. data/lib/active_support/testing/parallelization.rb +16 -95
  217. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  218. data/lib/active_support/testing/stream.rb +4 -6
  219. data/lib/active_support/testing/strict_warnings.rb +39 -0
  220. data/lib/active_support/testing/tagged_logging.rb +1 -1
  221. data/lib/active_support/testing/time_helpers.rb +89 -19
  222. data/lib/active_support/time_with_zone.rb +105 -70
  223. data/lib/active_support/values/time_zone.rb +59 -26
  224. data/lib/active_support/version.rb +1 -1
  225. data/lib/active_support/xml_mini/jdom.rb +4 -11
  226. data/lib/active_support/xml_mini/libxml.rb +5 -5
  227. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  228. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  229. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  230. data/lib/active_support/xml_mini/rexml.rb +9 -2
  231. data/lib/active_support/xml_mini.rb +7 -6
  232. data/lib/active_support.rb +40 -1
  233. metadata +127 -40
  234. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  235. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  236. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  237. data/lib/active_support/core_ext/marshal.rb +0 -24
  238. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  239. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  240. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  241. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -23
  242. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  243. data/lib/active_support/core_ext/uri.rb +0 -25
  244. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  245. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -4,33 +4,33 @@ require "active_support/concern"
4
4
  require "active_support/descendants_tracker"
5
5
  require "active_support/core_ext/array/extract_options"
6
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
7
  require "active_support/core_ext/string/filters"
10
- require "active_support/deprecation"
8
+ require "active_support/core_ext/object/blank"
11
9
  require "thread"
12
10
 
13
11
  module ActiveSupport
14
- # Callbacks are code hooks that are run at key points in an object's life cycle.
12
+ # = Active Support \Callbacks
13
+ #
14
+ # \Callbacks are code hooks that are run at key points in an object's life cycle.
15
15
  # The typical use case is to have a base class define a set of callbacks
16
16
  # relevant to the other functionality it supplies, so that subclasses can
17
17
  # install callbacks that enhance or modify the base functionality without
18
18
  # needing to override or redefine methods of the base class.
19
19
  #
20
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+),
21
+ # life cycle that will support callbacks (via ClassMethods#define_callbacks),
22
22
  # set the instance methods, procs, or callback objects to be called (via
23
- # +ClassMethods.set_callback+), and run the installed callbacks at the
23
+ # ClassMethods#set_callback), and run the installed callbacks at the
24
24
  # appropriate times (via +run_callbacks+).
25
25
  #
26
26
  # By default callbacks are halted by throwing +:abort+.
27
- # See +ClassMethods.define_callbacks+ for details.
27
+ # See ClassMethods#define_callbacks for details.
28
28
  #
29
29
  # Three kinds of callbacks are supported: before callbacks, run before a
30
30
  # certain event; after callbacks, run after the event; and around callbacks,
31
31
  # blocks that surround the event, triggering it when they yield. Callback code
32
32
  # can be contained in instance methods, procs or lambdas, or callback objects
33
- # that respond to certain predetermined methods. See +ClassMethods.set_callback+
33
+ # that respond to certain predetermined methods. See ClassMethods#set_callback
34
34
  # for details.
35
35
  #
36
36
  # class Record
@@ -70,7 +70,7 @@ module ActiveSupport
70
70
  class_attribute :__callbacks, instance_writer: false, default: {}
71
71
  end
72
72
 
73
- CALLBACK_FILTER_TYPES = [:before, :after, :around]
73
+ CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
74
74
 
75
75
  # Runs the callbacks for the given event.
76
76
  #
@@ -94,40 +94,15 @@ module ActiveSupport
94
94
  # callback can be as noisy as it likes -- but when control has passed
95
95
  # smoothly through and into the supplied block, we want as little evidence
96
96
  # as possible that we were here.
97
- def run_callbacks(kind)
97
+ def run_callbacks(kind, type = nil)
98
98
  callbacks = __callbacks[kind.to_sym]
99
99
 
100
100
  if callbacks.empty?
101
101
  yield if block_given?
102
102
  else
103
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
104
+
105
+ next_sequence = callbacks.compile(type)
131
106
 
132
107
  # Common case: no 'around' callbacks defined
133
108
  if next_sequence.final?
@@ -136,6 +111,33 @@ module ActiveSupport
136
111
  next_sequence.invoke_after(env)
137
112
  env.value
138
113
  else
114
+ invoke_sequence = Proc.new do
115
+ skipped = nil
116
+
117
+ while true
118
+ current = next_sequence
119
+ current.invoke_before(env)
120
+ if current.final?
121
+ env.value = !env.halted && (!block_given? || yield)
122
+ elsif current.skip?(env)
123
+ (skipped ||= []) << current
124
+ next_sequence = next_sequence.nested
125
+ next
126
+ else
127
+ next_sequence = next_sequence.nested
128
+ begin
129
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
130
+ target.send(method, *arguments, &block)
131
+ ensure
132
+ next_sequence = current
133
+ end
134
+ end
135
+ current.invoke_after(env)
136
+ skipped.pop.invoke_after(env) while skipped&.first
137
+ break env.value
138
+ end
139
+ end
140
+
139
141
  invoke_sequence.call
140
142
  end
141
143
  end
@@ -145,7 +147,7 @@ module ActiveSupport
145
147
  # A hook invoked every time a before callback is halted.
146
148
  # This can be overridden in ActiveSupport::Callbacks implementors in order
147
149
  # to provide better debugging/logging.
148
- def halted_callback_hook(filter)
150
+ def halted_callback_hook(filter, name)
149
151
  end
150
152
 
151
153
  module Conditionals # :nodoc:
@@ -161,17 +163,17 @@ module ActiveSupport
161
163
  Environment = Struct.new(:target, :halted, :value)
162
164
 
163
165
  class Before
164
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
166
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
165
167
  halted_lambda = chain_config[:terminator]
166
168
 
167
169
  if user_conditions.any?
168
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
170
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
169
171
  else
170
- halting(callback_sequence, user_callback, halted_lambda, filter)
172
+ halting(callback_sequence, user_callback, halted_lambda, filter, name)
171
173
  end
172
174
  end
173
175
 
174
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
176
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
175
177
  callback_sequence.before do |env|
176
178
  target = env.target
177
179
  value = env.value
@@ -181,7 +183,7 @@ module ActiveSupport
181
183
  result_lambda = -> { user_callback.call target, value }
182
184
  env.halted = halted_lambda.call(target, result_lambda)
183
185
  if env.halted
184
- target.send :halted_callback_hook, filter
186
+ target.send :halted_callback_hook, filter, name
185
187
  end
186
188
  end
187
189
 
@@ -190,7 +192,7 @@ module ActiveSupport
190
192
  end
191
193
  private_class_method :halting_and_conditional
192
194
 
193
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
195
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
194
196
  callback_sequence.before do |env|
195
197
  target = env.target
196
198
  value = env.value
@@ -199,9 +201,8 @@ module ActiveSupport
199
201
  unless halted
200
202
  result_lambda = -> { user_callback.call target, value }
201
203
  env.halted = halted_lambda.call(target, result_lambda)
202
-
203
204
  if env.halted
204
- target.send :halted_callback_hook, filter
205
+ target.send :halted_callback_hook, filter, name
205
206
  end
206
207
  end
207
208
 
@@ -279,7 +280,7 @@ module ActiveSupport
279
280
  end
280
281
  end
281
282
 
282
- class Callback #:nodoc:#
283
+ class Callback # :nodoc:#
283
284
  def self.build(chain, filter, kind, options)
284
285
  if filter.is_a?(String)
285
286
  raise ArgumentError, <<-MSG.squish
@@ -292,21 +293,17 @@ module ActiveSupport
292
293
  end
293
294
 
294
295
  attr_accessor :kind, :name
295
- attr_reader :chain_config
296
+ attr_reader :chain_config, :filter
296
297
 
297
298
  def initialize(name, filter, kind, options, chain_config)
298
299
  @chain_config = chain_config
299
300
  @name = name
300
301
  @kind = kind
301
302
  @filter = filter
302
- @key = compute_identifier filter
303
- @if = check_conditionals(Array(options[:if]))
304
- @unless = check_conditionals(Array(options[:unless]))
303
+ @if = check_conditionals(options[:if])
304
+ @unless = check_conditionals(options[:unless])
305
305
  end
306
306
 
307
- def filter; @key; end
308
- def raw_filter; @filter; end
309
-
310
307
  def merge_conditional_options(chain, if_option:, unless_option:)
311
308
  options = {
312
309
  if: @if.dup,
@@ -339,7 +336,7 @@ module ActiveSupport
339
336
 
340
337
  case kind
341
338
  when :before
342
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
339
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
343
340
  when :after
344
341
  Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
345
342
  when :around
@@ -352,8 +349,14 @@ module ActiveSupport
352
349
  end
353
350
 
354
351
  private
352
+ EMPTY_ARRAY = [].freeze
353
+ private_constant :EMPTY_ARRAY
354
+
355
355
  def check_conditionals(conditionals)
356
- if conditionals.any? { |c| c.is_a?(String) }
356
+ return EMPTY_ARRAY if conditionals.blank?
357
+
358
+ conditionals = Array(conditionals)
359
+ if conditionals.any?(String)
357
360
  raise ArgumentError, <<-MSG.squish
358
361
  Passing string to be evaluated in :if and :unless conditional
359
362
  options is not supported. Pass a symbol for an instance method,
@@ -361,16 +364,7 @@ module ActiveSupport
361
364
  MSG
362
365
  end
363
366
 
364
- conditionals
365
- end
366
-
367
- def compute_identifier(filter)
368
- case filter
369
- when ::Proc
370
- filter.object_id
371
- else
372
- filter
373
- end
367
+ conditionals.freeze
374
368
  end
375
369
 
376
370
  def conditions_lambdas
@@ -381,60 +375,153 @@ module ActiveSupport
381
375
 
382
376
  # A future invocation of user-supplied code (either as a callback,
383
377
  # or a condition filter).
384
- class CallTemplate # :nodoc:
385
- def initialize(target, method, arguments, block)
386
- @override_target = target
387
- @method_name = method
388
- @arguments = arguments
389
- @override_block = block
378
+ module CallTemplate # :nodoc:
379
+ class MethodCall
380
+ def initialize(method)
381
+ @method_name = method
382
+ end
383
+
384
+ # Return the parts needed to make this call, with the given
385
+ # input values.
386
+ #
387
+ # Returns an array of the form:
388
+ #
389
+ # [target, block, method, *arguments]
390
+ #
391
+ # This array can be used as such:
392
+ #
393
+ # target.send(method, *arguments, &block)
394
+ #
395
+ # The actual invocation is left up to the caller to minimize
396
+ # call stack pollution.
397
+ def expand(target, value, block)
398
+ [target, block, @method_name]
399
+ end
400
+
401
+ def make_lambda
402
+ lambda do |target, value, &block|
403
+ target.send(@method_name, &block)
404
+ end
405
+ end
406
+
407
+ def inverted_lambda
408
+ lambda do |target, value, &block|
409
+ !target.send(@method_name, &block)
410
+ end
411
+ end
390
412
  end
391
413
 
392
- # Return the parts needed to make this call, with the given
393
- # input values.
394
- #
395
- # Returns an array of the form:
396
- #
397
- # [target, block, method, *arguments]
398
- #
399
- # This array can be used as such:
400
- #
401
- # target.send(method, *arguments, &block)
402
- #
403
- # The actual invocation is left up to the caller to minimize
404
- # call stack pollution.
405
- def expand(target, value, block)
406
- result = @arguments.map { |arg|
407
- case arg
408
- when :value; value
409
- when :target; target
410
- when :block; block || raise(ArgumentError)
414
+ class ObjectCall
415
+ def initialize(target, method)
416
+ @override_target = target
417
+ @method_name = method
418
+ end
419
+
420
+ def expand(target, value, block)
421
+ [@override_target || target, block, @method_name, target]
422
+ end
423
+
424
+ def make_lambda
425
+ lambda do |target, value, &block|
426
+ (@override_target || target).send(@method_name, target, &block)
411
427
  end
412
- }
428
+ end
413
429
 
414
- result.unshift @method_name
415
- result.unshift @override_block || block
416
- result.unshift @override_target || target
430
+ def inverted_lambda
431
+ lambda do |target, value, &block|
432
+ !(@override_target || target).send(@method_name, target, &block)
433
+ end
434
+ end
435
+ end
417
436
 
418
- # target, block, method, *arguments = result
419
- # target.send(method, *arguments, &block)
420
- result
437
+ class InstanceExec0
438
+ def initialize(block)
439
+ @override_block = block
440
+ end
441
+
442
+ def expand(target, value, block)
443
+ [target, @override_block, :instance_exec]
444
+ end
445
+
446
+ def make_lambda
447
+ lambda do |target, value, &block|
448
+ target.instance_exec(&@override_block)
449
+ end
450
+ end
451
+
452
+ def inverted_lambda
453
+ lambda do |target, value, &block|
454
+ !target.instance_exec(&@override_block)
455
+ end
456
+ end
457
+ end
458
+
459
+ class InstanceExec1
460
+ def initialize(block)
461
+ @override_block = block
462
+ end
463
+
464
+ def expand(target, value, block)
465
+ [target, @override_block, :instance_exec, target]
466
+ end
467
+
468
+ def make_lambda
469
+ lambda do |target, value, &block|
470
+ target.instance_exec(target, &@override_block)
471
+ end
472
+ end
473
+
474
+ def inverted_lambda
475
+ lambda do |target, value, &block|
476
+ !target.instance_exec(target, &@override_block)
477
+ end
478
+ end
421
479
  end
422
480
 
423
- # Return a lambda that will make this call when given the input
424
- # values.
425
- def make_lambda
426
- lambda do |target, value, &block|
427
- target, block, method, *arguments = expand(target, value, block)
428
- target.send(method, *arguments, &block)
481
+ class InstanceExec2
482
+ def initialize(block)
483
+ @override_block = block
484
+ end
485
+
486
+ def expand(target, value, block)
487
+ raise ArgumentError unless block
488
+ [target, @override_block || block, :instance_exec, target, block]
489
+ end
490
+
491
+ def make_lambda
492
+ lambda do |target, value, &block|
493
+ raise ArgumentError unless block
494
+ target.instance_exec(target, block, &@override_block)
495
+ end
496
+ end
497
+
498
+ def inverted_lambda
499
+ lambda do |target, value, &block|
500
+ raise ArgumentError unless block
501
+ !target.instance_exec(target, block, &@override_block)
502
+ end
429
503
  end
430
504
  end
431
505
 
432
- # Return a lambda that will make this call when given the input
433
- # values, but then return the boolean inverse of that result.
434
- def inverted_lambda
435
- lambda do |target, value, &block|
436
- target, block, method, *arguments = expand(target, value, block)
437
- ! target.send(method, *arguments, &block)
506
+ class ProcCall
507
+ def initialize(target)
508
+ @override_target = target
509
+ end
510
+
511
+ def expand(target, value, block)
512
+ [@override_target || target, block, :call, target, value]
513
+ end
514
+
515
+ def make_lambda
516
+ lambda do |target, value, &block|
517
+ (@override_target || target).call(target, value, &block)
518
+ end
519
+ end
520
+
521
+ def inverted_lambda
522
+ lambda do |target, value, &block|
523
+ !(@override_target || target).call(target, value, &block)
524
+ end
438
525
  end
439
526
  end
440
527
 
@@ -449,21 +536,19 @@ module ActiveSupport
449
536
  def self.build(filter, callback)
450
537
  case filter
451
538
  when Symbol
452
- new(nil, filter, [], nil)
539
+ MethodCall.new(filter)
453
540
  when Conditionals::Value
454
- new(filter, :call, [:target, :value], nil)
541
+ ProcCall.new(filter)
455
542
  when ::Proc
456
543
  if filter.arity > 1
457
- new(nil, :instance_exec, [:target, :block], filter)
544
+ InstanceExec2.new(filter)
458
545
  elsif filter.arity > 0
459
- new(nil, :instance_exec, [:target], filter)
546
+ InstanceExec1.new(filter)
460
547
  else
461
- new(nil, :instance_exec, [], filter)
548
+ InstanceExec0.new(filter)
462
549
  end
463
550
  else
464
- method_to_call = callback.current_scopes.join("_")
465
-
466
- new(filter, method_to_call, [:target], nil)
551
+ ObjectCall.new(filter, callback.current_scopes.join("_").to_sym)
467
552
  end
468
553
  end
469
554
  end
@@ -518,7 +603,7 @@ module ActiveSupport
518
603
  end
519
604
  end
520
605
 
521
- class CallbackChain #:nodoc:#
606
+ class CallbackChain # :nodoc:
522
607
  include Enumerable
523
608
 
524
609
  attr_reader :name, :config
@@ -530,7 +615,8 @@ module ActiveSupport
530
615
  terminator: default_terminator
531
616
  }.merge!(config)
532
617
  @chain = []
533
- @callbacks = nil
618
+ @all_callbacks = nil
619
+ @single_callbacks = {}
534
620
  @mutex = Mutex.new
535
621
  end
536
622
 
@@ -539,32 +625,45 @@ module ActiveSupport
539
625
  def empty?; @chain.empty?; end
540
626
 
541
627
  def insert(index, o)
542
- @callbacks = nil
628
+ @all_callbacks = nil
629
+ @single_callbacks.clear
543
630
  @chain.insert(index, o)
544
631
  end
545
632
 
546
633
  def delete(o)
547
- @callbacks = nil
634
+ @all_callbacks = nil
635
+ @single_callbacks.clear
548
636
  @chain.delete(o)
549
637
  end
550
638
 
551
639
  def clear
552
- @callbacks = nil
640
+ @all_callbacks = nil
641
+ @single_callbacks.clear
553
642
  @chain.clear
554
643
  self
555
644
  end
556
645
 
557
646
  def initialize_copy(other)
558
- @callbacks = nil
647
+ @all_callbacks = nil
648
+ @single_callbacks = {}
559
649
  @chain = other.chain.dup
560
650
  @mutex = Mutex.new
561
651
  end
562
652
 
563
- def compile
564
- @callbacks || @mutex.synchronize do
565
- final_sequence = CallbackSequence.new
566
- @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
567
- callback.apply callback_sequence
653
+ def compile(type)
654
+ if type.nil?
655
+ @all_callbacks || @mutex.synchronize do
656
+ final_sequence = CallbackSequence.new
657
+ @all_callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
658
+ callback.apply(callback_sequence)
659
+ end
660
+ end
661
+ else
662
+ @single_callbacks[type] || @mutex.synchronize do
663
+ final_sequence = CallbackSequence.new
664
+ @single_callbacks[type] ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
665
+ type == callback.kind ? callback.apply(callback_sequence) : callback_sequence
666
+ end
568
667
  end
569
668
  end
570
669
  end
@@ -582,19 +681,22 @@ module ActiveSupport
582
681
 
583
682
  private
584
683
  def append_one(callback)
585
- @callbacks = nil
684
+ @all_callbacks = nil
685
+ @single_callbacks.clear
586
686
  remove_duplicates(callback)
587
687
  @chain.push(callback)
588
688
  end
589
689
 
590
690
  def prepend_one(callback)
591
- @callbacks = nil
691
+ @all_callbacks = nil
692
+ @single_callbacks.clear
592
693
  remove_duplicates(callback)
593
694
  @chain.unshift(callback)
594
695
  end
595
696
 
596
697
  def remove_duplicates(callback)
597
- @callbacks = nil
698
+ @all_callbacks = nil
699
+ @single_callbacks.clear
598
700
  @chain.delete_if { |c| callback.duplicates?(c) }
599
701
  end
600
702
 
@@ -620,8 +722,8 @@ module ActiveSupport
620
722
 
621
723
  # This is used internally to append, prepend and skip callbacks to the
622
724
  # CallbackChain.
623
- def __update_callbacks(name) #:nodoc:
624
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
725
+ def __update_callbacks(name) # :nodoc:
726
+ self.descendants.prepend(self).reverse_each do |target|
625
727
  chain = target.get_callbacks name
626
728
  yield target, chain.dup
627
729
  end
@@ -641,7 +743,7 @@ module ActiveSupport
641
743
  #
642
744
  # The callback can be specified as a symbol naming an instance method; as a
643
745
  # proc, lambda, or block; or as an object that responds to a certain method
644
- # determined by the <tt>:scope</tt> argument to +define_callbacks+.
746
+ # determined by the <tt>:scope</tt> argument to #define_callbacks.
645
747
  #
646
748
  # If a proc, lambda, or block is given, its body is evaluated in the context
647
749
  # of the current object. It can also optionally accept the current object as
@@ -685,14 +787,39 @@ module ActiveSupport
685
787
  end
686
788
  end
687
789
 
688
- # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
790
+ # Skip a previously set callback. Like #set_callback, <tt>:if</tt> or
689
791
  # <tt>:unless</tt> options may be passed in order to control when the
690
792
  # callback is skipped.
691
793
  #
692
- # class Writer < Person
693
- # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
794
+ # Note: this example uses +PersonRecord+ and +#saving_message+, which you
795
+ # can see defined here[rdoc-ref:ActiveSupport::Callbacks]
796
+ #
797
+ # class Writer < PersonRecord
798
+ # attr_accessor :age
799
+ # skip_callback :save, :before, :saving_message, if: -> { age > 18 }
694
800
  # end
695
801
  #
802
+ # When if option returns true, callback is skipped.
803
+ #
804
+ # writer = Writer.new
805
+ # writer.age = 20
806
+ # writer.save
807
+ #
808
+ # Output:
809
+ # - save
810
+ # saved
811
+ #
812
+ # When if option returns false, callback is NOT skipped.
813
+ #
814
+ # young_writer = Writer.new
815
+ # young_writer.age = 17
816
+ # young_writer.save
817
+ #
818
+ # Output:
819
+ # saving...
820
+ # - save
821
+ # saved
822
+ #
696
823
  # An <tt>ArgumentError</tt> will be raised if the callback has not
697
824
  # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
698
825
  def skip_callback(name, *filter_list, &block)
@@ -723,7 +850,7 @@ module ActiveSupport
723
850
  def reset_callbacks(name)
724
851
  callbacks = get_callbacks name
725
852
 
726
- ActiveSupport::DescendantsTracker.descendants(self).each do |target|
853
+ self.descendants.each do |target|
727
854
  chain = target.get_callbacks(name).dup
728
855
  callbacks.each { |c| chain.delete(c) }
729
856
  target.set_callbacks name, chain
@@ -809,14 +936,14 @@ module ActiveSupport
809
936
  # <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
810
937
  #
811
938
  # Calling +define_callbacks+ multiple times with the same +names+ will
812
- # overwrite previous callbacks registered with +set_callback+.
939
+ # overwrite previous callbacks registered with #set_callback.
813
940
  def define_callbacks(*names)
814
941
  options = names.extract_options!
815
942
 
816
943
  names.each do |name|
817
944
  name = name.to_sym
818
945
 
819
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
946
+ ([self] + self.descendants).each do |target|
820
947
  target.set_callbacks name, CallbackChain.new(name, options)
821
948
  end
822
949
 
@@ -846,7 +973,11 @@ module ActiveSupport
846
973
  end
847
974
 
848
975
  def set_callbacks(name, callbacks) # :nodoc:
849
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
976
+ unless singleton_class.method_defined?(:__callbacks, false)
977
+ self.__callbacks = __callbacks.dup
978
+ end
979
+ self.__callbacks[name.to_sym] = callbacks
980
+ self.__callbacks
850
981
  end
851
982
  end
852
983
  end