activesupport 6.0.4.4 → 7.0.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +257 -532
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +5 -5
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +163 -42
  10. data/lib/active_support/cache/memory_store.rb +57 -29
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +79 -98
  13. data/lib/active_support/cache/strategy/local_cache.rb +49 -57
  14. data/lib/active_support/cache.rb +378 -179
  15. data/lib/active_support/callbacks.rb +230 -122
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  34. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +164 -23
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/load_error.rb +1 -1
  54. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  57. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  58. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  59. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  60. data/lib/active_support/core_ext/name_error.rb +23 -2
  61. data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
  62. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  63. data/lib/active_support/core_ext/numeric.rb +1 -0
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  68. data/lib/active_support/core_ext/object/json.rb +42 -26
  69. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  70. data/lib/active_support/core_ext/object/try.rb +20 -20
  71. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  72. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  73. data/lib/active_support/core_ext/pathname.rb +3 -0
  74. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  75. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  76. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  77. data/lib/active_support/core_ext/range/each.rb +1 -1
  78. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  79. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  80. data/lib/active_support/core_ext/range.rb +1 -1
  81. data/lib/active_support/core_ext/regexp.rb +8 -1
  82. data/lib/active_support/core_ext/securerandom.rb +1 -1
  83. data/lib/active_support/core_ext/string/access.rb +5 -24
  84. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  85. data/lib/active_support/core_ext/string/filters.rb +1 -1
  86. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  87. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  88. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  89. data/lib/active_support/core_ext/string/output_safety.rb +92 -41
  90. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  92. data/lib/active_support/core_ext/symbol.rb +3 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +25 -7
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  95. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  96. data/lib/active_support/core_ext/time/zones.rb +7 -22
  97. data/lib/active_support/core_ext/time.rb +1 -0
  98. data/lib/active_support/core_ext/uri.rb +3 -23
  99. data/lib/active_support/core_ext.rb +2 -1
  100. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  101. data/lib/active_support/current_attributes.rb +39 -16
  102. data/lib/active_support/dependencies/interlock.rb +10 -18
  103. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  104. data/lib/active_support/dependencies.rb +58 -769
  105. data/lib/active_support/deprecation/behaviors.rb +23 -7
  106. data/lib/active_support/deprecation/disallowed.rb +56 -0
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  108. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  110. data/lib/active_support/deprecation/reporting.rb +50 -7
  111. data/lib/active_support/deprecation.rb +7 -2
  112. data/lib/active_support/descendants_tracker.rb +174 -64
  113. data/lib/active_support/digest.rb +5 -3
  114. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  115. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  116. data/lib/active_support/duration.rb +134 -55
  117. data/lib/active_support/encrypted_configuration.rb +13 -2
  118. data/lib/active_support/encrypted_file.rb +32 -3
  119. data/lib/active_support/environment_inquirer.rb +20 -0
  120. data/lib/active_support/error_reporter.rb +117 -0
  121. data/lib/active_support/evented_file_update_checker.rb +72 -138
  122. data/lib/active_support/execution_context/test_helper.rb +13 -0
  123. data/lib/active_support/execution_context.rb +53 -0
  124. data/lib/active_support/execution_wrapper.rb +43 -21
  125. data/lib/active_support/executor/test_helper.rb +7 -0
  126. data/lib/active_support/fork_tracker.rb +71 -0
  127. data/lib/active_support/gem_version.rb +3 -3
  128. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  129. data/lib/active_support/html_safe_translation.rb +43 -0
  130. data/lib/active_support/i18n.rb +1 -0
  131. data/lib/active_support/i18n_railtie.rb +14 -19
  132. data/lib/active_support/inflector/inflections.rb +24 -9
  133. data/lib/active_support/inflector/methods.rb +29 -49
  134. data/lib/active_support/inflector/transliterate.rb +5 -5
  135. data/lib/active_support/isolated_execution_state.rb +72 -0
  136. data/lib/active_support/json/decoding.rb +4 -4
  137. data/lib/active_support/json/encoding.rb +8 -4
  138. data/lib/active_support/key_generator.rb +23 -6
  139. data/lib/active_support/lazy_load_hooks.rb +28 -4
  140. data/lib/active_support/locale/en.yml +8 -4
  141. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  142. data/lib/active_support/log_subscriber.rb +23 -5
  143. data/lib/active_support/logger.rb +1 -1
  144. data/lib/active_support/logger_silence.rb +2 -26
  145. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  146. data/lib/active_support/message_encryptor.rb +16 -13
  147. data/lib/active_support/message_verifier.rb +50 -18
  148. data/lib/active_support/messages/metadata.rb +2 -2
  149. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  150. data/lib/active_support/messages/rotator.rb +6 -5
  151. data/lib/active_support/multibyte/chars.rb +13 -52
  152. data/lib/active_support/multibyte/unicode.rb +1 -87
  153. data/lib/active_support/multibyte.rb +1 -1
  154. data/lib/active_support/notifications/fanout.rb +110 -69
  155. data/lib/active_support/notifications/instrumenter.rb +37 -29
  156. data/lib/active_support/notifications.rb +55 -28
  157. data/lib/active_support/number_helper/number_converter.rb +2 -4
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  159. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  160. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  161. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  162. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  163. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  164. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  165. data/lib/active_support/number_helper.rb +29 -16
  166. data/lib/active_support/option_merger.rb +11 -18
  167. data/lib/active_support/ordered_hash.rb +1 -1
  168. data/lib/active_support/ordered_options.rb +9 -3
  169. data/lib/active_support/parameter_filter.rb +21 -11
  170. data/lib/active_support/per_thread_registry.rb +6 -1
  171. data/lib/active_support/rails.rb +1 -4
  172. data/lib/active_support/railtie.rb +77 -5
  173. data/lib/active_support/reloader.rb +1 -1
  174. data/lib/active_support/rescuable.rb +16 -16
  175. data/lib/active_support/ruby_features.rb +7 -0
  176. data/lib/active_support/secure_compare_rotator.rb +51 -0
  177. data/lib/active_support/security_utils.rb +19 -12
  178. data/lib/active_support/string_inquirer.rb +2 -2
  179. data/lib/active_support/subscriber.rb +19 -25
  180. data/lib/active_support/tagged_logging.rb +31 -6
  181. data/lib/active_support/test_case.rb +13 -21
  182. data/lib/active_support/testing/assertions.rb +50 -13
  183. data/lib/active_support/testing/deprecation.rb +52 -1
  184. data/lib/active_support/testing/isolation.rb +2 -2
  185. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  186. data/lib/active_support/testing/parallelization/server.rb +82 -0
  187. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  188. data/lib/active_support/testing/parallelization.rb +16 -95
  189. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  190. data/lib/active_support/testing/stream.rb +3 -5
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +53 -5
  193. data/lib/active_support/time_with_zone.rb +126 -62
  194. data/lib/active_support/values/time_zone.rb +54 -23
  195. data/lib/active_support/version.rb +1 -1
  196. data/lib/active_support/xml_mini/jdom.rb +1 -1
  197. data/lib/active_support/xml_mini/libxml.rb +5 -5
  198. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  199. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  200. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  201. data/lib/active_support/xml_mini/rexml.rb +9 -2
  202. data/lib/active_support/xml_mini.rb +5 -4
  203. data/lib/active_support.rb +29 -1
  204. metadata +46 -45
  205. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  206. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  207. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  208. data/lib/active_support/core_ext/marshal.rb +0 -24
  209. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  210. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  211. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  212. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -4,10 +4,8 @@ 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
