activesupport 5.2.5 → 6.0.0

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +327 -423
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -2
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +28 -1
  7. data/lib/active_support/cache/file_store.rb +22 -22
  8. data/lib/active_support/cache/mem_cache_store.rb +16 -2
  9. data/lib/active_support/cache/memory_store.rb +7 -2
  10. data/lib/active_support/cache/null_store.rb +5 -0
  11. data/lib/active_support/cache/redis_cache_store.rb +47 -25
  12. data/lib/active_support/cache.rb +45 -23
  13. data/lib/active_support/callbacks.rb +16 -5
  14. data/lib/active_support/concern.rb +24 -1
  15. data/lib/active_support/configurable.rb +7 -11
  16. data/lib/active_support/core_ext/array/access.rb +18 -6
  17. data/lib/active_support/core_ext/array/extract.rb +21 -0
  18. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  19. data/lib/active_support/core_ext/array.rb +1 -1
  20. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  21. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  22. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  23. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  24. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  25. data/lib/active_support/core_ext/enumerable.rb +97 -73
  26. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  27. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  28. data/lib/active_support/core_ext/hash/except.rb +1 -1
  29. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  30. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  31. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  32. data/lib/active_support/core_ext/hash.rb +1 -2
  33. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  34. data/lib/active_support/core_ext/kernel.rb +0 -1
  35. data/lib/active_support/core_ext/load_error.rb +1 -1
  36. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  37. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  38. data/lib/active_support/core_ext/module/delegation.rb +33 -7
  39. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  40. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  41. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  42. data/lib/active_support/core_ext/module.rb +0 -1
  43. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  44. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  45. data/lib/active_support/core_ext/numeric.rb +0 -1
  46. data/lib/active_support/core_ext/object/blank.rb +1 -2
  47. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  48. data/lib/active_support/core_ext/object/json.rb +1 -0
  49. data/lib/active_support/core_ext/object/try.rb +15 -7
  50. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  51. data/lib/active_support/core_ext/range/compare_range.rb +22 -13
  52. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  53. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  54. data/lib/active_support/core_ext/regexp.rb +0 -4
  55. data/lib/active_support/core_ext/securerandom.rb +23 -3
  56. data/lib/active_support/core_ext/string/access.rb +8 -0
  57. data/lib/active_support/core_ext/string/filters.rb +42 -1
  58. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  59. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  60. data/lib/active_support/core_ext/string/output_safety.rb +61 -5
  61. data/lib/active_support/core_ext/string/strip.rb +3 -1
  62. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  63. data/lib/active_support/core_ext/uri.rb +1 -0
  64. data/lib/active_support/current_attributes.rb +8 -0
  65. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  66. data/lib/active_support/dependencies.rb +69 -16
  67. data/lib/active_support/deprecation/behaviors.rb +1 -1
  68. data/lib/active_support/deprecation/method_wrappers.rb +8 -20
  69. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
  70. data/lib/active_support/deprecation.rb +1 -1
  71. data/lib/active_support/descendants_tracker.rb +56 -9
  72. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  73. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  74. data/lib/active_support/duration.rb +12 -15
  75. data/lib/active_support/encrypted_configuration.rb +0 -4
  76. data/lib/active_support/encrypted_file.rb +2 -1
  77. data/lib/active_support/evented_file_update_checker.rb +39 -9
  78. data/lib/active_support/execution_wrapper.rb +1 -0
  79. data/lib/active_support/gem_version.rb +3 -3
  80. data/lib/active_support/hash_with_indifferent_access.rb +22 -18
  81. data/lib/active_support/i18n.rb +1 -0
  82. data/lib/active_support/i18n_railtie.rb +9 -1
  83. data/lib/active_support/inflector/inflections.rb +1 -4
  84. data/lib/active_support/inflector/methods.rb +15 -27
  85. data/lib/active_support/inflector/transliterate.rb +47 -18
  86. data/lib/active_support/json/decoding.rb +23 -23
  87. data/lib/active_support/json/encoding.rb +6 -2
  88. data/lib/active_support/key_generator.rb +0 -32
  89. data/lib/active_support/lazy_load_hooks.rb +5 -1
  90. data/lib/active_support/locale/en.rb +31 -0
  91. data/lib/active_support/log_subscriber.rb +31 -8
  92. data/lib/active_support/logger.rb +0 -15
  93. data/lib/active_support/logger_silence.rb +28 -12
  94. data/lib/active_support/logger_thread_safe_level.rb +26 -4
  95. data/lib/active_support/message_encryptor.rb +3 -5
  96. data/lib/active_support/message_verifier.rb +3 -3
  97. data/lib/active_support/multibyte/chars.rb +29 -48
  98. data/lib/active_support/multibyte/unicode.rb +44 -281
  99. data/lib/active_support/notifications/fanout.rb +98 -13
  100. data/lib/active_support/notifications/instrumenter.rb +79 -8
  101. data/lib/active_support/notifications.rb +41 -4
  102. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  103. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  104. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  105. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  106. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  107. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  108. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  109. data/lib/active_support/number_helper.rb +7 -0
  110. data/lib/active_support/ordered_options.rb +1 -1
  111. data/lib/active_support/parameter_filter.rb +129 -0
  112. data/lib/active_support/rails.rb +0 -6
  113. data/lib/active_support/reloader.rb +4 -5
  114. data/lib/active_support/security_utils.rb +1 -1
  115. data/lib/active_support/subscriber.rb +65 -26
  116. data/lib/active_support/tagged_logging.rb +13 -4
  117. data/lib/active_support/test_case.rb +91 -0
  118. data/lib/active_support/testing/assertions.rb +15 -1
  119. data/lib/active_support/testing/deprecation.rb +0 -1
  120. data/lib/active_support/testing/file_fixtures.rb +2 -0
  121. data/lib/active_support/testing/isolation.rb +2 -2
  122. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  123. data/lib/active_support/testing/parallelization.rb +128 -0
  124. data/lib/active_support/testing/stream.rb +1 -1
  125. data/lib/active_support/testing/time_helpers.rb +7 -7
  126. data/lib/active_support/time_with_zone.rb +15 -5
  127. data/lib/active_support/values/time_zone.rb +12 -7
  128. data/lib/active_support/xml_mini/jdom.rb +2 -2
  129. data/lib/active_support/xml_mini/libxml.rb +2 -2
  130. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  131. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  132. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  133. data/lib/active_support/xml_mini/rexml.rb +2 -2
  134. data/lib/active_support/xml_mini.rb +2 -9
  135. data/lib/active_support.rb +2 -1
  136. metadata +34 -9
  137. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  138. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -20,6 +20,9 @@ module ActiveSupport #:nodoc:
