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.

Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +678 -348
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
  14. data/lib/active_support/cache.rb +71 -87
  15. data/lib/active_support/callbacks.rb +109 -113
  16. data/lib/active_support/concern.rb +1 -1
  17. data/lib/active_support/concurrency/latch.rb +11 -12
  18. data/lib/active_support/concurrency/share_lock.rb +226 -0
  19. data/lib/active_support/configurable.rb +1 -0
  20. data/lib/active_support/core_ext/array/access.rb +27 -1
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  22. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  23. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  24. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  25. data/lib/active_support/core_ext/array.rb +1 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  27. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  28. data/lib/active_support/core_ext/class/subclasses.rb +3 -2
  29. data/lib/active_support/core_ext/class.rb +0 -1
  30. data/lib/active_support/core_ext/date/blank.rb +12 -0
  31. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  32. data/lib/active_support/core_ext/date/conversions.rb +7 -6
  33. data/lib/active_support/core_ext/date.rb +1 -1
  34. data/lib/active_support/core_ext/date_and_time/calculations.rb +100 -27
  35. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -1
  36. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  37. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  38. data/lib/active_support/core_ext/date_time/calculations.rb +14 -8
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +1 -1
  41. data/lib/active_support/core_ext/enumerable.rb +75 -25
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +22 -2
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +25 -21
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +2 -2
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -84
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +5 -2
  56. data/lib/active_support/core_ext/marshal.rb +7 -9
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +11 -20
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +8 -2
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -13
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +78 -77
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +26 -6
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +15 -3
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +7 -12
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +67 -21
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  91. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  92. data/lib/active_support/core_ext/string/filters.rb +1 -2
  93. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  94. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  95. data/lib/active_support/core_ext/string/output_safety.rb +12 -14
  96. data/lib/active_support/core_ext/string/strip.rb +3 -6
  97. data/lib/active_support/core_ext/struct.rb +3 -6
  98. data/lib/active_support/core_ext/time/calculations.rb +18 -9
  99. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  100. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  101. data/lib/active_support/core_ext/time/zones.rb +36 -4
  102. data/lib/active_support/core_ext/time.rb +0 -1
  103. data/lib/active_support/core_ext/uri.rb +1 -3
  104. data/lib/active_support/core_ext.rb +2 -1
  105. data/lib/active_support/dependencies/interlock.rb +55 -0
  106. data/lib/active_support/dependencies.rb +88 -95
  107. data/lib/active_support/deprecation/behaviors.rb +15 -1
  108. data/lib/active_support/deprecation/instance_delegator.rb +13 -0
  109. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  110. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  111. data/lib/active_support/deprecation/reporting.rb +23 -5
  112. data/lib/active_support/deprecation.rb +1 -1
  113. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  114. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  115. data/lib/active_support/duration.rb +90 -15
  116. data/lib/active_support/evented_file_update_checker.rb +199 -0
  117. data/lib/active_support/execution_wrapper.rb +126 -0
  118. data/lib/active_support/executor.rb +6 -0
  119. data/lib/active_support/file_update_checker.rb +23 -3
  120. data/lib/active_support/gem_version.rb +5 -5
  121. data/lib/active_support/gzip.rb +1 -1
  122. data/lib/active_support/hash_with_indifferent_access.rb +40 -11
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +1 -51
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/lazy_load_hooks.rb +46 -18
  131. data/lib/active_support/locale/en.yml +2 -0
  132. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  133. data/lib/active_support/log_subscriber.rb +1 -1
  134. data/lib/active_support/logger.rb +3 -4
  135. data/lib/active_support/logger_silence.rb +2 -1
  136. data/lib/active_support/logger_thread_safe_level.rb +2 -3
  137. data/lib/active_support/message_encryptor.rb +7 -7
  138. data/lib/active_support/message_verifier.rb +70 -8
  139. data/lib/active_support/multibyte/chars.rb +12 -3
  140. data/lib/active_support/multibyte/unicode.rb +44 -21
  141. data/lib/active_support/notifications/fanout.rb +5 -5
  142. data/lib/active_support/notifications/instrumenter.rb +20 -2
  143. data/lib/active_support/notifications.rb +2 -2
  144. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  145. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  146. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  147. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  148. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  149. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  150. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  151. data/lib/active_support/number_helper.rb +90 -67
  152. data/lib/active_support/ordered_hash.rb +1 -1
  153. data/lib/active_support/ordered_options.rb +15 -1
  154. data/lib/active_support/per_thread_registry.rb +3 -0
  155. data/lib/active_support/rails.rb +2 -2
  156. data/lib/active_support/railtie.rb +6 -1
  157. data/lib/active_support/reloader.rb +129 -0
  158. data/lib/active_support/rescuable.rb +101 -47
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +8 -7
  162. data/lib/active_support/test_case.rb +17 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/deprecation.rb +9 -8
  165. data/lib/active_support/testing/file_fixtures.rb +34 -0
  166. data/lib/active_support/testing/isolation.rb +22 -8
  167. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  168. data/lib/active_support/testing/stream.rb +42 -0
  169. data/lib/active_support/testing/time_helpers.rb +3 -1
  170. data/lib/active_support/time_with_zone.rb +123 -33
  171. data/lib/active_support/values/time_zone.rb +101 -47
  172. data/lib/active_support/values/unicode_tables.dat +0 -0
  173. data/lib/active_support/xml_mini/jdom.rb +1 -1
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  176. data/lib/active_support.rb +11 -6
  177. metadata +36 -17
  178. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  179. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  180. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  181. data/lib/active_support/core_ext/object/itself.rb +0 -15
  182. 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 chain_config.key?(:terminator) && user_conditions.any?
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
- simple callback_sequence, user_callback
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
- result = user_callback.call target, value
146
- env.halted = halted_lambda.call(target, result)
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
- result = user_callback.call target, value
165
- env.halted = halted_lambda.call(target, result)
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 chain_config.key?(:terminator) && user_conditions.any?
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
- simple callback_sequence, user_callback
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 chain_config.key?(:terminator) && user_conditions.any?
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
- simple(callback_sequence, user_callback)
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
- env = run.call env
293
- env.value
267
+ run.call.value
294
268
  }