@@ -18,19 +16,19 @@ module ActiveSupport
18
16
  # needing to override or redefine methods of the base class.
19
17
  #
20
18
  # 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+),
19
+ # life cycle that will support callbacks (via ClassMethods#define_callbacks),
22
20
  # set the instance methods, procs, or callback objects to be called (via
23
- # +ClassMethods.set_callback+), and run the installed callbacks at the
21
+ # ClassMethods#set_callback), and run the installed callbacks at the
24
22
  # appropriate times (via +run_callbacks+).
25
23
  #
26
24
  # By default callbacks are halted by throwing +:abort+.
27
- # See +ClassMethods.define_callbacks+ for details.
25
+ # See ClassMethods#define_callbacks for details.
28
26
  #
29
27
  # Three kinds of callbacks are supported: before callbacks, run before a
30
28
  # certain event; after callbacks, run after the event; and around callbacks,
31
29
  # blocks that surround the event, triggering it when they yield. Callback code
32
30
  # can be contained in instance methods, procs or lambdas, or callback objects
33
- # that respond to certain predetermined methods. See +ClassMethods.set_callback+
31
+ # that respond to certain predetermined methods. See ClassMethods#set_callback
34
32
  # for details.
35
33
  #
36
34
  # class Record
@@ -103,32 +101,6 @@ module ActiveSupport
103
101
  env = Filters::Environment.new(self, false, nil)