20
20
  module Dependencies #:nodoc:
21
21
  extend self
22
22
 
23
+ UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
24
+ private_constant :UNBOUND_METHOD_MODULE_NAME
25
+
23
26
  mattr_accessor :interlock, default: Interlock.new
24
27
 
25
28
  # :doc:
@@ -70,6 +73,11 @@ module ActiveSupport #:nodoc:
70
73
  # only once. All directories in this set must also be present in +autoload_paths+.
71
74
  mattr_accessor :autoload_once_paths, default: []
72
75
 
76
+ # This is a private set that collects all eager load paths during bootstrap.
77
+ # Useful for Zeitwerk integration. Its public interface is the config.* path
78
+ # accessors of each engine.
79
+ mattr_accessor :_eager_load_paths, default: Set.new
80
+
73
81
  # An array of qualified constant names that have been loaded. Adding a name
74
82
  # to this array will cause it to be unloaded the next time Dependencies are
75
83
  # cleared.
@@ -79,6 +87,12 @@ module ActiveSupport #:nodoc:
79
87
  # to allow arbitrary constants to be marked for unloading.
80
88
  mattr_accessor :explicitly_unloadable_constants, default: []
81
89
 
90
+ # The logger used when tracing autoloads.
91
+ mattr_accessor :logger
92
+
93
+ # If true, trace autoloads with +logger.debug+.
94
+ mattr_accessor :verbose, default: false
95
+
82
96
  # The WatchStack keeps a stack of the modules being watched as files are
