activesupport 6.0.6.1 → 6.1.0.rc1

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +337 -573
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/array_inquirer.rb +4 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +2 -2
  9. data/lib/active_support/cache/mem_cache_store.rb +20 -14
  10. data/lib/active_support/cache/memory_store.rb +38 -26
  11. data/lib/active_support/cache/redis_cache_store.rb +25 -25
  12. data/lib/active_support/cache/strategy/local_cache.rb +13 -4
  13. data/lib/active_support/cache.rb +75 -34
  14. data/lib/active_support/callbacks.rb +65 -56
  15. data/lib/active_support/concern.rb +46 -2
  16. data/lib/active_support/configurable.rb +3 -3
  17. data/lib/active_support/configuration_file.rb +46 -0
  18. data/lib/active_support/core_ext/benchmark.rb +2 -2
  19. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  20. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  21. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  22. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  23. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  24. data/lib/active_support/core_ext/enumerable.rb +76 -4
  25. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  26. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  27. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  28. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  29. data/lib/active_support/core_ext/load_error.rb +1 -1
  30. data/lib/active_support/core_ext/marshal.rb +2 -0
  31. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  32. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  33. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  34. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  35. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  36. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  37. data/lib/active_support/core_ext/name_error.rb +29 -2
  38. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  39. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  40. data/lib/active_support/core_ext/object/json.rb +5 -1
  41. data/lib/active_support/core_ext/object/try.rb +2 -2
  42. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  44. data/lib/active_support/core_ext/string/access.rb +5 -24
  45. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  46. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  47. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  48. data/lib/active_support/core_ext/string/output_safety.rb +8 -38
  49. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  50. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  51. data/lib/active_support/core_ext/symbol.rb +3 -0
  52. data/lib/active_support/core_ext/time/calculations.rb +16 -0
  53. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  54. data/lib/active_support/core_ext/uri.rb +5 -1
  55. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  56. data/lib/active_support/current_attributes.rb +7 -2
  57. data/lib/active_support/dependencies.rb +38 -24
  58. data/lib/active_support/deprecation/behaviors.rb +15 -2
  59. data/lib/active_support/deprecation/disallowed.rb +56 -0
  60. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  61. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  62. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  63. data/lib/active_support/deprecation/reporting.rb +50 -7
  64. data/lib/active_support/deprecation.rb +6 -1
  65. data/lib/active_support/descendants_tracker.rb +6 -2
  66. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  67. data/lib/active_support/duration.rb +71 -22
  68. data/lib/active_support/encrypted_file.rb +19 -2
  69. data/lib/active_support/environment_inquirer.rb +20 -0
  70. data/lib/active_support/evented_file_update_checker.rb +69 -133
  71. data/lib/active_support/execution_wrapper.rb +13 -16
  72. data/lib/active_support/fork_tracker.rb +58 -0
  73. data/lib/active_support/gem_version.rb +3 -3
  74. data/lib/active_support/hash_with_indifferent_access.rb +35 -22
  75. data/lib/active_support/i18n_railtie.rb +14 -19
  76. data/lib/active_support/inflector/inflections.rb +1 -2
  77. data/lib/active_support/inflector/methods.rb +35 -31
  78. data/lib/active_support/inflector/transliterate.rb +4 -4
  79. data/lib/active_support/json/decoding.rb +4 -4
  80. data/lib/active_support/json/encoding.rb +5 -1
  81. data/lib/active_support/key_generator.rb +1 -1
  82. data/lib/active_support/locale/en.yml +7 -3
  83. data/lib/active_support/log_subscriber.rb +8 -0
  84. data/lib/active_support/logger.rb +1 -1
  85. data/lib/active_support/logger_silence.rb +2 -26
  86. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  87. data/lib/active_support/message_encryptor.rb +4 -7
  88. data/lib/active_support/message_verifier.rb +5 -5
  89. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  90. data/lib/active_support/messages/rotator.rb +6 -5
  91. data/lib/active_support/multibyte/chars.rb +4 -42
  92. data/lib/active_support/multibyte/unicode.rb +9 -83
  93. data/lib/active_support/notifications/fanout.rb +23 -8
  94. data/lib/active_support/notifications/instrumenter.rb +6 -15
  95. data/lib/active_support/notifications.rb +31 -4
  96. data/lib/active_support/number_helper/number_converter.rb +1 -1
  97. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  98. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  99. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  100. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  101. data/lib/active_support/number_helper.rb +29 -14
  102. data/lib/active_support/option_merger.rb +2 -1
  103. data/lib/active_support/ordered_options.rb +8 -2
  104. data/lib/active_support/parameter_filter.rb +15 -10
  105. data/lib/active_support/per_thread_registry.rb +1 -1
  106. data/lib/active_support/rails.rb +1 -4
  107. data/lib/active_support/railtie.rb +23 -1
  108. data/lib/active_support/reloader.rb +1 -1
  109. data/lib/active_support/secure_compare_rotator.rb +51 -0
  110. data/lib/active_support/security_utils.rb +19 -12
  111. data/lib/active_support/string_inquirer.rb +4 -2
  112. data/lib/active_support/subscriber.rb +12 -7
  113. data/lib/active_support/tagged_logging.rb +29 -4
  114. data/lib/active_support/testing/assertions.rb +18 -11
  115. data/lib/active_support/testing/parallelization/server.rb +78 -0
  116. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  117. data/lib/active_support/testing/parallelization.rb +12 -95
  118. data/lib/active_support/testing/time_helpers.rb +40 -3
  119. data/lib/active_support/time_with_zone.rb +66 -42
  120. data/lib/active_support/values/time_zone.rb +20 -10
  121. data/lib/active_support/xml_mini/rexml.rb +8 -1
  122. data/lib/active_support.rb +13 -1
  123. metadata +39 -42
  124. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  125. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  126. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  127. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  128. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  129. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -3,10 +3,12 @@