104
102
  next_sequence = callbacks.compile
105
103
 
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
104
  # Common case: no 'around' callbacks defined
133
105
  if next_sequence.final?
134
106
  next_sequence.invoke_before(env)
@@ -136,6 +108,33 @@ module ActiveSupport
136
108
  next_sequence.invoke_after(env)
137
109
  env.value
138
110
  else
111
+ invoke_sequence = Proc.new do
112
+ skipped = nil
113
+
114
+ while true
115
+ current = next_sequence
116
+ current.invoke_before(env)
117
+ if current.final?
118
+ env.value = !env.halted && (!block_given? || yield)
119
+ elsif current.skip?(env)
120
+ (skipped ||= []) << current
121
+ next_sequence = next_sequence.nested
122
+ next
123
+ else
124
+ next_sequence = next_sequence.nested
125
+ begin
126
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
127
+ target.send(method, *arguments, &block)
128
+ ensure
129
+ next_sequence = current
130
+ end
131
+ end
132
+ current.invoke_after(env)
133
+ skipped.pop.invoke_after(env) while skipped&.first
134
+ break env.value
135
+ end
136
+ end
137
+
139
138
  invoke_sequence.call
140
139
  end
141
140
  end
@@ -145,7 +144,7 @@ module ActiveSupport
145
144
  # A hook invoked every time a before callback is halted.
146
145
  # This can be overridden in ActiveSupport::Callbacks implementors in order
147
146
  # to provide better debugging/logging.
148
- def halted_callback_hook(filter)
147
+ def halted_callback_hook(filter, name)
149
148
  end
150
149
 
151
150
  module Conditionals # :nodoc:
@@ -161,17 +160,17 @@ module ActiveSupport
161
160
  Environment = Struct.new(:target, :halted, :value)
162
161
 
163
162
  class Before
164
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
163
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
165
164
  halted_lambda = chain_config[:terminator]
166
165
 
167
166
  if user_conditions.any?
168
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
167
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
169
168
  else
170
- halting(callback_sequence, user_callback, halted_lambda, filter)
169
+ halting(callback_sequence, user_callback, halted_lambda, filter, name)
171
170
  end
172
171
  end
173
172
 
174
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
173
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
175
174
  callback_sequence.before do |env|
176
175
  target = env.target
177
176
  value = env.value
@@ -181,7 +180,7 @@ module ActiveSupport
181
180
  result_lambda = -> { user_callback.call target, value }
182
181
  env.halted = halted_lambda.call(target, result_lambda)
183
182
  if env.halted
184
- target.send :halted_callback_hook, filter
183
+ target.send :halted_callback_hook, filter, name
185
184
  end
186
185
  end
187
186
 
@@ -190,7 +189,7 @@ module ActiveSupport
190
189
  end
191
190
  private_class_method :halting_and_conditional
192
191
 
193
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
192
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
194
193
  callback_sequence.before do |env|
195
194
  target = env.target
196
195
  value = env.value
@@ -199,9 +198,8 @@ module ActiveSupport
199
198
  unless halted
200
199
  result_lambda = -> { user_callback.call target, value }
201
200
  env.halted = halted_lambda.call(target, result_lambda)
202
-
203
201
  if env.halted
204
- target.send :halted_callback_hook, filter
202
+ target.send :halted_callback_hook, filter, name
205
203
  end
206
204
  end
207
205
 
@@ -279,7 +277,7 @@ module ActiveSupport
279
277
  end
280
278
  end
281
279
 
282
- class Callback #:nodoc:#
280
+ class Callback # :nodoc:#
283
281
  def self.build(chain, filter, kind, options)
284
282
  if filter.is_a?(String)