295
-
296
269
  env
297
270
  else
298
- run.call env
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 env
283
+ run.call
311
284
  else
312
285
  user_callback.call(target, value) {
313
- env = run.call env
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 merge(chain, new_options)
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(new_options.fetch(:unless, []))
381
- options[:unless].concat Array(new_options.fetch(:if, []))
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 |*args|
497
- around.call(*args) {
498
- self.call(*args)
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(*args)
504
- @before.each { |b| b.call(*args) }
505
- value = @call.call(*args)
506
- @after.each { |a| a.call(*args) }
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
- :scope => [ :kind ]
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, :before_meth
613
- # set_callback :save, :after, :after_meth, if: :condition
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 arguments indicates whether the callback is to be run +:before+,
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, :before_meth
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
- filter = chain.find {|c| c.matches?(type, filter) }
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 filter && options.any?
675
- new_filter = filter.merge(chain, options)
676
- chain.insert(chain.index(filter), new_filter)
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(filter)
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 callbacks from being called and
707
- # the event from being triggered. This should be a lambda to be executed.
708
- # The current object and the return result of the callback will be called
709
- # with the lambda.
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, result) { result == false }
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
- # other callbacks are not executed. Defaults to +false+, meaning no value
715
- # halts the chain.
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. Option makes sense only when <tt>:terminator</tt>
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.send(:include, dep) }
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 'thread'
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
- @count = count
9
- @lock = Monitor.new
10
- @cv = @lock.new_cond
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
- @lock.synchronize do
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
- @lock.synchronize do
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
@@ -122,6 +122,7 @@ module ActiveSupport
122
122
  send("#{name}=", yield) if block_given?
123
123
  end
124
124
  end
125
+ private :config_accessor
125
126
  end
126
127
 
127
128
  # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.