activesupport 5.2.6 → 6.0.0.beta1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -544
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support.rb +1 -1
  6. data/lib/active_support/backtrace_cleaner.rb +23 -0
  7. data/lib/active_support/cache.rb +40 -18
  8. data/lib/active_support/cache/file_store.rb +19 -12
  9. data/lib/active_support/cache/mem_cache_store.rb +16 -2
  10. data/lib/active_support/cache/memory_store.rb +5 -0
  11. data/lib/active_support/cache/null_store.rb +5 -0
  12. data/lib/active_support/cache/redis_cache_store.rb +39 -20
  13. data/lib/active_support/callbacks.rb +16 -5
  14. data/lib/active_support/configurable.rb +4 -8
  15. data/lib/active_support/core_ext/array.rb +1 -1
  16. data/lib/active_support/core_ext/array/extract.rb +21 -0
  17. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  18. data/lib/active_support/core_ext/class/attribute.rb +1 -1
  19. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  20. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  21. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -17
  22. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  23. data/lib/active_support/core_ext/enumerable.rb +71 -67
  24. data/lib/active_support/core_ext/hash.rb +0 -2
  25. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  26. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  27. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  28. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  29. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  30. data/lib/active_support/core_ext/load_error.rb +1 -1
  31. data/lib/active_support/core_ext/module.rb +0 -1
  32. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -5
  33. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -14
  34. data/lib/active_support/core_ext/module/delegation.rb +27 -7
  35. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  36. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  37. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  38. data/lib/active_support/core_ext/numeric.rb +0 -1
  39. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  40. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  41. data/lib/active_support/core_ext/object/blank.rb +1 -2
  42. data/lib/active_support/core_ext/object/duplicable.rb +5 -2
  43. data/lib/active_support/core_ext/object/json.rb +1 -0
  44. data/lib/active_support/core_ext/object/try.rb +15 -7
  45. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  46. data/lib/active_support/core_ext/range/compare_range.rb +1 -1
  47. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  48. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  49. data/lib/active_support/core_ext/regexp.rb +0 -4
  50. data/lib/active_support/core_ext/securerandom.rb +23 -3
  51. data/lib/active_support/core_ext/string/access.rb +8 -0
  52. data/lib/active_support/core_ext/string/filters.rb +41 -0
  53. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  54. data/lib/active_support/core_ext/string/output_safety.rb +16 -5
  55. data/lib/active_support/core_ext/string/strip.rb +3 -1
  56. data/lib/active_support/core_ext/uri.rb +1 -0
  57. data/lib/active_support/current_attributes.rb +2 -0
  58. data/lib/active_support/dependencies.rb +28 -11
  59. data/lib/active_support/deprecation.rb +1 -1
  60. data/lib/active_support/deprecation/behaviors.rb +1 -1
  61. data/lib/active_support/deprecation/method_wrappers.rb +4 -5
  62. data/lib/active_support/deprecation/proxy_wrappers.rb +0 -2
  63. data/lib/active_support/descendants_tracker.rb +6 -5
  64. data/lib/active_support/duration.rb +12 -14
  65. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  66. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  67. data/lib/active_support/encrypted_configuration.rb +0 -4
  68. data/lib/active_support/evented_file_update_checker.rb +25 -7
  69. data/lib/active_support/execution_wrapper.rb +1 -0
  70. data/lib/active_support/gem_version.rb +4 -4
  71. data/lib/active_support/hash_with_indifferent_access.rb +16 -28
  72. data/lib/active_support/i18n.rb +1 -0
  73. data/lib/active_support/i18n_railtie.rb +8 -1
  74. data/lib/active_support/inflector/inflections.rb +1 -4
  75. data/lib/active_support/inflector/methods.rb +15 -27
  76. data/lib/active_support/inflector/transliterate.rb +6 -6
  77. data/lib/active_support/json/decoding.rb +23 -23
  78. data/lib/active_support/json/encoding.rb +6 -2
  79. data/lib/active_support/key_generator.rb +0 -32
  80. data/lib/active_support/lazy_load_hooks.rb +5 -1
  81. data/lib/active_support/locale/en.rb +31 -0
  82. data/lib/active_support/log_subscriber.rb +31 -8
  83. data/lib/active_support/logger.rb +0 -15
  84. data/lib/active_support/logger_silence.rb +28 -12
  85. data/lib/active_support/logger_thread_safe_level.rb +27 -6
  86. data/lib/active_support/message_encryptor.rb +2 -4
  87. data/lib/active_support/message_verifier.rb +2 -2
  88. data/lib/active_support/multibyte/chars.rb +29 -48
  89. data/lib/active_support/multibyte/unicode.rb +44 -281
  90. data/lib/active_support/notifications.rb +32 -4
  91. data/lib/active_support/notifications/fanout.rb +42 -4
  92. data/lib/active_support/notifications/instrumenter.rb +73 -2
  93. data/lib/active_support/number_helper.rb +7 -0
  94. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  95. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  96. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  97. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  98. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  99. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  100. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  101. data/lib/active_support/ordered_options.rb +1 -1
  102. data/lib/active_support/parameter_filter.rb +124 -0
  103. data/lib/active_support/rails.rb +0 -6
  104. data/lib/active_support/reloader.rb +4 -5
  105. data/lib/active_support/subscriber.rb +16 -26
  106. data/lib/active_support/tagged_logging.rb +13 -4
  107. data/lib/active_support/test_case.rb +91 -0
  108. data/lib/active_support/testing/assertions.rb +15 -1
  109. data/lib/active_support/testing/deprecation.rb +0 -1
  110. data/lib/active_support/testing/file_fixtures.rb +2 -0
  111. data/lib/active_support/testing/isolation.rb +2 -2
  112. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  113. data/lib/active_support/testing/parallelization.rb +109 -0
  114. data/lib/active_support/testing/stream.rb +1 -1
  115. data/lib/active_support/testing/time_helpers.rb +7 -7
  116. data/lib/active_support/time_with_zone.rb +15 -5
  117. data/lib/active_support/values/time_zone.rb +12 -7
  118. data/lib/active_support/xml_mini.rb +2 -9
  119. data/lib/active_support/xml_mini/jdom.rb +2 -2
  120. data/lib/active_support/xml_mini/libxml.rb +2 -2
  121. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  122. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  123. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  124. data/lib/active_support/xml_mini/rexml.rb +2 -2
  125. metadata +12 -10
  126. data/lib/active_support/core_ext/digest.rb +0 -3
  127. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,28 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3