285
283
  raise ArgumentError, <<-MSG.squish
@@ -292,21 +290,17 @@ module ActiveSupport
292
290
  end
293
291
 
294
292
  attr_accessor :kind, :name
295
- attr_reader :chain_config
293
+ attr_reader :chain_config, :filter
296
294
 
297
295
  def initialize(name, filter, kind, options, chain_config)
298
296
  @chain_config = chain_config
299
297
  @name = name
300
298
  @kind = kind
301
299
  @filter = filter
302
- @key = compute_identifier filter
303
- @if = check_conditionals(Array(options[:if]))
304
- @unless = check_conditionals(Array(options[:unless]))
300
+ @if = check_conditionals(options[:if])
301
+ @unless = check_conditionals(options[:unless])
305
302
  end
306
303
 
307
- def filter; @key; end
308
- def raw_filter; @filter; end
309
-
310
304
  def merge_conditional_options(chain, if_option:, unless_option:)
311
305
  options = {
312
306
  if: @if.dup,
@@ -339,7 +333,7 @@ module ActiveSupport
339
333
 
340
334
  case kind
341
335
  when :before
342
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
336
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
343
337
  when :after
344
338
  Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
345
339
  when :around
@@ -352,8 +346,14 @@ module ActiveSupport
352
346
  end
353
347
 
354
348
  private
349
+ EMPTY_ARRAY = [].freeze
350
+ private_constant :EMPTY_ARRAY
351
+
355
352
  def check_conditionals(conditionals)
356
- if conditionals.any? { |c| c.is_a?(String) }
353
+ return EMPTY_ARRAY if conditionals.blank?
354
+
355
+ conditionals = Array(conditionals)
356
+ if conditionals.any?(String)
357
357
  raise ArgumentError, <<-MSG.squish
358
358
  Passing string to be evaluated in :if and :unless conditional
359
359
  options is not supported. Pass a symbol for an instance method,
@@ -361,16 +361,7 @@ module ActiveSupport
361
361
  MSG
362
362
  end
363
363
 
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
364
+ conditionals.freeze
374
365
  end
375
366
 
376
367
  def conditions_lambdas
@@ -381,60 +372,153 @@ module ActiveSupport
381
372
 
382
373
  # A future invocation of user-supplied code (either as a callback,
383
374
  # 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
375
+ module CallTemplate # :nodoc:
376
+ class MethodCall
377
+ def initialize(method)
378
+ @method_name = method
379
+ end
380
+
381
+ # Return the parts needed to make this call, with the given
382
+ # input values.
383
+ #
384
+ # Returns an array of the form:
385
+ #
386
+ # [target, block, method, *arguments]
387
+ #
388
+ # This array can be used as such:
389
+ #
390
+ # target.send(method, *arguments, &block)
391
+ #
392
+ # The actual invocation is left up to the caller to minimize
393
+ # call stack pollution.
394
+ def expand(target, value, block)
395
+ [target, block, @method_name]
396
+ end
397
+
398
+ def make_lambda
399
+ lambda do |target, value, &block|
400
+ target.send(@method_name, &block)
401
+ end
402
+ end
403
+
404
+ def inverted_lambda
405
+ lambda do |target, value, &block|
406
+ !target.send(@method_name, &block)
407
+ end
408
+ end
390
409
  end
391
410
 
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)
411
+ class ObjectCall
412
+ def initialize(target, method)
413
+ @override_target = target
414
+ @method_name = method
415
+ end
416
+
417
+ def expand(target, value, block)
418
+ [@override_target || target, block, @method_name, target]
419
+ end
420
+
421
+ def make_lambda
422
+ lambda do |target, value, &block|
423
+ (@override_target || target).send(@method_name, target, &block)
411
424
  end
412
- }
425
+ end
426
+
427
+ def inverted_lambda
428
+ lambda do |target, value, &block|
429
+ !(@override_target || target).send(@method_name, target, &block)
430
+ end
431
+ end
432
+ end
413
433
 
414
- result.unshift @method_name
415
- result.unshift @override_block || block
416
- result.unshift @override_target || target
434
+ class InstanceExec0
435
+ def initialize(block)
436
+ @override_block = block
437
+ end
417
438
 
