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
@@ -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
@@ -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
  #
@@ -134,10 +134,13 @@ 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 lstrip next reverse rstrip slice squeeze strip
139
+ succ swapcase tr tr_s unicode_normalize upcase
139
140
  )
140
141
 
142
+ UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
143
+
141
144
  alias_method :original_concat, :concat
142
145
  private :original_concat
143
146
 
@@ -149,9 +152,7 @@ module ActiveSupport #:nodoc:
149
152
  end
150
153
 
151
154
  def [](*args)
152
- if args.size < 2
153
- super
154
- elsif html_safe?
155
+ if html_safe?
155
156
  new_safe_buffer = super
156
157
 
157
158
  if new_safe_buffer
@@ -188,14 +189,36 @@ module ActiveSupport #:nodoc:
188
189
  end
189
190
  alias << concat
190
191
 
192
+ def insert(index, value)
193
+ super(index, html_escape_interpolated_argument(value))
194
+ end
195
+
191
196
  def prepend(value)
192
197
  super(html_escape_interpolated_argument(value))
193
198
  end
194
199
 
200
+ def replace(value)
201
+ super(html_escape_interpolated_argument(value))
202
+ end
203
+
204
+ def []=(*args)
205
+ if args.count == 3
206
+ super(args[0], args[1], html_escape_interpolated_argument(args[2]))
207
+ else
208
+ super(args[0], html_escape_interpolated_argument(args[1]))
209
+ end
210
+ end
211
+
195
212
  def +(other)
196
213
  dup.concat(other)
197
214
  end
198
215
 
216
+ def *(*)
217
+ new_safe_buffer = super
218
+ new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
219
+ new_safe_buffer
220
+ end
221
+
199
222
  def %(args)
200
223
  case args
201
224
  when Hash
@@ -238,11 +261,44 @@ module ActiveSupport #:nodoc:
238
261
  end
239
262
  end
240
263
 
264
+ UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
265
+ if unsafe_method.respond_to?(unsafe_method)
266
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
267
+ def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
268
+ if block # if block
269
+ to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
270
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
271
+ block.call(*params) # block.call(*params)
272
+ } # }
273
+ else # else
274
+ to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
275
+ end # end
276
+ end # end
277
+
278
+ def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
279
+ @html_safe = false # @html_safe = false
280
+ if block # if block
281
+ super(*args) { |*params| # super(*args) { |*params|
282
+ set_block_back_references(block, $~) # set_block_back_references(block, $~)
283
+ block.call(*params) # block.call(*params)
284
+ } # }
285
+ else # else
286
+ super # super
287
+ end # end
288
+ end # end
289
+ EOT
290
+ end
291
+ end
292
+
241
293
  private
242
294
 
243
295
  def html_escape_interpolated_argument(arg)
244
296
  (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
245
297
  end
298
+
299
+ def set_block_back_references(block, match_data)
300
+ block.binding.eval("proc { |m| $~ = m }").call(match_data)
301
+ end
246
302
  end
247
303
  end
248
304
 
@@ -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
@@ -170,8 +170,7 @@ class Time
170
170
  options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
171
171
  end
172
172
 
173
- d = to_date.advance(options)
174
- d = d.gregorian if d.julian?
173
+ d = to_date.gregorian.advance(options)
175
174
  time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
176
175
  seconds_to_advance = \
177
176
  options.fetch(:seconds, 0) +
@@ -312,4 +311,34 @@ class Time
312
311
  end
313
312
  alias_method :eql_without_coercion, :eql?
314
313
  alias_method :eql?, :eql_with_coercion
314
+
315
+ # Returns a new time the specified number of days ago.
316
+ def prev_day(days = 1)
317
+ advance(days: -days)
318
+ end
319
+
320
+ # Returns a new time the specified number of days in the future.
321
+ def next_day(days = 1)
322
+ advance(days: days)
323
+ end
324
+
325
+ # Returns a new time the specified number of months ago.
326
+ def prev_month(months = 1)
327
+ advance(months: -months)
328
+ end
329
+
330
+ # Returns a new time the specified number of months in the future.
331
+ def next_month(months = 1)
332
+ advance(months: months)
333
+ end
334
+
335
+ # Returns a new time the specified number of years ago.
336
+ def prev_year(years = 1)
337
+ advance(years: -years)
338
+ end
339
+
340
+ # Returns a new time the specified number of years in the future.
341
+ def next_year(years = 1)
342
+ advance(years: years)
343
+ end
315
344
  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,110 @@
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
+ def self.camelize(basename, _abspath)
58
+ basename.camelize
59
+ end
60
+ end
61
+
62
+ class << self
63
+ def take_over(enable_reloading:)
64
+ setup_autoloaders(enable_reloading)
65
+ freeze_paths
66
+ decorate_dependencies
67
+ end
68
+
69
+ private
70
+
71
+ def setup_autoloaders(enable_reloading)
72
+ Dependencies.autoload_paths.each do |autoload_path|
73
+ # Zeitwerk only accepts existing directories in `push_dir` to
74
+ # prevent misconfigurations.
75
+ next unless File.directory?(autoload_path)
76
+
77
+ autoloader = \
78
+ autoload_once?(autoload_path) ? Rails.autoloaders.once : Rails.autoloaders.main
79
+
80
+ autoloader.push_dir(autoload_path)
81
+ autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path)
82
+ end
83
+
84
+ Rails.autoloaders.main.enable_reloading if enable_reloading
85
+ Rails.autoloaders.each(&:setup)
86
+ end
87
+
88
+ def autoload_once?(autoload_path)
89
+ Dependencies.autoload_once_paths.include?(autoload_path)
90
+ end
91
+
92
+ def eager_load?(autoload_path)
93
+ Dependencies._eager_load_paths.member?(autoload_path)
94
+ end
95
+
96
+ def freeze_paths
97
+ Dependencies.autoload_paths.freeze
98
+ Dependencies.autoload_once_paths.freeze
99
+ Dependencies._eager_load_paths.freeze
100
+ end
101
+
102
+ def decorate_dependencies
103
+ Dependencies.unhook!
104
+ Dependencies.singleton_class.prepend(Decorations)
105
+ Object.prepend(RequireDependency)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end