83
97
  # loaded. If a file in the process of being loaded (parent.rb) triggers the
84
98
  # load of another file (child.rb) the stack will ensure that child.rb
@@ -140,7 +154,7 @@ module ActiveSupport #:nodoc:
140
154
 
141
155
  # Normalize the list of new constants, and add them to the list we will return
142
156
  new_constants.each do |suffix|
143
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
157
+ constants << ([namespace, suffix] - ["Object"]).join("::")
144
158
  end
145
159
  end
146
160
  constants
@@ -190,6 +204,11 @@ module ActiveSupport #:nodoc:
190
204
  end
191
205
  end
192
206
 
207
+ def self.include_into(base)
208
+ base.include(self)
209
+ append_features(base)
210
+ end
211
+
193
212
  def const_missing(const_name)
194
213
  from_mod = anonymous? ? guess_for_anonymous(const_name) : self
195
214
  Dependencies.load_missing_constant(from_mod, const_name)
@@ -219,6 +238,21 @@ module ActiveSupport #:nodoc:
219
238
  base.class_eval do
220
239
  define_method(:load, Kernel.instance_method(:load))
221
240
  private :load
241
+
242
+ define_method(:require, Kernel.instance_method(:require))
243
+ private :require
244
+ end
245
+ end
246
+
247
+ def self.include_into(base)
248
+ base.include(self)
249
+
250
+ if base.instance_method(:load).owner == base
251
+ base.remove_method(:load)
252
+ end
253
+
254
+ if base.instance_method(:require).owner == base
255
+ base.remove_method(:require)
222
256
  end
223
257
  end
224
258
 
@@ -315,9 +349,9 @@ module ActiveSupport #:nodoc:
315
349
  end
316
350
 
317
351
  def hook!
318
- Object.class_eval { include Loadable }
319
- Module.class_eval { include ModuleConstMissing }
320
- Exception.class_eval { include Blamable }
352
+ Loadable.include_into(Object)
353
+ ModuleConstMissing.include_into(Module)
354
+ Exception.include(Blamable)
321
355
  end
322
356
 
323
357
  def unhook!
@@ -349,7 +383,7 @@ module ActiveSupport #:nodoc:
349
383
  end
350
384
 
351
385
  def require_or_load(file_name, const_path = nil)
352
- file_name = $` if file_name =~ /\.rb\z/
386
+ file_name = file_name.chomp(".rb")
353
387
  expanded = File.expand_path(file_name)
354
388
  return if loaded.include?(expanded)
355
389
 
@@ -399,7 +433,7 @@ module ActiveSupport #:nodoc:
399
433
  # constant paths which would cause Dependencies to attempt to load this
400
434
  # file.
401
435
  def loadable_constants_for_path(path, bases = autoload_paths)
402
- path = $` if path =~ /\.rb\z/
436
+ path = path.chomp(".rb")
403
437
  expanded_path = File.expand_path(path)
404
438
  paths = []
405
439
 
@@ -408,7 +442,7 @@ module ActiveSupport #:nodoc:
408
442
  next unless expanded_path.start_with?(expanded_root)
409
443
 
410
444
  root_size = expanded_root.size
411
- next if expanded_path[root_size] != ?/.freeze
445
+ next if expanded_path[root_size] != ?/
412
446
 
413
447
  nesting = expanded_path[(root_size + 1)..-1]
414
448
  paths << nesting.camelize unless nesting.blank?
@@ -420,7 +454,7 @@ module ActiveSupport #:nodoc:
420
454
 
421
455
  # Search for a file in autoload_paths matching the provided suffix.
422
456
  def search_for_file(path_suffix)
423
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
457
+ path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
424
458
 
425
459
  autoload_paths.each do |root|
426
460
  path = File.join(root, path_suffix)