418
- # target, block, method, *arguments = result
419
- # target.send(method, *arguments, &block)
420
- result
439
+ def expand(target, value, block)
440
+ [target, @override_block, :instance_exec]
441
+ end
442
+
443
+ def make_lambda
444
+ lambda do |target, value, &block|
445
+ target.instance_exec(&@override_block)
446
+ end
447
+ end
448
+
449
+ def inverted_lambda
450
+ lambda do |target, value, &block|
451
+ !target.instance_exec(&@override_block)
452
+ end
453
+ end
421
454
  end
422
455
 
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)
456
+ class InstanceExec1
457
+ def initialize(block)
458
+ @override_block = block
459
+ end
460
+
461
+ def expand(target, value, block)
462
+ [target, @override_block, :instance_exec, target]
463
+ end
464
+
465
+ def make_lambda
466
+ lambda do |target, value, &block|
467
+ target.instance_exec(target, &@override_block)
468
+ end
469
+ end
470
+
471
+ def inverted_lambda
472
+ lambda do |target, value, &block|
473
+ !target.instance_exec(target, &@override_block)
474
+ end
429
475
  end
430
476
  end
431
477
 
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)
478
+ class InstanceExec2
479
+ def initialize(block)
480
+ @override_block = block
481
+ end
482
+
483
+ def expand(target, value, block)
484
+ raise ArgumentError unless block
485
+ [target, @override_block || block, :instance_exec, target, block]
486
+ end
487
+
488
+ def make_lambda
489
+ lambda do |target, value, &block|
490
+ raise ArgumentError unless block
491
+ target.instance_exec(target, block, &@override_block)
492
+ end
493
+ end
494
+
495
+ def inverted_lambda
496
+ lambda do |target, value, &block|
497
+ raise ArgumentError unless block
498
+ !target.instance_exec(target, block, &@override_block)
499
+ end
500
+ end
501
+ end
502
+
503
+ class ProcCall
504
+ def initialize(target)
505
+ @override_target = target
506
+ end
507
+
508
+ def expand(target, value, block)
509
+ [@override_target || target, block, :call, target, value]
510
+ end
511
+
512
+ def make_lambda
513
+ lambda do |target, value, &block|
514
+ (@override_target || target).call(target, value, &block)
515
+ end
516
+ end
517
+
518
+ def inverted_lambda
519
+ lambda do |target, value, &block|
520
+ !(@override_target || target).call(target, value, &block)
521
+ end
438
522
  end
439
523
  end
440
524
 
@@ -449,21 +533,19 @@ module ActiveSupport
449
533
  def self.build(filter, callback)
450
534
  case filter
451
535
  when Symbol
452
- new(nil, filter, [], nil)
536
+ MethodCall.new(filter)
453
537
  when Conditionals::Value
454
- new(filter, :call, [:target, :value], nil)
538
+ ProcCall.new(filter)
455
539
  when ::Proc
456
540
  if filter.arity > 1
457
- new(nil, :instance_exec, [:target, :block], filter)
541
+ InstanceExec2.new(filter)
458
542
  elsif filter.arity > 0
459
- new(nil, :instance_exec, [:target], filter)
543
+ InstanceExec1.new(filter)
460
544
  else
461
- new(nil, :instance_exec, [], filter)
545
+ InstanceExec0.new(filter)
462
546
  end
463
547
  else
464
- method_to_call = callback.current_scopes.join("_")
465
-
466
- new(filter, method_to_call, [:target], nil)
548
+ ObjectCall.new(filter, callback.current_scopes.join("_").to_sym)
467
549
  end
468
550
  end
469
551
  end
@@ -518,7 +600,7 @@ module ActiveSupport
518
600
  end
519
601
  end
520
602
 
521
- class CallbackChain #:nodoc:#
603
+ class CallbackChain # :nodoc:
522
604
  include Enumerable
523
605
 
524
606
  attr_reader :name, :config
@@ -620,8 +702,8 @@ module ActiveSupport
620
702
 
621
703
  # This is used internally to append, prepend and skip callbacks to the
622
704
  # CallbackChain.
623
- def __update_callbacks(name) #:nodoc:
624
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
705
+ def __update_callbacks(name) # :nodoc:
706
+ ([self] + self.descendants).reverse_each do |target|
625
707
  chain = target.get_callbacks name
