activesupport 5.2.8.1 → 6.0.6

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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +467 -410
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +27 -1
  7. data/lib/active_support/cache/file_store.rb +32 -32
  8. data/lib/active_support/cache/mem_cache_store.rb +12 -7
  9. data/lib/active_support/cache/memory_store.rb +15 -9
  10. data/lib/active_support/cache/null_store.rb +8 -3
  11. data/lib/active_support/cache/redis_cache_store.rb +47 -20
  12. data/lib/active_support/cache/strategy/local_cache.rb +22 -22
  13. data/lib/active_support/cache.rb +71 -48
  14. data/lib/active_support/callbacks.rb +16 -8
  15. data/lib/active_support/concern.rb +24 -1
  16. data/lib/active_support/concurrency/share_lock.rb +0 -1
  17. data/lib/active_support/configurable.rb +7 -11
  18. data/lib/active_support/core_ext/array/access.rb +18 -6
  19. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  20. data/lib/active_support/core_ext/array/extract.rb +21 -0
  21. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  22. data/lib/active_support/core_ext/array.rb +1 -1
  23. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  24. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  25. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  26. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  27. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  28. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  29. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  30. data/lib/active_support/core_ext/enumerable.rb +97 -73
  31. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  32. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  33. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  34. data/lib/active_support/core_ext/hash/except.rb +2 -2
  35. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  36. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  37. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  38. data/lib/active_support/core_ext/hash.rb +1 -2
  39. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  40. data/lib/active_support/core_ext/kernel.rb +0 -1
  41. data/lib/active_support/core_ext/load_error.rb +1 -1
  42. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  43. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  44. data/lib/active_support/core_ext/module/delegation.rb +41 -8
  45. data/lib/active_support/core_ext/module/introspection.rb +38 -13
  46. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  47. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  48. data/lib/active_support/core_ext/module.rb +0 -1
  49. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  50. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  51. data/lib/active_support/core_ext/numeric.rb +0 -1
  52. data/lib/active_support/core_ext/object/blank.rb +1 -2
  53. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  54. data/lib/active_support/core_ext/object/json.rb +2 -1
  55. data/lib/active_support/core_ext/object/try.rb +17 -7
  56. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  57. data/lib/active_support/core_ext/range/compare_range.rb +28 -13
  58. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  59. data/lib/active_support/core_ext/range/each.rb +0 -1
  60. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  61. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  62. data/lib/active_support/core_ext/regexp.rb +0 -4
  63. data/lib/active_support/core_ext/securerandom.rb +23 -3
  64. data/lib/active_support/core_ext/string/access.rb +8 -0
  65. data/lib/active_support/core_ext/string/filters.rb +42 -1
  66. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  67. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  68. data/lib/active_support/core_ext/string/output_safety.rb +68 -10
  69. data/lib/active_support/core_ext/string/strip.rb +3 -1
  70. data/lib/active_support/core_ext/time/calculations.rb +34 -3
  71. data/lib/active_support/core_ext/uri.rb +1 -0
  72. data/lib/active_support/current_attributes.rb +8 -0
  73. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  74. data/lib/active_support/dependencies.rb +74 -18
  75. data/lib/active_support/deprecation/behaviors.rb +1 -1
  76. data/lib/active_support/deprecation/method_wrappers.rb +17 -23
  77. data/lib/active_support/deprecation/proxy_wrappers.rb +28 -5
  78. data/lib/active_support/deprecation.rb +1 -1
  79. data/lib/active_support/descendants_tracker.rb +55 -9
  80. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  81. data/lib/active_support/duration/iso8601_serializer.rb +3 -5
  82. data/lib/active_support/duration.rb +7 -8
  83. data/lib/active_support/encrypted_configuration.rb +0 -4
  84. data/lib/active_support/encrypted_file.rb +3 -2
  85. data/lib/active_support/evented_file_update_checker.rb +39 -10
  86. data/lib/active_support/execution_wrapper.rb +2 -1
  87. data/lib/active_support/file_update_checker.rb +0 -1
  88. data/lib/active_support/gem_version.rb +4 -4
  89. data/lib/active_support/hash_with_indifferent_access.rb +22 -18
  90. data/lib/active_support/i18n.rb +1 -0
  91. data/lib/active_support/i18n_railtie.rb +13 -1
  92. data/lib/active_support/inflector/inflections.rb +1 -5
  93. data/lib/active_support/inflector/methods.rb +16 -29
  94. data/lib/active_support/inflector/transliterate.rb +47 -18
  95. data/lib/active_support/json/decoding.rb +23 -24
  96. data/lib/active_support/json/encoding.rb +6 -2
  97. data/lib/active_support/key_generator.rb +0 -32
  98. data/lib/active_support/lazy_load_hooks.rb +5 -2
  99. data/lib/active_support/locale/en.rb +33 -0
  100. data/lib/active_support/log_subscriber.rb +31 -9
  101. data/lib/active_support/logger.rb +1 -16
  102. data/lib/active_support/logger_silence.rb +28 -12
  103. data/lib/active_support/logger_thread_safe_level.rb +26 -4
  104. data/lib/active_support/message_encryptor.rb +4 -6
  105. data/lib/active_support/message_verifier.rb +5 -5
  106. data/lib/active_support/messages/metadata.rb +11 -2
  107. data/lib/active_support/messages/rotator.rb +4 -4
  108. data/lib/active_support/multibyte/chars.rb +29 -49
  109. data/lib/active_support/multibyte/unicode.rb +44 -282
  110. data/lib/active_support/notifications/fanout.rb +98 -13
  111. data/lib/active_support/notifications/instrumenter.rb +80 -9
  112. data/lib/active_support/notifications.rb +41 -4
  113. data/lib/active_support/number_helper/number_converter.rb +4 -5
  114. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  115. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  116. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -2
  117. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -2
  118. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  119. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  120. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -4
  121. data/lib/active_support/number_helper/rounding_helper.rb +1 -1
  122. data/lib/active_support/number_helper.rb +11 -0
  123. data/lib/active_support/option_merger.rb +21 -3
  124. data/lib/active_support/ordered_hash.rb +1 -1
  125. data/lib/active_support/ordered_options.rb +5 -1
  126. data/lib/active_support/parameter_filter.rb +128 -0
  127. data/lib/active_support/rails.rb +0 -6
  128. data/lib/active_support/reloader.rb +4 -5
  129. data/lib/active_support/security_utils.rb +1 -1
  130. data/lib/active_support/string_inquirer.rb +0 -1
  131. data/lib/active_support/subscriber.rb +65 -26
  132. data/lib/active_support/tagged_logging.rb +13 -4
  133. data/lib/active_support/test_case.rb +91 -0
  134. data/lib/active_support/testing/assertions.rb +15 -1
  135. data/lib/active_support/testing/deprecation.rb +0 -1
  136. data/lib/active_support/testing/file_fixtures.rb +2 -0
  137. data/lib/active_support/testing/isolation.rb +2 -2
  138. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  139. data/lib/active_support/testing/parallelization.rb +134 -0
  140. data/lib/active_support/testing/stream.rb +1 -2
  141. data/lib/active_support/testing/time_helpers.rb +7 -9
  142. data/lib/active_support/time_with_zone.rb +15 -5
  143. data/lib/active_support/values/time_zone.rb +12 -7
  144. data/lib/active_support/xml_mini/jdom.rb +2 -3
  145. data/lib/active_support/xml_mini/libxml.rb +2 -2
  146. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  147. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  148. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  149. data/lib/active_support/xml_mini/rexml.rb +2 -2
  150. data/lib/active_support/xml_mini.rb +2 -10
  151. data/lib/active_support.rb +2 -1
  152. metadata +37 -8
  153. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  154. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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)