@@ -454,6 +488,7 @@ module ActiveSupport #:nodoc:
454
488
  return nil unless base_path = autoloadable_module?(path_suffix)
455
489
  mod = Module.new
456
490
  into.const_set const_name, mod
491
+ log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
457
492
  autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
458
493
  autoloaded_constants.uniq!
459
494
  mod
@@ -495,26 +530,31 @@ module ActiveSupport #:nodoc:
495
530
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
496
531
  end
497
532
 
498
- qualified_name = qualified_name_for from_mod, const_name
533
+ qualified_name = qualified_name_for(from_mod, const_name)
499
534
  path_suffix = qualified_name.underscore
500
535
 
501
536
  file_path = search_for_file(path_suffix)
502
537
 
503
538
  if file_path
504
539
  expanded = File.expand_path(file_path)
505
- expanded.sub!(/\.rb\z/, "".freeze)
540
+ expanded.sub!(/\.rb\z/, "")
506
541
 
507
542
  if loading.include?(expanded)
508
543
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
509
544
  else
510
545
  require_or_load(expanded, qualified_name)
511
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
512
- return from_mod.const_get(const_name)
546
+
547
+ if from_mod.const_defined?(const_name, false)
548
+ log("constant #{qualified_name} autoloaded from #{expanded}.rb")
549
+ return from_mod.const_get(const_name)
550
+ else
551
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
552
+ end
513
553
  end
514
554
  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
515
555
  return mod
516
- elsif (parent = from_mod.parent) && parent != from_mod &&
517
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
556
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
557
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
518
558
  # If our parents do not have a constant named +const_name+ then we are free
519
559
  # to attempt to load upwards. If they do have such a constant, then this
520
560
  # const_missing must be due to from_mod::const_name, which should not
@@ -558,6 +598,7 @@ module ActiveSupport #:nodoc:
558
598
  # as the environment will be in an inconsistent state, e.g. other constants
559
599
  # may have already been unloaded and not accessible.
560
600
  def remove_unloadable_constants!
601
+ log("removing unloadable constants")
561
602
  autoloaded_constants.each { |const| remove_constant const }
562
603
  autoloaded_constants.clear
563
604
  Reference.clear!
@@ -621,7 +662,7 @@ module ActiveSupport #:nodoc:
621
662
 
622
663
  # Determine if the given constant has been automatically loaded.
623
664
  def autoloaded?(desc)
624
- return false if desc.is_a?(Module) && desc.anonymous?
665
+ return false if desc.is_a?(Module) && real_mod_name(desc).nil?
625
666
  name = to_constant_name desc
626
667
  return false unless qualified_const_defined?(name)
627
668
  autoloaded_constants.include?(name)
@@ -677,7 +718,7 @@ module ActiveSupport #:nodoc:
677
718
  when String then desc.sub(/^::/, "")
678
719
  when Symbol then desc.to_s
679
720
  when Module
680
- desc.name ||
721
+ real_mod_name(desc) ||
681
722
  raise(ArgumentError, "Anonymous modules have no name to be referenced by")
682
723
  else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
683
724
  end
@@ -747,6 +788,18 @@ module ActiveSupport #:nodoc:
747
788
  # The constant is no longer reachable, just skip it.
748
789
  end
749
790
  end
791
+
792
+ def log(message)
793
+ logger.debug("autoloading: #{message}") if logger && verbose
794
+ end
795
+
796
+ private
797
+
798
+ # Returns the original name of a class or module even if `name` has been
799
+ # overridden.
800
+ def real_mod_name(mod)
801
+ UNBOUND_METHOD_MODULE_NAME.bind(mod).call
802
+ end
750
803
  end
751
804
  end
752
805
 
@@ -43,7 +43,7 @@ module ActiveSupport
43
43
  deprecation_horizon: deprecation_horizon)
44
44
  },
45
45
 
46
- silence: ->(message, callstack, deprecation_horizon, gem_name) {},
46
+ silence: ->(message, callstack, deprecation_horizon, gem_name) { },
47
47
  }