4
- class Numeric
5
- # Returns true if the number is positive.
6
- #
7
- # 1.positive? # => true
8
- # 0.positive? # => false
9
- # -1.positive? # => false
10
- def positive?
11
- self > 0
12
- end
3
+ require "active_support/deprecation"
13
4
 
14
- # Returns true if the number is negative.
15
- #
16
- # -1.negative? # => true
17
- # 0.negative? # => false
18
- # 1.negative? # => false
19
- def negative?
20
- self < 0
21
- end
22
- end
23
-
24
- class Complex
25
- undef :positive?
26
- undef :negative?
27
- end
28
- end
5
+ ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Numeric#positive? and Numeric#negative? natively, so requiring active_support/core_ext/numeric/inquiry is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/regexp"
4
3
  require "concurrent/map"
5
4
 
6
5
  class Object
7
6
  # An object is blank if it's false, empty, or a whitespace string.
8
- # For example, +false+, '', ' ', +nil+, [], and {} are all blank.
7
+ # For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
9
8
  #
10
9
  # This simplifies
11
10
  #
@@ -75,8 +75,11 @@ end
75
75
 
76
76
  class Symbol
77
77
  begin
78
- :symbol.dup # Ruby 2.4.x.
79
- "symbol_from_string".to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
78
+ :symbol.dup
79
+
80
+ # Some symbols couldn't be duped in Ruby 2.4.0 only, due to a bug.
81
+ # This feature check catches any regression.
82
+ "symbol_from_string".to_sym.dup
80
83
  rescue TypeError
81
84
 
82
85
  # Symbols are not duplicable:
@@ -14,6 +14,7 @@ require "active_support/core_ext/time/conversions"
14
14
  require "active_support/core_ext/date_time/conversions"
15
15
  require "active_support/core_ext/date/conversions"
16
16
 
17
+ #--
17
18
  # The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
18
19
  # their default behavior. That said, we need to define the basic to_json method in all of them,
19
20
  # otherwise they will always use to_json gem implementation, which is backwards incompatible in
@@ -4,19 +4,27 @@ require "delegate"
4
4
 