@@ -15,7 +15,6 @@ module ActiveSupport
15
15
  end
16
16
 
17
17
  private
18
-
19
18
  def ensure_iteration_allowed
20
19
  raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
21
20
  end
@@ -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"
@@ -9,9 +9,9 @@ module ActiveSupport
9
9
  # (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
10
10
  #
11
11
  def include?(value)
12
- if first.is_a?(TimeWithZone)
12
+ if self.begin.is_a?(TimeWithZone)
13
13
  cover?(value)
14
- elsif last.is_a?(TimeWithZone)
14
+ elsif self.end.is_a?(TimeWithZone)
15
15
  cover?(value)
16
16
  else
17
17
  super
@@ -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
@@ -75,7 +75,48 @@ class String
75
75
  length_with_room_for_omission
76
76
  end
77
77
 
78
- "#{self[0, stop]}#{omission}"
78
+ +"#{self[0, stop]}#{omission}"
79
+ end
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
79
120
  end
80
121
 
81
122
  # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
@@ -162,6 +162,11 @@ class String
162
162
 
163
163
  # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
164
164
  #
165
+ # If the optional parameter +locale+ is specified,
166
+ # the word will be parameterized as a word of that language.
167
+ # By default, this parameter is set to <tt>nil</tt> and it will use
168
+ # the configured <tt>I18n.locale</tt>.
169
+ #
165
170
  # class Person