3
3
  require "zlib"
4
4
  require "active_support/core_ext/array/extract_options"
5
5
  require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/enumerable"
6
7
  require "active_support/core_ext/module/attribute_accessors"
7
8
  require "active_support/core_ext/numeric/bytes"
8
9
  require "active_support/core_ext/numeric/time"
9
10
  require "active_support/core_ext/object/to_param"
11
+ require "active_support/core_ext/object/try"
10
12
  require "active_support/core_ext/string/inflections"
11
13
 
12
14
  module ActiveSupport
@@ -20,7 +22,7 @@ module ActiveSupport
20
22
 
21
23
  # These options mean something to all cache implementations. Individual cache
22
24
  # implementations may support additional options.
23
- UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
25
+ UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl, :coder]
24
26
 
25
27
  module Strategy
26
28
  autoload :LocalCache, "active_support/cache/strategy/local_cache"
@@ -79,7 +81,7 @@ module ActiveSupport
79
81
  #
80
82
  # The +key+ argument can also respond to +cache_key+ or +to_param+.
81
83
  def expand_cache_key(key, namespace = nil)
82
- expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
84
+ expanded_cache_key = namespace ? +"#{namespace}/" : +""
83
85
 
84
86
  if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
85
87
  expanded_cache_key << "#{prefix}/"
@@ -156,6 +158,8 @@ module ActiveSupport
156
158
  # threshold is configurable with the <tt>:compress_threshold</tt> option,
157
159
  # specified in bytes.
158
160
  class Store
161
+ DEFAULT_CODER = Marshal
162
+
159
163
  cattr_accessor :logger, instance_writer: true
160
164
 
161
165
  attr_reader :silence, :options
@@ -183,6 +187,7 @@ module ActiveSupport
183
187
  # namespace for the cache.
184
188
  def initialize(options = nil)
185
189
  @options = options ? options.dup : {}
190
+ @coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
186
191
  end
187
192
 
188
193
  # Silences the logger.
@@ -441,8 +446,8 @@ module ActiveSupport
441
446
  instrument :read_multi, names, options do |payload|
442
447
  reads = read_multi_entries(names, **options)
443
448
  writes = {}
444
- ordered = names.each_with_object({}) do |name, hash|
445
- hash[name] = reads.fetch(name) { writes[name] = yield(name) }
449
+ ordered = names.index_with do |name|
450
+ reads.fetch(name) { writes[name] = yield(name) }
446
451
  end
447
452
 
448
453
  payload[:hits] = reads.keys
@@ -477,6 +482,18 @@ module ActiveSupport
477
482
  end
478
483
  end
479
484
 
485
+ # Deletes multiple entries in the cache.
486
+ #
487
+ # Options are passed to the underlying cache implementation.
488
+ def delete_multi(names, options = nil)
489
+ options = merged_options(options)
490
+ names.map! { |key| normalize_key(key, options) }
491
+
492
+ instrument :delete_multi, names do
493
+ delete_multi_entries(names, **options)
494
+ end
495
+ end
496
+
480
497
  # Returns +true+ if the cache contains an entry for the given key.