5
5
  module ActiveSupport
6
6
  module Tryable #:nodoc:
7
- def try(*a, &b)
8
- try!(*a, &b) if a.empty? || respond_to?(a.first)
7
+ def try(method_name = nil, *args, &b)
8
+ if method_name.nil? && block_given?
9
+ if b.arity == 0
10
+ instance_eval(&b)
11
+ else
12
+ yield self
13
+ end
14
+ elsif respond_to?(method_name)
15
+ public_send(method_name, *args, &b)
16
+ end
9
17
  end
10
18
 
11
- def try!(*a, &b)
12
- if a.empty? && block_given?
19
+ def try!(method_name = nil, *args, &b)
20
+ if method_name.nil? && block_given?
13
21
  if b.arity == 0
14
22
  instance_eval(&b)
15
23
  else
16
24
  yield self
17
25
  end
18
26
  else
19
- public_send(*a, &b)
27
+ public_send(method_name, *args, &b)
20
28
  end
21
29
  end
22
30
  end
@@ -135,14 +143,14 @@ class NilClass
135
143
  #
136
144
  # With +try+
137
145
  # @person.try(:children).try(:first).try(:name)
138
- def try(*args)
146
+ def try(method_name = nil, *args)
139
147
  nil
140
148
  end
141
149
 
142
150
  # Calling +try!+ on +nil+ always returns +nil+.
143
151
  #
144
152
  # nil.try!(:name) # => nil
145
- def try!(*args)
153
+ def try!(method_name = nil, *args)
146
154
  nil
147
155
  end
148
156
  end
@@ -68,7 +68,7 @@ class Object
68
68
  # You can access these methods using the class name instead:
69
69
  #
70
70
  # class Phone < ActiveRecord::Base
71
- # enum phone_number_type: [home: 0, office: 1, mobile: 2]
71
+ # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
72
72
  #
73
73
  # with_options presence: true do
74
74
  # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- module CompareWithRange #:nodoc:
4
+ module CompareWithRange
5
5
  # Extends the default Range#=== to support range comparisons.
6
6
  # (1..5) === (1..5) # => true
7
7
  # (1..5) === (2..3) # => true
@@ -1,39 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActiveSupport::RangeWithFormat
4
- RANGE_FORMATS = {
5
- db: -> (start, stop) do
6
- case start
7
- when String then "BETWEEN '#{start}' AND '#{stop}'"
3
+ module ActiveSupport
4
+ module RangeWithFormat
5
+ RANGE_FORMATS = {
6
+ db: -> (start, stop) do
7
+ case start
8
+ when String then "BETWEEN '#{start}' AND '#{stop}'"
9
+ else
10
+ "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
11
+ end
12
+ end
13
+ }
14
+
15
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
16
+ #
17
+ # range = (1..100) # => 1..100
18
+ #
19
+ # range.to_s # => "1..100"
20
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
21
+ #
22
+ # == Adding your own range formats to to_s
23
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
24
+ # Use the format name as the hash key and a Proc instance.
25
+ #
26
+ # # config/initializers/range_formats.rb
27
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
28
+ def to_s(format = :default)
29
+ if formatter = RANGE_FORMATS[format]
30
+ formatter.call(first, last)
8
31
  else
9
- "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
32
+ super()
10
33
  end
11
34
  end
12
- }
13
35
 
14
- # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
15
- #
16
- # range = (1..100) # => 1..100
17
- #
18
- # range.to_s # => "1..100"
19
- # range.to_s(:db) # => "BETWEEN '1' AND '100'"
20
- #
21
- # == Adding your own range formats to to_s
22
- # You can add your own formats to the Range::RANGE_FORMATS hash.
23
- # Use the format name as the hash key and a Proc instance.
24
- #
25
- # # config/initializers/range_formats.rb
26
- # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
27
- def to_s(format = :default)
28
- if formatter = RANGE_FORMATS[format]
29
- formatter.call(first, last)
30
- else
31
- super()
32
- end
36
+ alias_method :to_default_s, :to_s
37
+ alias_method :to_formatted_s, :to_s
33
38
  end
34
-
35
- alias_method :to_default_s, :to_s
36
- alias_method :to_formatted_s, :to_s
37
39
  end
38
40
 
39
41
  Range.prepend(ActiveSupport::RangeWithFormat)
