activesupport 4.2.11.3 → 5.0.7.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +678 -348
- data/MIT-LICENSE +2 -2
- data/README.rdoc +2 -3
- data/lib/active_support/array_inquirer.rb +44 -0
- data/lib/active_support/backtrace_cleaner.rb +1 -1
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +36 -22
- data/lib/active_support/cache/mem_cache_store.rb +63 -54
- data/lib/active_support/cache/memory_store.rb +16 -21
- data/lib/active_support/cache/null_store.rb +1 -4
- data/lib/active_support/cache/strategy/local_cache.rb +31 -20
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
- data/lib/active_support/cache.rb +71 -87
- data/lib/active_support/callbacks.rb +109 -113
- data/lib/active_support/concern.rb +1 -1
- data/lib/active_support/concurrency/latch.rb +11 -12
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +1 -0
- data/lib/active_support/core_ext/array/access.rb +27 -1
- data/lib/active_support/core_ext/array/conversions.rb +6 -4
- data/lib/active_support/core_ext/array/grouping.rb +9 -18
- data/lib/active_support/core_ext/array/inquiry.rb +17 -0
- data/lib/active_support/core_ext/array/wrap.rb +5 -4
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
- data/lib/active_support/core_ext/class/attribute.rb +10 -9
- data/lib/active_support/core_ext/class/subclasses.rb +3 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/blank.rb +12 -0
- data/lib/active_support/core_ext/date/calculations.rb +1 -1
- data/lib/active_support/core_ext/date/conversions.rb +7 -6
- data/lib/active_support/core_ext/date.rb +1 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +100 -27
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
- data/lib/active_support/core_ext/date_time/blank.rb +12 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +14 -8
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_time.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +75 -25
- data/lib/active_support/core_ext/file/atomic.rb +30 -25
- data/lib/active_support/core_ext/hash/conversions.rb +22 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +9 -8
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +25 -21
- data/lib/active_support/core_ext/hash/slice.rb +1 -1
- data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
- data/lib/active_support/core_ext/integer/time.rb +2 -2
- data/lib/active_support/core_ext/kernel/concern.rb +2 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -84
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +5 -2
- data/lib/active_support/core_ext/marshal.rb +7 -9
- data/lib/active_support/core_ext/module/aliasing.rb +6 -1
- data/lib/active_support/core_ext/module/anonymous.rb +10 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
- data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
- data/lib/active_support/core_ext/module/concerning.rb +4 -4
- data/lib/active_support/core_ext/module/delegation.rb +11 -20
- data/lib/active_support/core_ext/module/deprecation.rb +2 -2
- data/lib/active_support/core_ext/module/introspection.rb +8 -2
- data/lib/active_support/core_ext/module/method_transplanting.rb +3 -13
- data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
- data/lib/active_support/core_ext/module/remove_method.rb +23 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/name_error.rb +15 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +78 -77
- data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
- data/lib/active_support/core_ext/numeric/time.rb +26 -6
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/blank.rb +15 -3
- data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
- data/lib/active_support/core_ext/object/duplicable.rb +7 -12
- data/lib/active_support/core_ext/object/inclusion.rb +2 -2
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +15 -7
- data/lib/active_support/core_ext/object/to_query.rb +1 -1
- data/lib/active_support/core_ext/object/try.rb +67 -21
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/object.rb +0 -1
- data/lib/active_support/core_ext/range/conversions.rb +18 -6
- data/lib/active_support/core_ext/range/each.rb +16 -18
- data/lib/active_support/core_ext/range/include_range.rb +20 -20
- data/lib/active_support/core_ext/securerandom.rb +23 -0
- data/lib/active_support/core_ext/string/behavior.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- data/lib/active_support/core_ext/string/filters.rb +1 -2
- data/lib/active_support/core_ext/string/inflections.rb +32 -5
- data/lib/active_support/core_ext/string/multibyte.rb +11 -7
- data/lib/active_support/core_ext/string/output_safety.rb +12 -14
- data/lib/active_support/core_ext/string/strip.rb +3 -6
- data/lib/active_support/core_ext/struct.rb +3 -6
- data/lib/active_support/core_ext/time/calculations.rb +18 -9
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/marshal.rb +2 -29
- data/lib/active_support/core_ext/time/zones.rb +36 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext/uri.rb +1 -3
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies.rb +88 -95
- data/lib/active_support/deprecation/behaviors.rb +15 -1
- data/lib/active_support/deprecation/instance_delegator.rb +13 -0
- data/lib/active_support/deprecation/method_wrappers.rb +42 -16
- data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
- data/lib/active_support/deprecation/reporting.rb +23 -5
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration/iso8601_parser.rb +122 -0
- data/lib/active_support/duration/iso8601_serializer.rb +51 -0
- data/lib/active_support/duration.rb +90 -15
- data/lib/active_support/evented_file_update_checker.rb +199 -0
- data/lib/active_support/execution_wrapper.rb +126 -0
- data/lib/active_support/executor.rb +6 -0
- data/lib/active_support/file_update_checker.rb +23 -3
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/gzip.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +40 -11
- data/lib/active_support/i18n_railtie.rb +25 -4
- data/lib/active_support/inflector/inflections.rb +36 -5
- data/lib/active_support/inflector/methods.rb +97 -90
- data/lib/active_support/inflector/transliterate.rb +36 -21
- data/lib/active_support/json/decoding.rb +11 -10
- data/lib/active_support/json/encoding.rb +1 -51
- data/lib/active_support/key_generator.rb +7 -9
- data/lib/active_support/lazy_load_hooks.rb +46 -18
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +3 -3
- data/lib/active_support/log_subscriber.rb +1 -1
- data/lib/active_support/logger.rb +3 -4
- data/lib/active_support/logger_silence.rb +2 -1
- data/lib/active_support/logger_thread_safe_level.rb +2 -3
- data/lib/active_support/message_encryptor.rb +7 -7
- data/lib/active_support/message_verifier.rb +70 -8
- data/lib/active_support/multibyte/chars.rb +12 -3
- data/lib/active_support/multibyte/unicode.rb +44 -21
- data/lib/active_support/notifications/fanout.rb +5 -5
- data/lib/active_support/notifications/instrumenter.rb +20 -2
- data/lib/active_support/notifications.rb +2 -2
- data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
- data/lib/active_support/number_helper.rb +90 -67
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/per_thread_registry.rb +3 -0
- data/lib/active_support/rails.rb +2 -2
- data/lib/active_support/railtie.rb +6 -1
- data/lib/active_support/reloader.rb +129 -0
- data/lib/active_support/rescuable.rb +101 -47
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/subscriber.rb +5 -10
- data/lib/active_support/tagged_logging.rb +8 -7
- data/lib/active_support/test_case.rb +17 -29
- data/lib/active_support/testing/assertions.rb +15 -13
- data/lib/active_support/testing/deprecation.rb +9 -8
- data/lib/active_support/testing/file_fixtures.rb +34 -0
- data/lib/active_support/testing/isolation.rb +22 -8
- data/lib/active_support/testing/method_call_assertions.rb +41 -0
- data/lib/active_support/testing/stream.rb +42 -0
- data/lib/active_support/testing/time_helpers.rb +3 -1
- data/lib/active_support/time_with_zone.rb +123 -33
- data/lib/active_support/values/time_zone.rb +101 -47
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support.rb +11 -6
- metadata +36 -17
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- data/lib/active_support/core_ext/thread.rb +0 -86
@@ -4,6 +4,9 @@ require 'active_support/core_ext/array/extract_options'
|
|
4
4
|
require 'active_support/core_ext/class/attribute'
|
5
5
|
require 'active_support/core_ext/kernel/reporting'
|
6
6
|
require 'active_support/core_ext/kernel/singleton_class'
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
8
|
+
require 'active_support/core_ext/string/filters'
|
9
|
+
require 'active_support/deprecation'
|
7
10
|
require 'thread'
|
8
11
|
|
9
12
|
module ActiveSupport
|
@@ -64,6 +67,12 @@ module ActiveSupport
|
|
64
67
|
|
65
68
|
CALLBACK_FILTER_TYPES = [:before, :after, :around]
|
66
69
|
|
70
|
+
# If true, Active Record and Active Model callbacks returning +false+ will
|
71
|
+
# halt the entire callback chain and display a deprecation message.
|
72
|
+
# If false, callback chains will only be halted by calling +throw :abort+.
|
73
|
+
# Defaults to +true+.
|
74
|
+
mattr_accessor(:halt_and_display_warning_on_return_false, instance_writer: false) { true }
|
75
|
+
|
67
76
|
# Runs the callbacks for the given event.
|
68
77
|
#
|
69
78
|
# Calls the before and around callbacks in the order they were set, yields
|
@@ -124,14 +133,10 @@ module ActiveSupport
|
|
124
133
|
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
|
125
134
|
halted_lambda = chain_config[:terminator]
|
126
135
|
|
127
|
-
if
|
136
|
+
if user_conditions.any?
|
128
137
|
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
129
|
-
elsif chain_config.key? :terminator
|
130
|
-
halting(callback_sequence, user_callback, halted_lambda, filter)
|
131
|
-
elsif user_conditions.any?
|
132
|
-
conditional(callback_sequence, user_callback, user_conditions)
|
133
138
|
else
|
134
|
-
|
139
|
+
halting(callback_sequence, user_callback, halted_lambda, filter)
|
135
140
|
end
|
136
141
|
end
|
137
142
|
|
@@ -142,8 +147,8 @@ module ActiveSupport
|
|
142
147
|
halted = env.halted
|
143
148
|
|
144
149
|
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
145
|
-
|
146
|
-
env.halted = halted_lambda.call(target,
|
150
|
+
result_lambda = -> { user_callback.call target, value }
|
151
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
147
152
|
if env.halted
|
148
153
|
target.send :halted_callback_hook, filter
|
149
154
|
end
|
@@ -161,8 +166,9 @@ module ActiveSupport
|
|
161
166
|
halted = env.halted
|
162
167
|
|
163
168
|
unless halted
|
164
|
-
|
165
|
-
env.halted = halted_lambda.call(target,
|
169
|
+
result_lambda = -> { user_callback.call target, value }
|
170
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
171
|
+
|
166
172
|
if env.halted
|
167
173
|
target.send :halted_callback_hook, filter
|
168
174
|
end
|
@@ -172,42 +178,15 @@ module ActiveSupport
|
|
172
178
|
end
|
173
179
|
end
|
174
180
|
private_class_method :halting
|
175
|
-
|
176
|
-
def self.conditional(callback_sequence, user_callback, user_conditions)
|
177
|
-
callback_sequence.before do |env|
|
178
|
-
target = env.target
|
179
|
-
value = env.value
|
180
|
-
|
181
|
-
if user_conditions.all? { |c| c.call(target, value) }
|
182
|
-
user_callback.call target, value
|
183
|
-
end
|
184
|
-
|
185
|
-
env
|
186
|
-
end
|
187
|
-
end
|
188
|
-
private_class_method :conditional
|
189
|
-
|
190
|
-
def self.simple(callback_sequence, user_callback)
|
191
|
-
callback_sequence.before do |env|
|
192
|
-
user_callback.call env.target, env.value
|
193
|
-
|
194
|
-
env
|
195
|
-
end
|
196
|
-
end
|
197
|
-
private_class_method :simple
|
198
181
|
end
|
199
182
|
|
200
183
|
class After
|
201
184
|
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
202
185
|
if chain_config[:skip_after_callbacks_if_terminated]
|
203
|
-
if
|
186
|
+
if user_conditions.any?
|
204
187
|
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
205
|
-
elsif chain_config.key?(:terminator)
|
206
|
-
halting(callback_sequence, user_callback)
|
207
|
-
elsif user_conditions.any?
|
208
|
-
conditional callback_sequence, user_callback, user_conditions
|
209
188
|
else
|
210
|
-
|
189
|
+
halting(callback_sequence, user_callback)
|
211
190
|
end
|
212
191
|
else
|
213
192
|
if user_conditions.any?
|
@@ -270,14 +249,10 @@ module ActiveSupport
|
|
270
249
|
|
271
250
|
class Around
|
272
251
|
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
273
|
-
if
|
252
|
+
if user_conditions.any?
|
274
253
|
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
275
|
-
elsif chain_config.key? :terminator
|
276
|
-
halting(callback_sequence, user_callback)
|
277
|
-
elsif user_conditions.any?
|
278
|
-
conditional(callback_sequence, user_callback, user_conditions)
|
279
254
|
else
|
280
|
-
|
255
|
+
halting(callback_sequence, user_callback)
|
281
256
|
end
|
282
257
|
end
|
283
258
|
|
@@ -289,13 +264,11 @@ module ActiveSupport
|
|
289
264
|
|
290
265
|
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
291
266
|
user_callback.call(target, value) {
|
292
|
-
|
293
|
-
env.value
|
267
|
+
run.call.value
|
294
268
|
}
|
295
|
-
|
296
269
|
env
|
297
270
|
else
|
298
|
-
run.call
|
271
|
+
run.call
|
299
272
|
end
|
300
273
|
end
|
301
274
|
end
|
@@ -307,51 +280,28 @@ module ActiveSupport
|
|
307
280
|
value = env.value
|
308
281
|
|
309
282
|
if env.halted
|
310
|
-
run.call
|
283
|
+
run.call
|
311
284
|
else
|
312
285
|
user_callback.call(target, value) {
|
313
|
-
|
314
|
-
env.value
|
286
|
+
run.call.value
|
315
287
|
}
|
316
288
|
env
|
317
289
|
end
|
318
290
|
end
|
319
291
|
end
|
320
292
|
private_class_method :halting
|
321
|
-
|
322
|
-
def self.conditional(callback_sequence, user_callback, user_conditions)
|
323
|
-
callback_sequence.around do |env, &run|
|
324
|
-
target = env.target
|
325
|
-
value = env.value
|
326
|
-
|
327
|
-
if user_conditions.all? { |c| c.call(target, value) }
|
328
|
-
user_callback.call(target, value) {
|
329
|
-
env = run.call env
|
330
|
-
env.value
|
331
|
-
}
|
332
|
-
env
|
333
|
-
else
|
334
|
-
run.call env
|
335
|
-
end
|
336
|
-
end
|
337
|
-
end
|
338
|
-
private_class_method :conditional
|
339
|
-
|
340
|
-
def self.simple(callback_sequence, user_callback)
|
341
|
-
callback_sequence.around do |env, &run|
|
342
|
-
user_callback.call(env.target, env.value) {
|
343
|
-
env = run.call env
|
344
|
-
env.value
|
345
|
-
}
|
346
|
-
env
|
347
|
-
end
|
348
|
-
end
|
349
|
-
private_class_method :simple
|
350
293
|
end
|
351
294
|
end
|
352
295
|
|
353
296
|
class Callback #:nodoc:#
|
354
297
|
def self.build(chain, filter, kind, options)
|
298
|
+
if filter.is_a?(String)
|
299
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
300
|
+
Passing string to define callback is deprecated and will be removed
|
301
|
+
in Rails 5.1 without replacement.
|
302
|
+
MSG
|
303
|
+
end
|
304
|
+
|
355
305
|
new chain.name, filter, kind, options, chain.config
|
356
306
|
end
|
357
307
|
|
@@ -371,14 +321,14 @@ module ActiveSupport
|
|
371
321
|
def filter; @key; end
|
372
322
|
def raw_filter; @filter; end
|
373
323
|
|
374
|
-
def
|
324
|
+
def merge_conditional_options(chain, if_option:, unless_option:)
|
375
325
|
options = {
|
376
326
|
:if => @if.dup,
|
377
327
|
:unless => @unless.dup
|
378
328
|
}
|
379
329
|
|
380
|
-
options[:if].concat Array(
|
381
|
-
options[:unless].concat Array(
|
330
|
+
options[:if].concat Array(unless_option)
|
331
|
+
options[:unless].concat Array(if_option)
|
382
332
|
|
383
333
|
self.class.build chain, @filter, @kind, options
|
384
334
|
end
|
@@ -493,17 +443,17 @@ module ActiveSupport
|
|
493
443
|
end
|
494
444
|
|
495
445
|
def around(&around)
|
496
|
-
CallbackSequence.new do
|
497
|
-
around.call(
|
498
|
-
self.call(
|
446
|
+
CallbackSequence.new do |arg|
|
447
|
+
around.call(arg) {
|
448
|
+
self.call(arg)
|
499
449
|
}
|
500
450
|
end
|
501
451
|
end
|
502
452
|
|
503
|
-
def call(
|
504
|
-
@before.each { |b| b.call(
|
505
|
-
value = @call.call(
|
506
|
-
@after.each { |a| a.call(
|
453
|
+
def call(arg)
|
454
|
+
@before.each { |b| b.call(arg) }
|
455
|
+
value = @call.call(arg)
|
456
|
+
@after.each { |a| a.call(arg) }
|
507
457
|
value
|
508
458
|
end
|
509
459
|
end
|
@@ -517,7 +467,8 @@ module ActiveSupport
|
|
517
467
|
def initialize(name, config)
|
518
468
|
@name = name
|
519
469
|
@config = {
|
520
|
-
:
|
470
|
+
scope: [:kind],
|
471
|
+
terminator: default_terminator
|
521
472
|
}.merge!(config)
|
522
473
|
@chain = []
|
523
474
|
@callbacks = nil
|
@@ -588,6 +539,17 @@ module ActiveSupport
|
|
588
539
|
@callbacks = nil
|
589
540
|
@chain.delete_if { |c| callback.duplicates?(c) }
|
590
541
|
end
|
542
|
+
|
543
|
+
def default_terminator
|
544
|
+
Proc.new do |target, result_lambda|
|
545
|
+
terminate = true
|
546
|
+
catch(:abort) do
|
547
|
+
result_lambda.call if result_lambda.is_a?(Proc)
|
548
|
+
terminate = false
|
549
|
+
end
|
550
|
+
terminate
|
551
|
+
end
|
552
|
+
end
|
591
553
|
end
|
592
554
|
|
593
555
|
module ClassMethods
|
@@ -609,18 +571,18 @@ module ActiveSupport
|
|
609
571
|
|
610
572
|
# Install a callback for the given event.
|
611
573
|
#
|
612
|
-
# set_callback :save, :before, :
|
613
|
-
# set_callback :save, :after, :
|
574
|
+
# set_callback :save, :before, :before_method
|
575
|
+
# set_callback :save, :after, :after_method, if: :condition
|
614
576
|
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
|
615
577
|
#
|
616
|
-
# The second
|
578
|
+
# The second argument indicates whether the callback is to be run +:before+,
|
617
579
|
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
|
618
580
|
# means the first example above can also be written as:
|
619
581
|
#
|
620
|
-
# set_callback :save, :
|
582
|
+
# set_callback :save, :before_method
|
621
583
|
#
|
622
584
|
# The callback can be specified as a symbol naming an instance method; as a
|
623
|
-
# proc, lambda, or block; as a string to be instance evaluated; or as an
|
585
|
+
# proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
|
624
586
|
# object that responds to a certain method determined by the <tt>:scope</tt>
|
625
587
|
# argument to +define_callbacks+.
|
626
588
|
#
|
@@ -664,19 +626,27 @@ module ActiveSupport
|
|
664
626
|
# class Writer < Person
|
665
627
|
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
|
666
628
|
# end
|
629
|
+
#
|
630
|
+
# An <tt>ArgumentError</tt> will be raised if the callback has not
|
631
|
+
# already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
|
667
632
|
def skip_callback(name, *filter_list, &block)
|
668
633
|
type, filters, options = normalize_callback_params(filter_list, block)
|
634
|
+
options[:raise] = true unless options.key?(:raise)
|
669
635
|
|
670
636
|
__update_callbacks(name) do |target, chain|
|
671
637
|
filters.each do |filter|
|
672
|
-
|
638
|
+
callback = chain.find {|c| c.matches?(type, filter) }
|
639
|
+
|
640
|
+
if !callback && options[:raise]
|
641
|
+
raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
|
642
|
+
end
|
673
643
|
|
674
|
-
if
|
675
|
-
|
676
|
-
chain.insert(chain.index(
|
644
|
+
if callback && (options.key?(:if) || options.key?(:unless))
|
645
|
+
new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
|
646
|
+
chain.insert(chain.index(callback), new_callback)
|
677
647
|
end
|
678
648
|
|
679
|
-
chain.delete(
|
649
|
+
chain.delete(callback)
|
680
650
|
end
|
681
651
|
target.set_callbacks name, chain
|
682
652
|
end
|
@@ -703,21 +673,23 @@ module ActiveSupport
|
|
703
673
|
# ===== Options
|
704
674
|
#
|
705
675
|
# * <tt>:terminator</tt> - Determines when a before filter will halt the
|
706
|
-
# callback chain, preventing following
|
707
|
-
# the event from being triggered.
|
708
|
-
#
|
709
|
-
#
|
676
|
+
# callback chain, preventing following before and around callbacks from
|
677
|
+
# being called and the event from being triggered.
|
678
|
+
# This should be a lambda to be executed.
|
679
|
+
# The current object and the result lambda of the callback will be provided
|
680
|
+
# to the terminator lambda.
|
710
681
|
#
|
711
|
-
# define_callbacks :validate, terminator: ->(target,
|
682
|
+
# define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
|
712
683
|
#
|
713
684
|
# In this example, if any before validate callbacks returns +false+,
|
714
|
-
#
|
715
|
-
#
|
685
|
+
# any successive before and around callback is not executed.
|
686
|
+
#
|
687
|
+
# The default terminator halts the chain when a callback throws +:abort+.
|
716
688
|
#
|
717
689
|
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
|
718
690
|
# callbacks should be terminated by the <tt>:terminator</tt> option. By
|
719
|
-
# default after callbacks executed no matter if callback chain was
|
720
|
-
# terminated or not.
|
691
|
+
# default after callbacks are executed no matter if callback chain was
|
692
|
+
# terminated or not. This option makes sense only when <tt>:terminator</tt>
|
721
693
|
# option is specified.
|
722
694
|
#
|
723
695
|
# * <tt>:scope</tt> - Indicates which methods should be executed when an
|
@@ -783,13 +755,37 @@ module ActiveSupport
|
|
783
755
|
|
784
756
|
protected
|
785
757
|
|
786
|
-
def get_callbacks(name)
|
758
|
+
def get_callbacks(name) # :nodoc:
|
787
759
|
send "_#{name}_callbacks"
|
788
760
|
end
|
789
761
|
|
790
|
-
def set_callbacks(name, callbacks)
|
762
|
+
def set_callbacks(name, callbacks) # :nodoc:
|
791
763
|
send "_#{name}_callbacks=", callbacks
|
792
764
|
end
|
765
|
+
|
766
|
+
def deprecated_false_terminator # :nodoc:
|
767
|
+
Proc.new do |target, result_lambda|
|
768
|
+
terminate = true
|
769
|
+
catch(:abort) do
|
770
|
+
result = result_lambda.call if result_lambda.is_a?(Proc)
|
771
|
+
if Callbacks.halt_and_display_warning_on_return_false && result == false
|
772
|
+
display_deprecation_warning_for_false_terminator
|
773
|
+
else
|
774
|
+
terminate = false
|
775
|
+
end
|
776
|
+
end
|
777
|
+
terminate
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
private
|
782
|
+
|
783
|
+
def display_deprecation_warning_for_false_terminator
|
784
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
785
|
+
Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
|
786
|
+
To explicitly halt the callback chain, please use `throw :abort` instead.
|
787
|
+
MSG
|
788
|
+
end
|
793
789
|
end
|
794
790
|
end
|
795
791
|
end
|
@@ -114,7 +114,7 @@ module ActiveSupport
|
|
114
114
|
return false
|
115
115
|
else
|
116
116
|
return false if base < self
|
117
|
-
@_dependencies.each { |dep| base.
|
117
|
+
@_dependencies.each { |dep| base.include(dep) }
|
118
118
|
super
|
119
119
|
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
120
120
|
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
|
@@ -1,26 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
require 'monitor'
|
1
|
+
require 'concurrent/atomic/count_down_latch'
|
3
2
|
|
4
3
|
module ActiveSupport
|
5
4
|
module Concurrency
|
6
5
|
class Latch
|
6
|
+
|
7
7
|
def initialize(count = 1)
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
if count == 1
|
9
|
+
ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::Event instead.")
|
10
|
+
else
|
11
|
+
ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.")
|
12
|
+
end
|
13
|
+
|
14
|
+
@inner = Concurrent::CountDownLatch.new(count)
|
11
15
|
end
|
12
16
|
|
13
17
|
def release
|
14
|
-
@
|
15
|
-
@count -= 1 if @count > 0
|
16
|
-
@cv.broadcast if @count.zero?
|
17
|
-
end
|
18
|
+
@inner.count_down
|
18
19
|
end
|
19
20
|
|
20
21
|
def await
|
21
|
-
@
|
22
|
-
@cv.wait_while { @count > 0 }
|
23
|
-
end
|
22
|
+
@inner.wait(nil)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
module Concurrency
|
6
|
+
# A share/exclusive lock, otherwise known as a read/write lock.
|
7
|
+
#
|
8
|
+
# https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
|
9
|
+
class ShareLock
|
10
|
+
include MonitorMixin
|
11
|
+
|
12
|
+
# We track Thread objects, instead of just using counters, because
|
13
|
+
# we need exclusive locks to be reentrant, and we need to be able
|
14
|
+
# to upgrade share locks to exclusive.
|
15
|
+
|
16
|
+
|
17
|
+
def raw_state # :nodoc:
|
18
|
+
synchronize do
|
19
|
+
threads = @sleeping.keys | @sharing.keys | @waiting.keys
|
20
|
+
threads |= [@exclusive_thread] if @exclusive_thread
|
21
|
+
|
22
|
+
data = {}
|
23
|
+
|
24
|
+
threads.each do |thread|
|
25
|
+
purpose, compatible = @waiting[thread]
|
26
|
+
|
27
|
+
data[thread] = {
|
28
|
+
thread: thread,
|
29
|
+
sharing: @sharing[thread],
|
30
|
+
exclusive: @exclusive_thread == thread,
|
31
|
+
purpose: purpose,
|
32
|
+
compatible: compatible,
|
33
|
+
waiting: !!@waiting[thread],
|
34
|
+
sleeper: @sleeping[thread],
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# NB: Yields while holding our *internal* synchronize lock,
|
39
|
+
# which is supposed to be used only for a few instructions at
|
40
|
+
# a time. This allows the caller to inspect additional state
|
41
|
+
# without things changing out from underneath, but would have
|
42
|
+
# disastrous effects upon normal operation. Fortunately, this
|
43
|
+
# method is only intended to be called when things have
|
44
|
+
# already gone wrong.
|
45
|
+
yield data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
super()
|
51
|
+
|
52
|
+
@cv = new_cond
|
53
|
+
|
54
|
+
@sharing = Hash.new(0)
|
55
|
+
@waiting = {}
|
56
|
+
@sleeping = {}
|
57
|
+
@exclusive_thread = nil
|
58
|
+
@exclusive_depth = 0
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns false if +no_wait+ is set and the lock is not
|
62
|
+
# immediately available. Otherwise, returns true after the lock
|
63
|
+
# has been acquired.
|
64
|
+
#
|
65
|
+
# +purpose+ and +compatible+ work together; while this thread is
|
66
|
+
# waiting for the exclusive lock, it will yield its share (if any)
|
67
|
+
# to any other attempt whose +purpose+ appears in this attempt's
|
68
|
+
# +compatible+ list. This allows a "loose" upgrade, which, being
|
69
|
+
# less strict, prevents some classes of deadlocks.
|
70
|
+
#
|
71
|
+
# For many resources, loose upgrades are sufficient: if a thread
|
72
|
+
# is awaiting a lock, it is not running any other code. With
|
73
|
+
# +purpose+ matching, it is possible to yield only to other
|
74
|
+
# threads whose activity will not interfere.
|
75
|
+
def start_exclusive(purpose: nil, compatible: [], no_wait: false)
|
76
|
+
synchronize do
|
77
|
+
unless @exclusive_thread == Thread.current
|
78
|
+
if busy_for_exclusive?(purpose)
|
79
|
+
return false if no_wait
|
80
|
+
|
81
|
+
yield_shares(purpose: purpose, compatible: compatible, block_share: true) do
|
82
|
+
wait_for(:start_exclusive) { busy_for_exclusive?(purpose) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@exclusive_thread = Thread.current
|
86
|
+
end
|
87
|
+
@exclusive_depth += 1
|
88
|
+
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Relinquish the exclusive lock. Must only be called by the thread
|
94
|
+
# that called start_exclusive (and currently holds the lock).
|
95
|
+
def stop_exclusive(compatible: [])
|
96
|
+
synchronize do
|
97
|
+
raise "invalid unlock" if @exclusive_thread != Thread.current
|
98
|
+
|
99
|
+
@exclusive_depth -= 1
|
100
|
+
if @exclusive_depth == 0
|
101
|
+
@exclusive_thread = nil
|
102
|
+
|
103
|
+
if eligible_waiters?(compatible)
|
104
|
+
yield_shares(compatible: compatible, block_share: true) do
|
105
|
+
wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
@cv.broadcast
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def start_sharing
|
114
|
+
synchronize do
|
115
|
+
if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current
|
116
|
+
# We already hold a lock; nothing to wait for
|
117
|
+
elsif @waiting[Thread.current]
|
118
|
+
# We're nested inside a +yield_shares+ call: we'll resume as
|
119
|
+
# soon as there isn't an exclusive lock in our way
|
120
|
+
wait_for(:start_sharing) { @exclusive_thread }
|
121
|
+
else
|
122
|
+
# This is an initial / outermost share call: any outstanding
|
123
|
+
# requests for an exclusive lock get to go first
|
124
|
+
wait_for(:start_sharing) { busy_for_sharing?(false) }
|
125
|
+
end
|
126
|
+
@sharing[Thread.current] += 1
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def stop_sharing
|
131
|
+
synchronize do
|
132
|
+
if @sharing[Thread.current] > 1
|
133
|
+
@sharing[Thread.current] -= 1
|
134
|
+
else
|
135
|
+
@sharing.delete Thread.current
|
136
|
+
@cv.broadcast
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Execute the supplied block while holding the Exclusive lock. If
|
142
|
+
# +no_wait+ is set and the lock is not immediately available,
|
143
|
+
# returns +nil+ without yielding. Otherwise, returns the result of
|
144
|
+
# the block.
|
145
|
+
#
|
146
|
+
# See +start_exclusive+ for other options.
|
147
|
+
def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
|
148
|
+
if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
|
149
|
+
begin
|
150
|
+
yield
|
151
|
+
ensure
|
152
|
+
stop_exclusive(compatible: after_compatible)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Execute the supplied block while holding the Share lock.
|
158
|
+
def sharing
|
159
|
+
start_sharing
|
160
|
+
begin
|
161
|
+
yield
|
162
|
+
ensure
|
163
|
+
stop_sharing
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Temporarily give up all held Share locks while executing the
|
168
|
+
# supplied block, allowing any +compatible+ exclusive lock request
|
169
|
+
# to proceed.
|
170
|
+
def yield_shares(purpose: nil, compatible: [], block_share: false)
|
171
|
+
loose_shares = previous_wait = nil
|
172
|
+
synchronize do
|
173
|
+
if loose_shares = @sharing.delete(Thread.current)
|
174
|
+
if previous_wait = @waiting[Thread.current]
|
175
|
+
purpose = nil unless purpose == previous_wait[0]
|
176
|
+
compatible &= previous_wait[1]
|
177
|
+
end
|
178
|
+
compatible |= [false] unless block_share
|
179
|
+
@waiting[Thread.current] = [purpose, compatible]
|
180
|
+
end
|
181
|
+
|
182
|
+
@cv.broadcast
|
183
|
+
end
|
184
|
+
|
185
|
+
begin
|
186
|
+
yield
|
187
|
+
ensure
|
188
|
+
synchronize do
|
189
|
+
wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current }
|
190
|
+
|
191
|
+
if previous_wait
|
192
|
+
@waiting[Thread.current] = previous_wait
|
193
|
+
else
|
194
|
+
@waiting.delete Thread.current
|
195
|
+
end
|
196
|
+
@sharing[Thread.current] = loose_shares if loose_shares
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
# Must be called within synchronize
|
204
|
+
def busy_for_exclusive?(purpose)
|
205
|
+
busy_for_sharing?(purpose) ||
|
206
|
+
@sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
|
207
|
+
end
|
208
|
+
|
209
|
+
def busy_for_sharing?(purpose)
|
210
|
+
(@exclusive_thread && @exclusive_thread != Thread.current) ||
|
211
|
+
@waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) }
|
212
|
+
end
|
213
|
+
|
214
|
+
def eligible_waiters?(compatible)
|
215
|
+
@waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
|
216
|
+
end
|
217
|
+
|
218
|
+
def wait_for(method)
|
219
|
+
@sleeping[Thread.current] = method
|
220
|
+
@cv.wait_while { yield }
|
221
|
+
ensure
|
222
|
+
@sleeping.delete Thread.current
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|