481
498
  #
482
499
  # Options are passed to the underlying cache implementation.
@@ -567,26 +584,31 @@ module ActiveSupport
567
584
  raise NotImplementedError.new
568
585
  end
569
586
 
587
+ def serialize_entry(entry)
588
+ @coder.dump(entry)
589
+ end
590
+
591
+ def deserialize_entry(payload)
592
+ payload.nil? ? nil : @coder.load(payload)
593
+ end
594
+
570
595
  # Reads multiple entries from the cache implementation. Subclasses MAY
571
596
  # implement this method.
572
597
  def read_multi_entries(names, **options)
573
- results = {}
574
- names.each do |name|
575
- key = normalize_key(name, options)
598
+ names.each_with_object({}) do |name, results|
599
+ key = normalize_key(name, options)
600
+ entry = read_entry(key, **options)
601
+
602
+ next unless entry
603
+
576
604
  version = normalize_version(name, options)
577
- entry = read_entry(key, **options)
578
-
579
- if entry
580
- if entry.expired?
581
- delete_entry(key, **options)
582
- elsif entry.mismatched?(version)
583
- # Skip mismatched versions
584
- else
585
- results[name] = entry.value
586
- end
605
+
606
+ if entry.expired?
607
+ delete_entry(key, **options)
608
+ elsif !entry.mismatched?(version)
609
+ results[name] = entry.value
587
610
  end
588
611
  end
589
- results
590
612
  end
591
613
 
592
614
  # Writes multiple entries to the cache implementation. Subclasses MAY
@@ -603,6 +625,12 @@ module ActiveSupport
603
625
  raise NotImplementedError.new
604
626
  end
605
627
 
628
+ # Deletes multiples entries in the cache implementation. Subclasses MAY
629
+ # implement this method.
630
+ def delete_multi_entries(entries, **options)
631
+ entries.count { |key| delete_entry(key, **options) }
632
+ end
633
+
606
634
  # Merges the default options with ones specific to a method call.
607
635
  def merged_options(call_options)
608
636
  if call_options
@@ -639,6 +667,10 @@ module ActiveSupport
639
667
  namespace = namespace.call
640
668
  end
641
669
 
670
+ if key && key.encoding != Encoding::UTF_8
671
+ key = key.dup.force_encoding(Encoding::UTF_8)
672
+ end
673
+
642
674
  if namespace
643
675
  "#{namespace}:#{key}"
644
676
  else
@@ -655,15 +687,15 @@ module ActiveSupport
655
687
  case key
656
688
  when Array
657
689
  if key.size > 1
658
- key = key.collect { |element| expanded_key(element) }
690
+ key.collect { |element| expanded_key(element) }
659
691
  else
660
- key = expanded_key(key.first)
692
+ expanded_key(key.first)
661
693
  end
662
694
  when Hash
663
- key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
664
- end
665
-
666
- key.to_param
695
+ key.collect { |k, v| "#{k}=#{v}" }.sort!
696
+ else
697
+ key
698
+ end.to_param
667
699
  end
668
700
 
669
701
  def normalize_version(key, options = nil)
@@ -673,24 +705,21 @@ module ActiveSupport
673
705
  def expanded_version(key)
674
706
  case
675
707
  when key.respond_to?(:cache_version) then key.cache_version.to_param
676
- when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
708
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
677
709
  when key.respond_to?(:to_a) then expanded_version(key.to_a)
678
710
  end
679
711
  end
680
712
 
681
713
  def instrument(operation, key, options = nil)
682
- log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
714
+ if logger && logger.debug? && !silence?
715
+ logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
716
+ end
683
717
 
684
- payload = { key: key }
718
+ payload = { key: key, store: self.class.name }
685
719
  payload.merge!(options) if options.is_a?(Hash)
686
720
  ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
687
721
  end
688
722
 
689
- def log
690
- return unless logger && logger.debug? && !silence?
691
- logger.debug(yield)
692
- end
693
-
694
723
  def handle_expired_entry(entry, key, options)
695
724
  if entry && entry.expired?
696
725
  race_ttl = options[:race_condition_ttl].to_i
@@ -722,6 +751,18 @@ module ActiveSupport
722
751
  end
723
752
  end