166
171
  # def to_param
167
172
  # "#{id}-#{name.parameterize}"
@@ -187,8 +192,8 @@ class String
187
192
  #
188
193
  # <%= link_to(@person.name, person_path) %>
189
194
  # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
190
- def parameterize(separator: "-", preserve_case: false)
191
- ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
195
+ def parameterize(separator: "-", preserve_case: false, locale: nil)
196
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
192
197
  end
193
198
 
194
199
  # Creates the name of a table like Rails does for models to table names. This method
@@ -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
  #
@@ -162,10 +162,13 @@ end
162
162
  module ActiveSupport #:nodoc:
163
163
  class SafeBuffer < String
164
164
  UNSAFE_STRING_METHODS = %w(
165
- capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
166
- slice squeeze strip sub succ swapcase tr tr_s upcase
165
+ capitalize chomp chop delete delete_prefix delete_suffix
166
+ downcase lstrip next reverse rstrip scrub slice squeeze strip
167
+ succ swapcase tr tr_s unicode_normalize upcase
167
168
  )
168
169
 
170
+ UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
171
+
169
172
  alias_method :original_concat, :concat
170
173
  private :original_concat
171
174
 
@@ -177,15 +180,13 @@ module ActiveSupport #:nodoc:
177
180
  end
178
181
 
179
182
  def [](*args)
180
- if args.size < 2
181
- super
182
- elsif html_safe?
183
- new_safe_buffer = super
183
+ if html_safe?
184
+ new_string = super
184
185
 
185
- if new_safe_buffer
186
- new_safe_buffer.instance_variable_set :@html_safe, true
187
- end
186
+ return unless new_string
188
187
 
188
+ new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
189
+ new_safe_buffer.instance_variable_set :@html_safe, true
189
190
  new_safe_buffer
190
191
  else
191
192
  to_str[*args]
@@ -216,14 +217,37 @@ module ActiveSupport #:nodoc:
216
217
  end
217
218
  alias << concat
218
219
 
220
+ def insert(index, value)
221
+ super(index, html_escape_interpolated_argument(value))
222
+ end
223
+
219
224
  def prepend(value)
220
225
  super(html_escape_interpolated_argument(value))
221
226
  end
222
227
 
228
+ def replace(value)
229
+ super(html_escape_interpolated_argument(value))
230
+ end
231
+
232
+ def []=(*args)
233
+ if args.count == 3
234
+ super(args[0], args[1], html_escape_interpolated_argument(args[2]))
235
+ else
236
+ super(args[0], html_escape_interpolated_argument(args[1]))
237
+ end
238
+ end
239
+
223
240
  def +(other)
