activesupport 5.2.1.1 → 6.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +416 -351
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -2
- data/lib/active_support.rb +2 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +28 -1
- data/lib/active_support/cache.rb +45 -23
- data/lib/active_support/cache/file_store.rb +22 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -0
- data/lib/active_support/cache/memory_store.rb +9 -2
- data/lib/active_support/cache/null_store.rb +5 -0
- data/lib/active_support/cache/redis_cache_store.rb +37 -10
- data/lib/active_support/callbacks.rb +16 -5
- data/lib/active_support/concern.rb +31 -4
- data/lib/active_support/configurable.rb +7 -11
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
- data/lib/active_support/core_ext/class/attribute.rb +11 -16
- data/lib/active_support/core_ext/class/subclasses.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +97 -73
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/compact.rb +2 -26
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +0 -29
- data/lib/active_support/core_ext/hash/slice.rb +3 -25
- data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
- data/lib/active_support/core_ext/module/delegation.rb +33 -7
- data/lib/active_support/core_ext/module/introspection.rb +37 -13
- data/lib/active_support/core_ext/module/reachable.rb +1 -6
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
- data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +1 -0
- data/lib/active_support/core_ext/object/try.rb +15 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +76 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/include_range.rb +6 -22
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
- data/lib/active_support/core_ext/regexp.rb +0 -4
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +8 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +7 -2
- data/lib/active_support/core_ext/string/multibyte.rb +4 -3
- data/lib/active_support/core_ext/string/output_safety.rb +63 -5
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +31 -2
- data/lib/active_support/core_ext/uri.rb +2 -1
- data/lib/active_support/current_attributes.rb +8 -0
- data/lib/active_support/dependencies.rb +74 -17
- data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/deprecation/behaviors.rb +1 -1
- data/lib/active_support/deprecation/method_wrappers.rb +13 -12
- data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
- data/lib/active_support/descendants_tracker.rb +56 -9
- data/lib/active_support/duration.rb +6 -5
- data/lib/active_support/duration/iso8601_parser.rb +2 -3
- data/lib/active_support/duration/iso8601_serializer.rb +3 -4
- data/lib/active_support/encrypted_configuration.rb +1 -5
- data/lib/active_support/encrypted_file.rb +2 -1
- data/lib/active_support/evented_file_update_checker.rb +39 -9
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +36 -18
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +14 -2
- data/lib/active_support/inflector/inflections.rb +1 -4
- data/lib/active_support/inflector/methods.rb +17 -27
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +23 -23
- data/lib/active_support/json/encoding.rb +6 -2
- data/lib/active_support/key_generator.rb +0 -32
- data/lib/active_support/lazy_load_hooks.rb +5 -1
- data/lib/active_support/locale/en.rb +31 -0
- data/lib/active_support/log_subscriber.rb +31 -8
- data/lib/active_support/logger.rb +0 -15
- data/lib/active_support/logger_silence.rb +28 -12
- data/lib/active_support/logger_thread_safe_level.rb +28 -5
- data/lib/active_support/message_encryptor.rb +3 -5
- data/lib/active_support/message_verifier.rb +3 -3
- data/lib/active_support/multibyte/chars.rb +29 -48
- data/lib/active_support/multibyte/unicode.rb +44 -281
- data/lib/active_support/notifications.rb +41 -4
- data/lib/active_support/notifications/fanout.rb +100 -15
- data/lib/active_support/notifications/instrumenter.rb +80 -8
- data/lib/active_support/number_helper.rb +7 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +1 -1
- data/lib/active_support/parameter_filter.rb +129 -0
- data/lib/active_support/rails.rb +0 -6
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/subscriber.rb +65 -22
- data/lib/active_support/tagged_logging.rb +13 -4
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +15 -1
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +128 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -7
- data/lib/active_support/time_with_zone.rb +15 -5
- data/lib/active_support/values/time_zone.rb +12 -7
- data/lib/active_support/xml_mini.rb +2 -9
- data/lib/active_support/xml_mini/jdom.rb +2 -2
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- metadata +33 -10
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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
|
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:
|
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
|
138
|
-
|
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
|
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,46 @@ 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
|
+
rescue ArgumentError
|
302
|
+
# Can't create binding from C level Proc
|
303
|
+
end
|
246
304
|
end
|
247
305
|
end
|
248
306
|
|
@@ -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}/, "".
|
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
|
@@ -10,7 +11,7 @@ if RUBY_VERSION < "2.6.0"
|
|
10
11
|
# YK: My initial experiments say yes, but let's be sure please
|
11
12
|
enc = str.encoding
|
12
13
|
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
|
13
|
-
str.gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
|
14
|
+
str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
@@ -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
|
|
@@ -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
|
@@ -97,6 +111,8 @@ module ActiveSupport #:nodoc:
|
|
97
111
|
# parent.rb then requires namespace/child.rb, the stack will look like
|
98
112
|
# [[Object], [Namespace]].
|
99
113
|
|
114
|
+
attr_reader :watching
|
115
|
+
|
100
116
|
def initialize
|
101
117
|
@watching = []
|
102
118
|
@stack = Hash.new { |h, k| h[k] = [] }
|
@@ -138,7 +154,7 @@ module ActiveSupport #:nodoc:
|
|
138
154
|
|
139
155
|
# Normalize the list of new constants, and add them to the list we will return
|
140
156
|
new_constants.each do |suffix|
|
141
|
-
constants << ([namespace, suffix] - ["Object"]).join("::"
|
157
|
+
constants << ([namespace, suffix] - ["Object"]).join("::")
|
142
158
|
end
|
143
159
|
end
|
144
160
|
constants
|
@@ -188,6 +204,11 @@ module ActiveSupport #:nodoc:
|
|
188
204
|
end
|
189
205
|
end
|
190
206
|
|
207
|
+
def self.include_into(base)
|
208
|
+
base.include(self)
|
209
|
+
append_features(base)
|
210
|
+
end
|
211
|
+
|
191
212
|
def const_missing(const_name)
|
192
213
|
from_mod = anonymous? ? guess_for_anonymous(const_name) : self
|
193
214
|
Dependencies.load_missing_constant(from_mod, const_name)
|
@@ -217,6 +238,21 @@ module ActiveSupport #:nodoc:
|
|
217
238
|
base.class_eval do
|
218
239
|
define_method(:load, Kernel.instance_method(:load))
|
219
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)
|
220
256
|
end
|
221
257
|
end
|
222
258
|
|
@@ -248,7 +284,9 @@ module ActiveSupport #:nodoc:
|
|
248
284
|
|
249
285
|
def load_dependency(file)
|
250
286
|
if Dependencies.load? && Dependencies.constant_watch_stack.watching?
|
251
|
-
Dependencies.
|
287
|
+
descs = Dependencies.constant_watch_stack.watching.flatten.uniq
|
288
|
+
|
289
|
+
Dependencies.new_constants_in(*descs) { yield }
|
252
290
|
else
|
253
291
|
yield
|
254
292
|
end
|
@@ -311,9 +349,9 @@ module ActiveSupport #:nodoc:
|
|
311
349
|
end
|
312
350
|
|
313
351
|
def hook!
|
314
|
-
Object
|
315
|
-
Module
|
316
|
-
Exception.
|
352
|
+
Loadable.include_into(Object)
|
353
|
+
ModuleConstMissing.include_into(Module)
|
354
|
+
Exception.include(Blamable)
|
317
355
|
end
|
318
356
|
|
319
357
|
def unhook!
|
@@ -345,7 +383,7 @@ module ActiveSupport #:nodoc:
|
|
345
383
|
end
|
346
384
|
|
347
385
|
def require_or_load(file_name, const_path = nil)
|
348
|
-
file_name =
|
386
|
+
file_name = file_name.chomp(".rb")
|
349
387
|
expanded = File.expand_path(file_name)
|
350
388
|
return if loaded.include?(expanded)
|
351
389
|
|
@@ -395,7 +433,7 @@ module ActiveSupport #:nodoc:
|
|
395
433
|
# constant paths which would cause Dependencies to attempt to load this
|
396
434
|
# file.
|
397
435
|
def loadable_constants_for_path(path, bases = autoload_paths)
|
398
|
-
path =
|
436
|
+
path = path.chomp(".rb")
|
399
437
|
expanded_path = File.expand_path(path)
|
400
438
|
paths = []
|
401
439
|
|
@@ -404,7 +442,7 @@ module ActiveSupport #:nodoc:
|
|
404
442
|
next unless expanded_path.start_with?(expanded_root)
|
405
443
|
|
406
444
|
root_size = expanded_root.size
|
407
|
-
next if expanded_path[root_size] !=
|
445
|
+
next if expanded_path[root_size] != ?/
|
408
446
|
|
409
447
|
nesting = expanded_path[(root_size + 1)..-1]
|
410
448
|
paths << nesting.camelize unless nesting.blank?
|
@@ -416,7 +454,7 @@ module ActiveSupport #:nodoc:
|
|
416
454
|
|
417
455
|
# Search for a file in autoload_paths matching the provided suffix.
|
418
456
|
def search_for_file(path_suffix)
|
419
|
-
path_suffix
|
457
|
+
path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
|
420
458
|
|
421
459
|
autoload_paths.each do |root|
|
422
460
|
path = File.join(root, path_suffix)
|
@@ -450,6 +488,7 @@ module ActiveSupport #:nodoc:
|
|
450
488
|
return nil unless base_path = autoloadable_module?(path_suffix)
|
451
489
|
mod = Module.new
|
452
490
|
into.const_set const_name, mod
|
491
|
+
log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
|
453
492
|
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
|
454
493
|
autoloaded_constants.uniq!
|
455
494
|
mod
|
@@ -491,26 +530,31 @@ module ActiveSupport #:nodoc:
|
|
491
530
|
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
|
492
531
|
end
|
493
532
|
|
494
|
-
qualified_name = qualified_name_for
|
533
|
+
qualified_name = qualified_name_for(from_mod, const_name)
|
495
534
|
path_suffix = qualified_name.underscore
|
496
535
|
|
497
536
|
file_path = search_for_file(path_suffix)
|
498
537
|
|
499
538
|
if file_path
|
500
539
|
expanded = File.expand_path(file_path)
|
501
|
-
expanded.sub!(/\.rb\z/, ""
|
540
|
+
expanded.sub!(/\.rb\z/, "")
|
502
541
|
|
503
542
|
if loading.include?(expanded)
|
504
543
|
raise "Circular dependency detected while autoloading constant #{qualified_name}"
|
505
544
|
else
|
506
545
|
require_or_load(expanded, qualified_name)
|
507
|
-
|
508
|
-
|
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
|
509
553
|
end
|
510
554
|
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
|
511
555
|
return mod
|
512
|
-
elsif (parent = from_mod.
|
513
|
-
! from_mod.
|
556
|
+
elsif (parent = from_mod.module_parent) && parent != from_mod &&
|
557
|
+
! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
|
514
558
|
# If our parents do not have a constant named +const_name+ then we are free
|
515
559
|
# to attempt to load upwards. If they do have such a constant, then this
|
516
560
|
# const_missing must be due to from_mod::const_name, which should not
|
@@ -554,6 +598,7 @@ module ActiveSupport #:nodoc:
|
|
554
598
|
# as the environment will be in an inconsistent state, e.g. other constants
|
555
599
|
# may have already been unloaded and not accessible.
|
556
600
|
def remove_unloadable_constants!
|
601
|
+
log("removing unloadable constants")
|
557
602
|
autoloaded_constants.each { |const| remove_constant const }
|
558
603
|
autoloaded_constants.clear
|
559
604
|
Reference.clear!
|
@@ -617,7 +662,7 @@ module ActiveSupport #:nodoc:
|
|
617
662
|
|
618
663
|
# Determine if the given constant has been automatically loaded.
|
619
664
|
def autoloaded?(desc)
|
620
|
-
return false if desc.is_a?(Module) && desc.
|
665
|
+
return false if desc.is_a?(Module) && real_mod_name(desc).nil?
|
621
666
|
name = to_constant_name desc
|
622
667
|
return false unless qualified_const_defined?(name)
|
623
668
|
autoloaded_constants.include?(name)
|
@@ -673,7 +718,7 @@ module ActiveSupport #:nodoc:
|
|
673
718
|
when String then desc.sub(/^::/, "")
|
674
719
|
when Symbol then desc.to_s
|
675
720
|
when Module
|
676
|
-
desc
|
721
|
+
real_mod_name(desc) ||
|
677
722
|
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
|
678
723
|
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
|
679
724
|
end
|
@@ -743,6 +788,18 @@ module ActiveSupport #:nodoc:
|
|
743
788
|
# The constant is no longer reachable, just skip it.
|
744
789
|
end
|
745
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
|
746
803
|
end
|
747
804
|
end
|
748
805
|
|