48
48
 
49
49
  # Behavior module allows to determine how to display deprecation messages.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/module/aliasing"
4
3
  require "active_support/core_ext/array/extract_options"
4
+ require "active_support/core_ext/module/redefine_method"
5
5
 
6
6
  module ActiveSupport
7
7
  class Deprecation
@@ -53,37 +53,25 @@ module ActiveSupport
53
53
  options = method_names.extract_options!
54
54
  deprecator = options.delete(:deprecator) || self
55
55
  method_names += options.keys
56
- mod = Module.new
56
+ mod = nil
57
57
 
58
58
  method_names.each do |method_name|
59
59
  if target_module.method_defined?(method_name) || target_module.private_method_defined?(method_name)
60
- aliased_method, punctuation = method_name.to_s.sub(/([?!=])$/, ""), $1
61
- with_method = "#{aliased_method}_with_deprecation#{punctuation}"
62
- without_method = "#{aliased_method}_without_deprecation#{punctuation}"
63
-
64
- target_module.send(:define_method, with_method) do |*args, &block|
60
+ method = target_module.instance_method(method_name)
61
+ target_module.redefine_method(method_name) do |*args, &block|
65
62
  deprecator.deprecation_warning(method_name, options[method_name])
66
- send(without_method, *args, &block)
67
- end
68
-
69
- target_module.send(:alias_method, without_method, method_name)
70
- target_module.send(:alias_method, method_name, with_method)
71
-
72
- case
73
- when target_module.protected_method_defined?(without_method)
74
- target_module.send(:protected, method_name)
75
- when target_module.private_method_defined?(without_method)
76
- target_module.send(:private, method_name)
63
+ method.bind(self).call(*args, &block)
77
64
  end
78
65
  else
79
- mod.send(:define_method, method_name) do |*args, &block|
66
+ mod ||= Module.new
67
+ mod.define_method(method_name) do |*args, &block|
80
68
  deprecator.deprecation_warning(method_name, options[method_name])
81
69
  super(*args, &block)
82
70
  end
83
71
  end
84
72
  end
85
73
 
86
- target_module.prepend(mod) unless mod.instance_methods(false).empty?
74
+ target_module.prepend(mod) if mod
87
75
  end
88
76
  end
89
77
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/regexp"
4
-
5
3
  module ActiveSupport
6
4
  class Deprecation
7
5
  class DeprecationProxy #:nodoc:
@@ -122,7 +120,14 @@ module ActiveSupport
122
120
  # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
123
121
  # (Backtrace information…)
124
122
  # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
125
- class DeprecatedConstantProxy < DeprecationProxy
123
+ class DeprecatedConstantProxy < Module
124
+ def self.new(*args, &block)
125
+ object = args.first
126
+
127
+ return object unless object
128
+ super
129
+ end
130
+
126
131
  def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.")
127
132
  require "active_support/inflector/methods"
128
133
 
@@ -132,6 +137,14 @@ module ActiveSupport
132
137
  @message = message
133
138
  end
134
139
 
140
+ instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) }
141
+
142
+ # Don't give a deprecation warning on inspect since test/unit and error
143
+ # logs rely on it for diagnostics.
144
+ def inspect
145
+ target.inspect
146
+ end
147
+
135
148
  # Returns the class of the new constant.
136
149
  #
137
150
  # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
@@ -146,8 +159,14 @@ module ActiveSupport
146
159
  ActiveSupport::Inflector.constantize(@new_const.to_s)
147
160
  end
148
161
 
149
- def warn(callstack, called, args)
150
- @deprecator.warn(@message, callstack)
162
+ def const_missing(name)
163
+ @deprecator.warn(@message, caller_locations)
164
+ target.const_get(name)
165
+ end
166
+
167
+ def method_missing(called, *args, &block)
168
+ @deprecator.warn(@message, caller_locations)
169
+ target.__send__(called, *args, &block)
151
170
  end
152
171
  end
153
172
  end