@@ -1,3 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
5
+ ActiveSupport::Deprecation.warn "You have required `active_support/core_ext/range/include_range`. " \
6
+ "This file will be removed in Rails 6.1. You should require `active_support/core_ext/range/compare_range` " \
7
+ "instead."
8
+
3
9
  require "active_support/core_ext/range/compare_range"
@@ -4,8 +4,4 @@ class Regexp #:nodoc:
4
4
  def multiline?
5
5
  options & MULTILINE == MULTILINE
6
6
  end
7
-
8
- def match?(string, pos = 0)
9
- !!match(string, pos)
10
- end unless //.respond_to?(:match?)
11
7
  end
@@ -4,17 +4,18 @@ require "securerandom"
4
4
 
5
5
  module SecureRandom
6
6
  BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
7
+ BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
8
+
7
9
  # SecureRandom.base58 generates a random base58 string.
8
10
  #
9
- # The argument _n_ specifies the length, of the random string to be generated.
11
+ # The argument _n_ specifies the length of the random string to be generated.
10
12
  #
11
13
  # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
12
14
  #
13
- # The result may contain alphanumeric characters except 0, O, I and l
15
+ # The result may contain alphanumeric characters except 0, O, I and l.
14
16
  #
15
17
  # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
16
18
  # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
17
- #
18
19
  def self.base58(n = 16)
19
20
  SecureRandom.random_bytes(n).unpack("C*").map do |byte|
20
21
  idx = byte % 64
@@ -22,4 +23,23 @@ module SecureRandom
22
23
  BASE58_ALPHABET[idx]
23
24
  end.join
24
25
  end
26
+
27
+ # SecureRandom.base36 generates a random base36 string in lowercase.
28
+ #
29
+ # The argument _n_ specifies the length of the random string to be generated.
30
+ #
31
+ # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
32
+ # This method can be used over +base58+ if a deterministic case key is necessary.
33
+ #
34
+ # The result will contain alphanumeric characters in lowercase.
35
+ #
36
+ # p SecureRandom.base36 # => "4kugl2pdqmscqtje"
37
+ # p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
38
+ def self.base36(n = 16)
39
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
40
+ idx = byte % 64
41
+ idx = SecureRandom.random_number(36) if idx >= 36
42
+ BASE36_ALPHABET[idx]
43
+ end.join
44
+ end
25
45
  end
@@ -75,6 +75,10 @@ class String
75
75
  # str.first(0) # => ""
76
76
  # str.first(6) # => "hello"
77
77
  def first(limit = 1)
78
+ ActiveSupport::Deprecation.warn(
79
+ "Calling String#first with a negative integer limit " \
80
+ "will raise an ArgumentError in Rails 6.1."
81
+ ) if limit < 0
78
82
  if limit == 0
79
83
  ""
80
84
  elsif limit >= size
@@ -95,6 +99,10 @@ class String
95
99
  # str.last(0) # => ""
96
100
  # str.last(6) # => "hello"
97
101
  def last(limit = 1)
102
+ ActiveSupport::Deprecation.warn(
103
+ "Calling String#last with a negative integer limit " \
104
+ "will raise an ArgumentError in Rails 6.1."
105
+ ) if limit < 0
98
106
  if limit == 0
99
107
  ""
100
108
  elsif limit >= size
@@ -78,6 +78,47 @@ class String
78
78
  "#{self[0, stop]}#{omission}"
79
79
  end
80
80
 
81
+ # Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
82
+ # breaking string encoding by splitting multibyte characters or breaking
83
+ # grapheme clusters ("perceptual characters") by truncating at combining
84
+ # characters.
85
+ #
86
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
87
+ # => 20
88
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
89
+ # => 80
90
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
91
+ # => "🔪🔪🔪🔪…"
92
+ #
93
+ # The truncated text ends with the <tt>:omission</tt> string, defaulting
94
+ # to "…", for a total length not exceeding <tt>bytesize</tt>.
95
+ def truncate_bytes(truncate_at, omission: "…")
96
+ omission ||= ""
97
+
98
+ case
99
+ when bytesize <= truncate_at
100
+ dup
101
+ when omission.bytesize > truncate_at
102
+ raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_at} bytes"
103
+ when omission.bytesize == truncate_at
104
+ omission.dup
105
+ else
106
+ self.class.new.tap do |cut|
107
+ cut_at = truncate_at - omission.bytesize
108
+
109
+ scan(/\X/) do |grapheme|
110
+ if cut.bytesize + grapheme.bytesize <= cut_at
111
+ cut << grapheme
112
+ else
113
+ break
114
+ end
115
+ end
116
+
117
+ cut << omission
118
+ end
119
+ end
120
+ end
121
+
81
122
  # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