724
753
 
754
+ module NullCoder # :nodoc:
755
+ class << self
756
+ def load(payload)
757
+ payload
758
+ end
759
+
760
+ def dump(entry)
761
+ entry
762
+ end
763
+ end
764
+ end
765
+
725
766
  # This class is used to represent cache entries. Cache entries have a value, an optional
726
767
  # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
727
768
  # on the cache. The version is used to support the :version option on the cache for rejecting
@@ -772,8 +813,8 @@ module ActiveSupport
772
813
  end
773
814
 
774
815
  # Returns the size of the cached value. This could be less than
775
- # <tt>value.size</tt> if the data is compressed.
776
- def size
816
+ # <tt>value.bytesize</tt> if the data is compressed.
817
+ def bytesize
777
818
  case value
778
819
  when NilClass
779
820
  0
@@ -4,10 +4,7 @@ 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"
11
8
  require "thread"
12
9
 
13
10
  module ActiveSupport
@@ -103,32 +100,6 @@ module ActiveSupport
103
100
  env = Filters::Environment.new(self, false, nil)
104
101
  next_sequence = callbacks.compile
105
102
 
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
103
  # Common case: no 'around' callbacks defined
133
104
  if next_sequence.final?
134
105
  next_sequence.invoke_before(env)
@@ -136,6 +107,33 @@ module ActiveSupport
136
107
  next_sequence.invoke_after(env)
137
108
  env.value
138
109
  else
110
+ invoke_sequence = Proc.new do
111
+ skipped = nil
112
+
113
+ while true
114
+ current = next_sequence
115
+ current.invoke_before(env)
116
+ if current.final?
117
+ env.value = !env.halted && (!block_given? || yield)
118
+ elsif current.skip?(env)
119
+ (skipped ||= []) << current
120
+ next_sequence = next_sequence.nested
121
+ next
122
+ else
123
+ next_sequence = next_sequence.nested
124
+ begin
125
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
126
+ target.send(method, *arguments, &block)
127
+ ensure
128
+ next_sequence = current
129
+ end
130
+ end
131
+ current.invoke_after(env)
132
+ skipped.pop.invoke_after(env) while skipped&.first
133
+ break env.value
134
+ end
135
+ end
136
+
139
137
  invoke_sequence.call
140
138
  end
141
139
  end
@@ -145,7 +143,7 @@ module ActiveSupport
145
143
  # A hook invoked every time a before callback is halted.
146
144
  # This can be overridden in ActiveSupport::Callbacks implementors in order
147
145
  # to provide better debugging/logging.
148
- def halted_callback_hook(filter)
146
+ def halted_callback_hook(filter, name)
149
147
  end
150
148
 
151
149
  module Conditionals # :nodoc:
@@ -161,17 +159,17 @@ module ActiveSupport
161
159
  Environment = Struct.new(:target, :halted, :value)
162
160
 
163
161
  class Before
164
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
162
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
165
163
  halted_lambda = chain_config[:terminator]
166
164
 
167
165
  if user_conditions.any?
168
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
166
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
169
167
  else
170
- halting(callback_sequence, user_callback, halted_lambda, filter)
168
+ halting(callback_sequence, user_callback, halted_lambda, filter, name)
171
169
  end
172
170
  end
173
171
 
174
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
172
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
175
173
  callback_sequence.before do |env|
176
174
  target = env.target
177
175
  value = env.value
@@ -181,7 +179,7 @@ module ActiveSupport
181
179
  result_lambda = -> { user_callback.call target, value }
182
180
  env.halted = halted_lambda.call(target, result_lambda)
183
181
  if env.halted
184
- target.send :halted_callback_hook, filter
182
+ target.send :halted_callback_hook, filter, name
185
183
  end
186
184
  end
187
185
 
@@ -190,7 +188,7 @@ module ActiveSupport
190
188
  end
191
189
  private_class_method :halting_and_conditional
192
190
 
193
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
191
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
194
192
  callback_sequence.before do |env|
195
193
  target = env.target
196
194
  value = env.value
@@ -199,9 +197,8 @@ module ActiveSupport
199
197
  unless halted
200
198
  result_lambda = -> { user_callback.call target, value }
201
199
  env.halted = halted_lambda.call(target, result_lambda)
202
-
203
200
  if env.halted
204
- target.send :halted_callback_hook, filter
201
+ target.send :halted_callback_hook, filter, name
205
202
  end