224
241
  dup.concat(other)
225
242
  end
226
243
 
244
+ def *(*)
245
+ new_string = super
246
+ new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
247
+ new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
248
+ new_safe_buffer
249
+ end
250
+
227
251
  def %(args)
228
252
  case args
229
253
  when Hash
@@ -266,11 +290,45 @@ module ActiveSupport #:nodoc:
266
290
  end
267
291
  end
268
292
 
269
- private
293
+ UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
294
+ if unsafe_method.respond_to?(unsafe_method)
295
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
296
+ def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
297
+ if block # if block
298
+ to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
299
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
300
+ block.call(*params) # block.call(*params)
301
+ } # }
302
+ else # else
303
+ to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
304
+ end # end
305
+ end # end
306
+
307
+ def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
308
+ @html_safe = false # @html_safe = false
309
+ if block # if block
310
+ super(*args) { |*params| # super(*args) { |*params|
311
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
312
+ block.call(*params) # block.call(*params)
313
+ } # }
314
+ else # else
315
+ super # super
316
+ end # end
317
+ end # end
318
+ EOT
319
+ end
320
+ end
270
321
 
322
+ private
271
323
  def html_escape_interpolated_argument(arg)
272
324
  (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
273
325
  end
326
+
327
+ def set_block_back_references(block, match_data)
328
+ block.binding.eval("proc { |m| $~ = m }").call(match_data)
329
+ rescue ArgumentError
330
+ # Can't create binding from C level Proc
331
+ end
274
332
  end
275
333
  end
276
334
 
@@ -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
@@ -47,7 +47,9 @@ class Time
47
47
  # Time.at can be called with a time or numerical value
48
48
  time_or_number = args.first
49
49
 
50
- if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
50
+ if time_or_number.is_a?(ActiveSupport::TimeWithZone)
51
+ at_without_coercion(time_or_number.to_r).getlocal
52
+ elsif time_or_number.is_a?(DateTime)
51
53
  at_without_coercion(time_or_number.to_f).getlocal
52
54
  else
53
55
  at_without_coercion(time_or_number)
@@ -170,8 +172,7 @@ class Time
170
172
  options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
171
173
  end
172
174
 
173
- d = to_date.advance(options)
174
- d = d.gregorian if d.julian?
175
+ d = to_date.gregorian.advance(options)
175
176
  time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
176
177
  seconds_to_advance = \
177
178
  options.fetch(:seconds, 0) +
@@ -312,4 +313,34 @@ class Time
312
313
  end
313
314
  alias_method :eql_without_coercion, :eql?
314
315
  alias_method :eql?, :eql_with_coercion
316
+
317
+ # Returns a new time the specified number of days ago.
318
+ def prev_day(days = 1)
319
+ advance(days: -days)
320
+ end
321
+
322
+ # Returns a new time the specified number of days in the future.
323
+ def next_day(days = 1)
324
+ advance(days: days)
325
+ end
326
+
327
+ # Returns a new time the specified number of months ago.
328
+ def prev_month(months = 1)
329
+ advance(months: -months)
330
+ end
331
+
332
+ # Returns a new time the specified number of months in the future.
333
+ def next_month(months = 1)
334
+ advance(months: months)
335
+ end
336
+
337
+ # Returns a new time the specified number of years ago.
338
+ def prev_year(years = 1)
339
+ advance(years: -years)
340
+ end
341
+
342
+ # Returns a new time the specified number of years in the future.
343
+ def next_year(years = 1)
344
+ advance(years: years)
345
+ end
315
346
  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
@@ -117,10 +119,16 @@ module ActiveSupport
117
119
  end
118
120
  end
119
121
 
122
+ # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
123
+ def before_reset(&block)
124
+ set_callback :reset, :before, &block
125
+ end
126
+
120
127
  # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
121
128
  def resets(&block)
122
129
  set_callback :reset, :after, &block
123
130
  end
131
+ alias_method :after_reset, :resets
124
132
 
125
133
  delegate :set, :reset, to: :instance
126
134
 
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "active_support/core_ext/string/inflections"
5
+
6
+ module ActiveSupport
7
+ module Dependencies
8
+ module ZeitwerkIntegration # :nodoc: all
9
+ module Decorations
10
+ def clear
11
+ Dependencies.unload_interlock do
12
+ Rails.autoloaders.main.reload
13
+ rescue Zeitwerk::ReloadingDisabledError
14
+ raise "reloading is disabled because config.cache_classes is true"
15
+ end
16
+ end
17
+
18
+ def constantize(cpath)
19
+ ActiveSupport::Inflector.constantize(cpath)
20
+ end
21
+
22
+ def safe_constantize(cpath)
23
+ ActiveSupport::Inflector.safe_constantize(cpath)
24
+ end
25
+
26
+ def autoloaded_constants
27
+ Rails.autoloaders.main.unloadable_cpaths
28
+ end
29
+
30
+ def autoloaded?(object)
31
+ cpath = object.is_a?(Module) ? real_mod_name(object) : object.to_s
32
+ Rails.autoloaders.main.unloadable_cpath?(cpath)
33
+ end
34
+
35
+ def verbose=(verbose)
36
+ l = verbose ? logger || Rails.logger : nil
37
+ Rails.autoloaders.each { |autoloader| autoloader.logger = l }
38
+ end
39
+
40
+ def unhook!
41
+ :no_op
42
+ end
43
+ end
44
+
45
+ module RequireDependency
46
+ def require_dependency(filename)
47
+ filename = filename.to_path if filename.respond_to?(:to_path)
48
+ if abspath = ActiveSupport::Dependencies.search_for_file(filename)
49
+ require abspath
50
+ else
51
+ require filename
52
+ end
53
+ end
54
+ end
55
+
56
+ module Inflector
57
+ # Concurrent::Map is not needed. This is a private class, and overrides
58
+ # must be defined while the application boots.
59
+ @overrides = {}
60
+
61
+ def self.camelize(basename, _abspath)
62
+ @overrides[basename] || basename.camelize
63
+ end
64
+
65
+ def self.inflect(overrides)
66
+ @overrides.merge!(overrides)
67
+ end
68
+ end
69
+
70
+ class << self
71
+ def take_over(enable_reloading:)
72
+ setup_autoloaders(enable_reloading)
73
+ freeze_paths
74
+ decorate_dependencies
75
+ end
76
+
77
+ private
78
+ def setup_autoloaders(enable_reloading)
79
+ Dependencies.autoload_paths.each do |autoload_path|
80
+ # Zeitwerk only accepts existing directories in `push_dir` to
81
+ # prevent misconfigurations.
82
+ next unless File.directory?(autoload_path)
83
+
84
+ autoloader = \
85
+ autoload_once?(autoload_path) ? Rails.autoloaders.once : Rails.autoloaders.main
86
+
87
+ autoloader.push_dir(autoload_path)
88
+ autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path)
89
+ end
90
+
91
+ Rails.autoloaders.main.enable_reloading if enable_reloading
92
+ Rails.autoloaders.each(&:setup)
93
+ end
94
+
95
+ def autoload_once?(autoload_path)
96
+ Dependencies.autoload_once_paths.include?(autoload_path)
97
+ end
98
+
99
+ def eager_load?(autoload_path)
100
+ Dependencies._eager_load_paths.member?(autoload_path)
101
+ end
102
+
103
+ def freeze_paths
104
+ Dependencies.autoload_paths.freeze
105
+ Dependencies.autoload_once_paths.freeze
106
+ Dependencies._eager_load_paths.freeze
107
+ end
108
+
109
+ def decorate_dependencies
110
+ Dependencies.unhook!
111
+ Dependencies.singleton_class.prepend(Decorations)
112
+ Object.prepend(RequireDependency)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end