@@ -35,7 +35,7 @@ module ActiveSupport
35
35
  # and the second is a library name.
36
36
  #
37
37
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
38
- def initialize(deprecation_horizon = "6.0", gem_name = "Rails")
38
+ def initialize(deprecation_horizon = "6.1", gem_name = "Rails")
39
39
  self.gem_name = gem_name
40
40
  self.deprecation_horizon = deprecation_horizon
41
41
  # By default, warnings are not silenced and debugging is off.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "weakref"
4
+
3
5
  module ActiveSupport
4
6
  # This module provides an internal implementation to track descendants
5
7
  # which is faster than iterating through ObjectSpace.
@@ -8,7 +10,8 @@ module ActiveSupport
8
10
 
9
11
  class << self
10
12
  def direct_descendants(klass)
11
- @@direct_descendants[klass] || []
13
+ descendants = @@direct_descendants[klass]
14
+ descendants ? descendants.to_a : []
12
15
  end
13
16
 
14
17
  def descendants(klass)
@@ -20,10 +23,10 @@ module ActiveSupport
20
23
  def clear
21
24
  if defined? ActiveSupport::Dependencies
22
25
  @@direct_descendants.each do |klass, descendants|
23
- if ActiveSupport::Dependencies.autoloaded?(klass)
26
+ if Dependencies.autoloaded?(klass)
24
27
  @@direct_descendants.delete(klass)
25
28
  else
26
- descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
29
+ descendants.reject! { |v| Dependencies.autoloaded?(v) }
27
30
  end
28
31
  end
29
32
  else
@@ -34,16 +37,19 @@ module ActiveSupport
34
37
  # This is the only method that is not thread safe, but is only ever called
35
38
  # during the eager loading phase.
36
39
  def store_inherited(klass, descendant)
37
- (@@direct_descendants[klass] ||= []) << descendant
40
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
38
41
  end
39
42
 
40
43
  private
41
- def accumulate_descendants(klass, acc)
42
- if direct_descendants = @@direct_descendants[klass]
43
- acc.concat(direct_descendants)
44
- direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
44
+
45
+ def accumulate_descendants(klass, acc)
46
+ if direct_descendants = @@direct_descendants[klass]
47
+ direct_descendants.each do |direct_descendant|
48
+ acc << direct_descendant
49
+ accumulate_descendants(direct_descendant, acc)
50
+ end
51
+ end
45
52
  end
46
- end
47
53
  end
48
54
 
49
55
  def inherited(base)
@@ -58,5 +64,46 @@ module ActiveSupport
58
64
  def descendants
59
65
  DescendantsTracker.descendants(self)
60
66
  end
67
+
68
+ # DescendantsArray is an array that contains weak references to classes.
69
+ class DescendantsArray # :nodoc:
70
+ include Enumerable
71
+
72
+ def initialize
73
+ @refs = []
74
+ end
75
+
76
+ def initialize_copy(orig)
77
+ @refs = @refs.dup
78
+ end
79
+
80
+ def <<(klass)
81
+ cleanup!
82
+ @refs << WeakRef.new(klass)
83
+ end
84
+
85
+ def each
86
+ @refs.each do |ref|
87
+ yield ref.__getobj__
88
+ rescue WeakRef::RefError
89
+ end
90
+ end
91
+
92
+ def refs_size
93
+ @refs.size
94
+ end
95
+
96
+ def cleanup!
97
+ @refs.delete_if { |ref| !ref.weakref_alive? }
98
+ end
99
+
100
+ def reject!
101
+ @refs.reject! do |ref|
102
+ yield ref.__getobj__
103
+ rescue WeakRef::RefError
104
+ true
105
+ end
106
+ end
107
+ end
61
108
  end
62
109
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "strscan"
4
- require "active_support/core_ext/regexp"
5
4
 
6
5
  module ActiveSupport
7
6
  class Duration
@@ -14,8 +13,8 @@ module ActiveSupport
14
13
  class ParsingError < ::ArgumentError; end
15
14
 
16
15
  PERIOD_OR_COMMA = /\.|,/