206
203
  end
207
204
 
@@ -300,8 +297,8 @@ module ActiveSupport
300
297
  @kind = kind
301
298
  @filter = filter
302
299
  @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
304
  def filter; @key; end
@@ -339,7 +336,7 @@ module ActiveSupport
339
336
 
340
337
  case kind
341
338
  when :before
342
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
339
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
343
340
  when :after
344
341
  Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
345
342
  when :around
@@ -352,7 +349,13 @@ module ActiveSupport
352
349
  end
353
350
 
354
351
  private
352
+ EMPTY_ARRAY = [].freeze
353
+ private_constant :EMPTY_ARRAY
354
+
355
355
  def check_conditionals(conditionals)
356
+ return EMPTY_ARRAY if conditionals.blank?
357
+
358
+ conditionals = Array(conditionals)
356
359
  if conditionals.any? { |c| c.is_a?(String) }
357
360
  raise ArgumentError, <<-MSG.squish
358
361
  Passing string to be evaluated in :if and :unless conditional
@@ -361,7 +364,7 @@ module ActiveSupport
361
364
  MSG
362
365
  end
363
366
 
364
- conditionals
367
+ conditionals.freeze
365
368
  end
366
369
 
367
370
  def compute_identifier(filter)
@@ -403,21 +406,17 @@ module ActiveSupport
403
406
  # The actual invocation is left up to the caller to minimize
404
407
  # call stack pollution.
405
408
  def expand(target, value, block)
406
- result = @arguments.map { |arg|
409
+ expanded = [@override_target || target, @override_block || block, @method_name]
410
+
411
+ @arguments.each do |arg|
407
412
  case arg
408
- when :value; value
409
- when :target; target
410
- when :block; block || raise(ArgumentError)
413
+ when :value then expanded << value
414
+ when :target then expanded << target
415
+ when :block then expanded << (block || raise(ArgumentError))
411
416
  end
412
- }
413
-
414
- result.unshift @method_name
415
- result.unshift @override_block || block
416
- result.unshift @override_target || target
417
+ end
417
418
 
418
- # target, block, method, *arguments = result
419
- # target.send(method, *arguments, &block)
420
- result
419
+ expanded
421
420
  end
422
421
 
423
422
  # Return a lambda that will make this call when given the input
@@ -845,8 +844,18 @@ module ActiveSupport
845
844
  __callbacks[name.to_sym]
846
845
  end
847
846
 
848
- def set_callbacks(name, callbacks) # :nodoc:
849
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
847
+ if Module.instance_method(:method_defined?).arity == 1 # Ruby 2.5 and older
848
+ def set_callbacks(name, callbacks) # :nodoc:
849
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
850
+ end
851
+ else # Ruby 2.6 and newer
852
+ def set_callbacks(name, callbacks) # :nodoc:
853
+ unless singleton_class.method_defined?(:__callbacks, false)
854
+ self.__callbacks = __callbacks.dup
855
+ end
856
+ self.__callbacks[name.to_sym] = callbacks
857
+ self.__callbacks
858
+ end
850
859
  end
851
860
  end
852
861
  end
@@ -19,7 +19,7 @@ module ActiveSupport
19
19
  # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
20
20
  # written as:
21
21
  #
22
- # require 'active_support/concern'
22
+ # require "active_support/concern"
23
23
  #
24
24
  # module M
25
25
  # extend ActiveSupport::Concern
@@ -76,7 +76,7 @@ module ActiveSupport
76
76
  # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
77
77
  # module dependencies are properly resolved:
78
78
  #
79
- # require 'active_support/concern'
79
+ # require "active_support/concern"
80
80
  #
81
81
  # module Foo
82
82
  # extend ActiveSupport::Concern
@@ -99,6 +99,14 @@ module ActiveSupport
99
99
  # class Host
100
100
  # include Bar # It works, now Bar takes care of its dependencies
101
101
  # end
102
+ #
103
+ # === Prepending concerns
104
+ #
105
+ # Just like `include`, concerns also support `prepend` with a corresponding
106
+ # `prepended do` callback. `module ClassMethods` or `class_methods do` are
107
+ # prepended as well.
108
+ #
109
+ # `prepend` is also used for any dependencies.
102
110
  module Concern
103
111
  class MultipleIncludedBlocks < StandardError #:nodoc:
104
112
  def initialize
@@ -106,6 +114,12 @@ module ActiveSupport
106
114
  end