82
123
  #
83
124
  # 'Once upon a time in a world far far away'.truncate_words(4)
@@ -11,12 +11,13 @@ class String
11
11
  # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
12
12
  # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
13
13
  #
14
- # >> "lj".upcase
15
- # => "lj"
16
14
  # >> "lj".mb_chars.upcase.to_s
17
15
  # => "LJ"
18
16
  #
19
- # NOTE: An above example is useful for pre Ruby 2.4. Ruby 2.4 supports Unicode case mappings.
17
+ # NOTE: Ruby 2.4 and later support native Unicode case mappings:
18
+ #
19
+ # >> "lj".upcase
20
+ # => "LJ"
20
21
  #
21
22
  # == Method chaining
22
23
  #
@@ -134,8 +134,9 @@ end
134
134
  module ActiveSupport #:nodoc:
135
135
  class SafeBuffer < String
136
136
  UNSAFE_STRING_METHODS = %w(
137
- capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
138
- slice squeeze strip sub succ swapcase tr tr_s upcase
137
+ capitalize chomp chop delete delete_prefix delete_suffix
138
+ downcase gsub lstrip next reverse rstrip slice squeeze strip
139
+ sub succ swapcase tr tr_s unicode_normalize upcase
139
140
  )
140
141
 
141
142
  alias_method :original_concat, :concat
@@ -149,9 +150,7 @@ module ActiveSupport #:nodoc:
149
150
  end
150
151
 
151
152
  def [](*args)
152
- if args.size < 2
153
- super
154
- elsif html_safe?
153
+ if html_safe?
155
154
  new_safe_buffer = super
156
155
 
157
156
  if new_safe_buffer
@@ -188,10 +187,22 @@ module ActiveSupport #:nodoc:
188
187
  end
189
188
  alias << concat
190
189
 
190
+ def insert(index, value)
191
+ super(index, html_escape_interpolated_argument(value))
192
+ end
193
+
191
194
  def prepend(value)
192
195
  super(html_escape_interpolated_argument(value))
193
196
  end
194
197
 
198
+ def replace(value)
199
+ super(html_escape_interpolated_argument(value))
200
+ end
201
+
202
+ def []=(index, value)
203
+ super(index, html_escape_interpolated_argument(value))
204
+ end
205
+
195
206
  def +(other)
196
207
  dup.concat(other)
197
208
  end
@@ -20,6 +20,8 @@ class String
20
20
  # Technically, it looks for the least indented non-empty line
21
21
  # in the whole string, and removes that amount of leading whitespace.
22
22
  def strip_heredoc
23
- gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
23
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
24
+ stripped.freeze if frozen?
25
+ end
24
26
  end
25
27
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "uri"
4
+
4
5
  if RUBY_VERSION < "2.6.0"
5
6
  require "active_support/core_ext/module/redefine_method"
6
7
  URI::Parser.class_eval do
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/callbacks"
4
+
3
5
  module ActiveSupport
4
6
  # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
5
7
  # before and after each request. This allows you to keep all the per-request attributes easily
@@ -79,6 +79,12 @@ module ActiveSupport #:nodoc:
79
79
  # to allow arbitrary constants to be marked for unloading.
80
80
  mattr_accessor :explicitly_unloadable_constants, default: []
81
81
 
82
+ # The logger used when tracing autoloads.
83
+ mattr_accessor :logger
84
+
85
+ # If true, trace autoloads with +logger.debug+.
86
+ mattr_accessor :verbose, default: false
87
+
82
88
  # The WatchStack keeps a stack of the modules being watched as files are
83
89
  # loaded. If a file in the process of being loaded (parent.rb) triggers the
84
90
  # load of another file (child.rb) the stack will ensure that child.rb
@@ -140,7 +146,7 @@ module ActiveSupport #:nodoc:
140
146
 
141
147
  # Normalize the list of new constants, and add them to the list we will return