17
- PERIOD = ".".freeze
18
- COMMA = ",".freeze
16
+ PERIOD = "."
17
+ COMMA = ","
19
18
 
20
19
  SIGN_MARKER = /\A\-|\+|/
21
20
  DATE_MARKER = /P/
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/object/blank"
4
- require "active_support/core_ext/hash/transform_values"
5
4
 
6
5
  module ActiveSupport
7
6
  class Duration
@@ -15,14 +14,14 @@ module ActiveSupport
15
14
  # Builds and returns output string.
16
15
  def serialize
17
16
  parts, sign = normalize
18
- return "PT0S".freeze if parts.empty?
17
+ return "PT0S" if parts.empty?
19
18
 
20
- output = "P".dup
19
+ output = +"P"
21
20
  output << "#{parts[:years]}Y" if parts.key?(:years)
22
21
  output << "#{parts[:months]}M" if parts.key?(:months)
23
22
  output << "#{parts[:weeks]}W" if parts.key?(:weeks)
24
23
  output << "#{parts[:days]}D" if parts.key?(:days)
25
- time = "".dup
24
+ time = +""
26
25
  time << "#{parts[:hours]}H" if parts.key?(:hours)
27
26
  time << "#{parts[:minutes]}M" if parts.key?(:minutes)
28
27
  if parts.key?(:seconds)
@@ -4,7 +4,6 @@ require "active_support/core_ext/array/conversions"
4
4
  require "active_support/core_ext/module/delegation"
5
5
  require "active_support/core_ext/object/acts_like"
6
6
  require "active_support/core_ext/string/filters"
7
- require "active_support/deprecation"
8
7
 
9
8
  module ActiveSupport
10
9
  # Provides accurate date and time measurements using Date#advance and
@@ -183,15 +182,15 @@ module ActiveSupport
183
182
  #
184
183
  def build(value)
185
184
  parts = {}
186
- remainder = value.round(9)
185
+ remainder = value.to_f
187
186
 
188
187
  PARTS.each do |part|
189
188
  unless part == :seconds
190
189
  part_in_seconds = PARTS_IN_SECONDS[part]
191
190
  parts[part] = remainder.div(part_in_seconds)
192
- remainder %= part_in_seconds
191
+ remainder = (remainder % part_in_seconds).round(9)
193
192
  end
194
- end unless value == 0
193
+ end
195
194
 
196
195
  parts[:seconds] = remainder
197
196
 
@@ -210,12 +209,15 @@ module ActiveSupport
210
209
  def initialize(value, parts) #:nodoc:
211
210
  @value, @parts = value, parts.to_h
212
211
  @parts.default = 0
213
- @parts.reject! { |k, v| v.zero? } unless value == 0
212
+ @parts.reject! { |k, v| v.zero? }
214
213
  end
215
214
 
216
215
  def coerce(other) #:nodoc:
217
- if Scalar === other
216
+ case other
217
+ when Scalar
218
218
  [other, self]
219
+ when Duration
220
+ [Scalar.new(other.value), self]
219
221
  else
220
222
  [Scalar.new(other), self]
221
223
  end
@@ -373,7 +375,6 @@ module ActiveSupport
373
375
  return "0 seconds" if parts.empty?
374
376
 
375
377
  parts.
376
- reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
377
378
  sort_by { |unit, _ | PARTS.index(unit) }.
378
379
  map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
379
380
  to_sentence(locale: ::I18n.default_locale)
@@ -400,14 +401,8 @@ module ActiveSupport
400
401
  private
401
402
 
402
403
  def sum(sign, time = ::Time.current)
403
- unless time.acts_like?(:time) || time.acts_like?(:date)
404
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
405
- end
406
-
407
- if parts.empty?
408
- time.since(sign * value)
409
- else
410
- parts.inject(time) do |t, (type, number)|
404
+ parts.inject(time) do |t, (type, number)|
405
+ if t.acts_like?(:time) || t.acts_like?(:date)
411
406
  if type == :seconds