107
115
  end
108
116
 
117
+ class MultiplePrependBlocks < StandardError #:nodoc:
118
+ def initialize
119
+ super "Cannot define multiple 'prepended' blocks for a Concern"
120
+ end
121
+ end
122
+
109
123
  def self.extended(base) #:nodoc:
110
124
  base.instance_variable_set(:@_dependencies, [])
111
125
  end
@@ -123,6 +137,19 @@ module ActiveSupport
123
137
  end
124
138
  end
125
139
 
140
+ def prepend_features(base) #:nodoc:
141
+ if base.instance_variable_defined?(:@_dependencies)
142
+ base.instance_variable_get(:@_dependencies).unshift self
143
+ false
144
+ else
145
+ return false if base < self
146
+ @_dependencies.each { |dep| base.prepend(dep) }
147
+ super
148
+ base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
149
+ base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
150
+ end
151
+ end
152
+
126
153
  # Evaluate given block in context of base class,
127
154
  # so that you can write class macros here.
128
155
  # When you define more than one +included+ block, it raises an exception.
@@ -140,6 +167,23 @@ module ActiveSupport
140
167
  end
141
168
  end
142
169
 
170
+ # Evaluate given block in context of base class,
171
+ # so that you can write class macros here.
172
+ # When you define more than one +prepended+ block, it raises an exception.
173
+ def prepended(base = nil, &block)
174
+ if base.nil?
175
+ if instance_variable_defined?(:@_prepended_block)
176
+ if @_prepended_block.source_location != block.source_location
177
+ raise MultiplePrependBlocks
178
+ end
179
+ else
180
+ @_prepended_block = block
181
+ end
182
+ else
183
+ super
184
+ end
185
+ end
186
+
143
187
  # Define class methods from given block.
144
188
  # You can define private class methods as well.
145
189
  #
@@ -5,7 +5,7 @@ require "active_support/ordered_options"
5
5
 
6
6
  module ActiveSupport
7
7
  # Configurable provides a <tt>config</tt> method to store and retrieve
8
- # configuration options as an <tt>OrderedHash</tt>.
8
+ # configuration options as an <tt>OrderedOptions</tt>.
9
9
  module Configurable
10
10
  extend ActiveSupport::Concern
11
11
 
@@ -124,9 +124,9 @@ module ActiveSupport
124
124
  private :config_accessor
125
125
  end
126
126
 
127
- # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
127
+ # Reads and writes attributes from a configuration <tt>OrderedOptions</tt>.
128
128
  #
129
- # require 'active_support/configurable'
129
+ # require "active_support/configurable"
130
130
  #
131
131
  # class User
132
132
  # include ActiveSupport::Configurable
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Reads a YAML configuration file, evaluating any ERB, then
5
+ # parsing the resulting YAML.
6
+ #
7
+ # Warns in case of YAML confusing characters, like invisible
8
+ # non-breaking spaces.
9
+ class ConfigurationFile # :nodoc:
10
+ class FormatError < StandardError; end
11
+
12
+ def initialize(content_path)
13
+ @content_path = content_path.to_s
14
+ @content = read content_path
15
+ end
16
+
17
+ def self.parse(content_path, **options)
18
+ new(content_path).parse(**options)
19
+ end
20
+
21
+ def parse(context: nil, **options)
22
+ YAML.load(render(context), **options) || {}
23
+ rescue Psych::SyntaxError => error
24
+ raise "YAML syntax error occurred while parsing #{@content_path}. " \
25
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
26
+ "Error: #{error.message}"
27
+ end
28
+
29
+ private
30
+ def read(content_path)
31
+ require "yaml"
32
+ require "erb"
33
+
34
+ File.read(content_path).tap do |content|
35
+ if content.include?("\u00A0")
36
+ warn "File contains invisible non-breaking spaces, you may want to remove those"
37
+ end
38
+ end
39
+ end
40
+
41
+ def render(context)
42
+ erb = ERB.new(@content).tap { |e| e.filename = @content_path }
43
+ context ? erb.result(context) : erb.result
44
+ end
45
+ end
46
+ end
@@ -10,7 +10,7 @@ class << Benchmark
10
10
  #
11
11
  # Benchmark.ms { User.all }
12
12
  # # => 0.074
13
- def ms
14
- 1000 * realtime { yield }
13
+ def ms(&block)
14
+ 1000 * realtime(&block)
15
15
  end
16
16
  end