142
148
  new_constants.each do |suffix|
143
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
149
+ constants << ([namespace, suffix] - ["Object"]).join("::")
144
150
  end
145
151
  end
146
152
  constants
@@ -349,7 +355,7 @@ module ActiveSupport #:nodoc:
349
355
  end
350
356
 
351
357
  def require_or_load(file_name, const_path = nil)
352
- file_name = $` if file_name =~ /\.rb\z/
358
+ file_name = file_name.chomp(".rb")
353
359
  expanded = File.expand_path(file_name)
354
360
  return if loaded.include?(expanded)
355
361
 
@@ -399,7 +405,7 @@ module ActiveSupport #:nodoc:
399
405
  # constant paths which would cause Dependencies to attempt to load this
400
406
  # file.
401
407
  def loadable_constants_for_path(path, bases = autoload_paths)
402
- path = $` if path =~ /\.rb\z/
408
+ path = path.chomp(".rb")
403
409
  expanded_path = File.expand_path(path)
404
410
  paths = []
405
411
 
@@ -408,7 +414,7 @@ module ActiveSupport #:nodoc:
408
414
  next unless expanded_path.start_with?(expanded_root)
409
415
 
410
416
  root_size = expanded_root.size
411
- next if expanded_path[root_size] != ?/.freeze
417
+ next if expanded_path[root_size] != ?/
412
418
 
413
419
  nesting = expanded_path[(root_size + 1)..-1]
414
420
  paths << nesting.camelize unless nesting.blank?
@@ -420,7 +426,7 @@ module ActiveSupport #:nodoc:
420
426
 
421
427
  # Search for a file in autoload_paths matching the provided suffix.
422
428
  def search_for_file(path_suffix)
423
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
429
+ path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
424
430
 
425
431
  autoload_paths.each do |root|
426
432
  path = File.join(root, path_suffix)
@@ -454,6 +460,7 @@ module ActiveSupport #:nodoc:
454
460
  return nil unless base_path = autoloadable_module?(path_suffix)
455
461
  mod = Module.new
456
462
  into.const_set const_name, mod
463
+ log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
457
464
  autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
458
465
  autoloaded_constants.uniq!
459
466
  mod
@@ -495,26 +502,31 @@ module ActiveSupport #:nodoc:
495
502
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
496
503
  end
497
504
 
498
- qualified_name = qualified_name_for from_mod, const_name
505
+ qualified_name = qualified_name_for(from_mod, const_name)
499
506
  path_suffix = qualified_name.underscore
500
507
 
501
508
  file_path = search_for_file(path_suffix)
502
509
 
503
510
  if file_path
504
511
  expanded = File.expand_path(file_path)
505
- expanded.sub!(/\.rb\z/, "".freeze)
512
+ expanded.sub!(/\.rb\z/, "")
506
513
 
507
514
  if loading.include?(expanded)
508
515
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
509
516
  else
510
517
  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)
518
+
519
+ if from_mod.const_defined?(const_name, false)
520
+ log("constant #{qualified_name} autoloaded from #{expanded}.rb")
521
+ return from_mod.const_get(const_name)
522
+ else
523
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
524
+ end
513
525
  end
514
526
  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
515
527
  return mod
516
- elsif (parent = from_mod.parent) && parent != from_mod &&
517
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
528
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
529
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
518
530
  # If our parents do not have a constant named +const_name+ then we are free
519
531
  # to attempt to load upwards. If they do have such a constant, then this
520
532
  # const_missing must be due to from_mod::const_name, which should not
@@ -558,6 +570,7 @@ module ActiveSupport #:nodoc:
558
570
  # as the environment will be in an inconsistent state, e.g. other constants
559
571
  # may have already been unloaded and not accessible.
560
572
  def remove_unloadable_constants!
573
+ log("removing unloadable constants")
561
574
  autoloaded_constants.each { |const| remove_constant const }
562
575
  autoloaded_constants.clear
563
576
  Reference.clear!
@@ -747,6 +760,10 @@ module ActiveSupport #:nodoc:
747
760
  # The constant is no longer reachable, just skip it.
748
761
  end
749
762
  end
763
+
764
+ def log(message)
765
+ logger.debug("autoloading: #{message}") if logger && verbose
766
+ end
750
767
  end
751
768
  end
752
769