412
407
  t.since(sign * number)
413
408
  elsif type == :minutes
@@ -417,6 +412,8 @@ module ActiveSupport
417
412
  else
418
413
  t.advance(type => sign * number)
419
414
  end
415
+ else
416
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
420
417
  end
421
418
  end
422
419
  end
@@ -38,10 +38,6 @@ module ActiveSupport
38
38
  @options ||= ActiveSupport::InheritableOptions.new(config)
39
39
  end
40
40
 
41
- def serialize(config)
42
- config.present? ? YAML.dump(config) : ""
43
- end
44
-
45
41
  def deserialize(config)
46
42
  YAML.load(config).presence || {}
47
43
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pathname"
4
+ require "tmpdir"
4
5
  require "active_support/message_encryptor"
5
6
 
6
7
  module ActiveSupport
@@ -67,7 +68,7 @@ module ActiveSupport
67
68
 
68
69
  write(updated_contents) if updated_contents != contents
69
70
  ensure
70
- FileUtils.rm(tmp_path) if tmp_path.exist?
71
+ FileUtils.rm(tmp_path) if tmp_path&.exist?
71
72
  end
72
73
 
73
74
 
@@ -52,16 +52,17 @@ module ActiveSupport
52
52
  @pid = Process.pid
53
53
  @boot_mutex = Mutex.new
54
54
 
55
- if (@dtw = directories_to_watch).any?
55
+ dtw = directories_to_watch
56
+ @dtw, @missing = dtw.partition(&:exist?)
57
+
58
+ if @dtw.any?
56
59
  # Loading listen triggers warnings. These are originated by a legit
57
60
  # usage of attr_* macros for private attributes, but adds a lot of noise
58
61
  # to our test suite. Thus, we lazy load it and disable warnings locally.
59
62
  silence_warnings do
60
- begin
61
- require "listen"
62
- rescue LoadError => e
63
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
64
- end
63
+ require "listen"
64
+ rescue LoadError => e
65
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
65
66
  end
66
67
  end
67
68
  boot!
@@ -75,6 +76,19 @@ module ActiveSupport
75
76
  @updated.make_true
76
77
  end
77
78
  end
79
+
80
+ if @missing.any?(&:exist?)
81
+ @boot_mutex.synchronize do
82
+ appeared, @missing = @missing.partition(&:exist?)
83
+ shutdown!
84
+
85
+ @dtw += appeared
86
+ boot!
87
+
88
+ @updated.make_true
89
+ end
90
+ end
91
+
78
92
  @updated.true?
79
93
  end
80
94
 
@@ -93,7 +107,21 @@ module ActiveSupport
93
107
 
94
108
  private
95
109
  def boot!
96
- Listen.to(*@dtw, &method(:changed)).start
110
+ normalize_dirs!
111
+
112
+ unless @dtw.empty?
113
+ Listen.to(*@dtw, &method(:changed)).start
114
+ end
115
+ end
116
+
117
+ def shutdown!
118
+ Listen.stop
119
+ end
120
+
121
+ def normalize_dirs!
122
+ @dirs.transform_keys! do |dir|
123
+ dir.exist? ? dir.realpath : dir
124
+ end
97
125
  end
98
126
 
99
127
  def changed(modified, added, removed)
@@ -113,7 +141,9 @@ module ActiveSupport
113
141
  ext = @ph.normalize_extension(file.extname)
114
142
 
115
143
  file.dirname.ascend do |dir|
116
- if @dirs.fetch(dir, []).include?(ext)
144
+ matching = @dirs[dir]
145
+
146
+ if matching && (matching.empty? || matching.include?(ext))
117
147
  break true
118
148
  elsif dir == @lcsp || dir.root?
119
149
  break false
@@ -123,7 +153,7 @@ module ActiveSupport
123
153
  end
124
154
 
125
155
  def directories_to_watch
126
- dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
156
+ dtw = @files.map(&:dirname) + @dirs.keys
127
157
  dtw.compact!
128
158
  dtw.uniq!
129
159