626
708
  yield target, chain.dup
627
709
  end
@@ -689,10 +771,32 @@ module ActiveSupport
689
771
  # <tt>:unless</tt> options may be passed in order to control when the
690
772
  # callback is skipped.
691
773
  #
692
- # class Writer < Person
693
- # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
774
+ # class Writer < PersonRecord
775
+ # attr_accessor :age
776
+ # skip_callback :save, :before, :saving_message, if: -> { age > 18 }
694
777
  # end
695
778
  #
779
+ # When if option returns true, callback is skipped.
780
+ #
781
+ # writer = Writer.new
782
+ # writer.age = 20
783
+ # writer.save
784
+ #
785
+ # Output:
786
+ # - save
787
+ # saved
788
+ #
789
+ # When if option returns false, callback is NOT skipped.
790
+ #
791
+ # young_writer = Writer.new
792
+ # young_writer.age = 17
793
+ # young_writer.save
794
+ #
795
+ # Output:
796
+ # saving...
797
+ # - save
798
+ # saved
799
+ #
696
800
  # An <tt>ArgumentError</tt> will be raised if the callback has not
697
801
  # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
698
802
  def skip_callback(name, *filter_list, &block)
@@ -723,7 +827,7 @@ module ActiveSupport
723
827
  def reset_callbacks(name)
724
828
  callbacks = get_callbacks name
725
829
 
726
- ActiveSupport::DescendantsTracker.descendants(self).each do |target|
830
+ self.descendants.each do |target|
727
831
  chain = target.get_callbacks(name).dup
728
832
  callbacks.each { |c| chain.delete(c) }
729
833
  target.set_callbacks name, chain
@@ -816,7 +920,7 @@ module ActiveSupport
816
920
  names.each do |name|
817
921
  name = name.to_sym
818
922
 
819
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
923
+ ([self] + self.descendants).each do |target|
820
924
  target.set_callbacks name, CallbackChain.new(name, options)
821
925
  end
822
926
 
@@ -846,7 +950,11 @@ module ActiveSupport
846
950
  end
847
951
 
848
952
  def set_callbacks(name, callbacks) # :nodoc:
849
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
953
+ unless singleton_class.method_defined?(:__callbacks, false)
954
+ self.__callbacks = __callbacks.dup
955
+ end
956
+ self.__callbacks[name.to_sym] = callbacks
957
+ self.__callbacks
850
958
  end
851
959
  end
852
960
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ class CodeGenerator # :nodoc:
5
+ class MethodSet
6
+ METHOD_CACHES = Hash.new { |h, k| h[k] = Module.new }
7
+
8
+ def initialize(namespace)
9
+ @cache = METHOD_CACHES[namespace]
10
+ @sources = []
11
+ @methods = {}
12
+ end
13
+
14
+ def define_cached_method(name, as: name)
15
+ name = name.to_sym
16
+ as = as.to_sym
17
+ @methods.fetch(name) do
18
+ unless @cache.method_defined?(as)
19
+ yield @sources
20
+ end
21
+ @methods[name] = as
22
+ end
23
+ end
24
+
25
+ def apply(owner, path, line)
26
+ unless @sources.empty?
27
+ @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line)
28
+ end
29
+ @methods.each do |name, as|
30
+ owner.define_method(name, @cache.instance_method(as))
31
+ end
32
+ end
33
+ end
34
+
35
+ class << self
36
+ def batch(owner, path, line)
37
+ if owner.is_a?(CodeGenerator)
38
+ yield owner
39
+ else
40
+ instance = new(owner, path, line)
41
+ result = yield instance
42
+ instance.execute
43
+ result
44
+ end
45
+ end
46
+ end
47
+
48
+ def initialize(owner, path, line)
49
+ @owner = owner
50
+ @path = path
51
+ @line = line
52
+ @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
53
+ end
54
+
55
+ def define_cached_method(name, namespace:, as: name, &block)
56
+ @namespaces[namespace].define_cached_method(name, as: as, &block)
57
+ end
58
+
59
+ def execute
60
+ @namespaces.each_value do |method_set|
61
+ method_set.apply(@owner, @path, @line - 1)
62
+ end
63
+ end
64
+ end
65
+ end