activesupport 7.2.3 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -347
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +2 -2
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +74 -61
- data/lib/active_support/cache/file_store.rb +14 -4
- data/lib/active_support/cache/mem_cache_store.rb +15 -13
- data/lib/active_support/cache/memory_store.rb +9 -5
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +6 -3
- data/lib/active_support/cache/strategy/local_cache.rb +20 -56
- data/lib/active_support/cache.rb +19 -14
- data/lib/active_support/callbacks.rb +7 -6
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/core_ext/array/conversions.rb +3 -3
- data/lib/active_support/core_ext/benchmark.rb +6 -10
- data/lib/active_support/core_ext/class/attribute.rb +22 -18
- data/lib/active_support/core_ext/date/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +2 -2
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -4
- data/lib/active_support/core_ext/enumerable.rb +13 -20
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
- data/lib/active_support/core_ext/module/introspection.rb +0 -3
- data/lib/active_support/core_ext/object/json.rb +15 -9
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range.rb +0 -1
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +14 -2
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/current_attributes.rb +7 -14
- data/lib/active_support/delegation.rb +0 -2
- data/lib/active_support/dependencies.rb +0 -1
- data/lib/active_support/deprecation/reporting.rb +0 -19
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration.rb +14 -10
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/error_reporter.rb +27 -6
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +1 -1
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +31 -35
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/isolated_execution_state.rb +0 -2
- data/lib/active_support/json/decoding.rb +1 -1
- data/lib/active_support/json/encoding.rb +7 -25
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/message_encryptors.rb +2 -2
- data/lib/active_support/message_verifier.rb +0 -9
- data/lib/active_support/message_verifiers.rb +3 -5
- data/lib/active_support/messages/rotator.rb +0 -5
- data/lib/active_support/multibyte/chars.rb +1 -4
- data/lib/active_support/notifications/fanout.rb +0 -1
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/railtie.rb +4 -0
- data/lib/active_support/tagged_logging.rb +5 -0
- data/lib/active_support/testing/assertions.rb +79 -21
- data/lib/active_support/testing/isolation.rb +0 -2
- data/lib/active_support/testing/parallelization/server.rb +2 -15
- data/lib/active_support/testing/parallelization/worker.rb +2 -2
- data/lib/active_support/testing/parallelization.rb +1 -12
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/time_helpers.rb +2 -1
- data/lib/active_support/time_with_zone.rb +22 -13
- data/lib/active_support/values/time_zone.rb +11 -9
- data/lib/active_support/xml_mini.rb +0 -2
- data/lib/active_support.rb +10 -3
- metadata +27 -9
- data/lib/active_support/core_ext/range/sole.rb +0 -17
- data/lib/active_support/proxy_object.rb +0 -20
|
@@ -144,9 +144,9 @@ module ActiveSupport
|
|
|
144
144
|
#
|
|
145
145
|
def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
|
|
146
146
|
error = RuntimeError.new(error) if error.is_a?(String)
|
|
147
|
-
error.set_backtrace(caller(1)) if error.backtrace.nil?
|
|
148
147
|
|
|
149
148
|
if @debug_mode
|
|
149
|
+
ensure_backtrace(error)
|
|
150
150
|
raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
|
|
151
151
|
else
|
|
152
152
|
report(error, handled: true, severity: severity, context: context, source: source)
|
|
@@ -209,6 +209,7 @@ module ActiveSupport
|
|
|
209
209
|
#
|
|
210
210
|
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
|
|
211
211
|
return if error.instance_variable_defined?(:@__rails_error_reported)
|
|
212
|
+
ensure_backtrace(error)
|
|
212
213
|
|
|
213
214
|
unless SEVERITIES.include?(severity)
|
|
214
215
|
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
|
@@ -231,14 +232,34 @@ module ActiveSupport
|
|
|
231
232
|
end
|
|
232
233
|
end
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
error.instance_variable_set(:@__rails_error_reported, true)
|
|
237
|
-
end
|
|
238
|
-
error = error.cause
|
|
235
|
+
unless error.frozen?
|
|
236
|
+
error.instance_variable_set(:@__rails_error_reported, true)
|
|
239
237
|
end
|
|
240
238
|
|
|
241
239
|
nil
|
|
242
240
|
end
|
|
241
|
+
|
|
242
|
+
private
|
|
243
|
+
def ensure_backtrace(error)
|
|
244
|
+
return if error.frozen? # re-raising won't add a backtrace
|
|
245
|
+
return unless error.backtrace.nil?
|
|
246
|
+
|
|
247
|
+
begin
|
|
248
|
+
# We could use Exception#set_backtrace, but until Ruby 3.4
|
|
249
|
+
# it only support setting `Exception#backtrace` and not
|
|
250
|
+
# `Exception#backtrace_locations`. So raising the exception
|
|
251
|
+
# is a good way to build a real backtrace.
|
|
252
|
+
raise error
|
|
253
|
+
rescue error.class => error
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
count = 0
|
|
257
|
+
while error.backtrace_locations.first&.path == __FILE__
|
|
258
|
+
count += 1
|
|
259
|
+
error.backtrace_locations.shift
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
error.backtrace.shift(count)
|
|
263
|
+
end
|
|
243
264
|
end
|
|
244
265
|
end
|
|
@@ -120,7 +120,7 @@ module ActiveSupport
|
|
|
120
120
|
# healthy to consider this edge case because with mtimes in the future
|
|
121
121
|
# reloading is not triggered.
|
|
122
122
|
def max_mtime(paths)
|
|
123
|
-
time_now = Time.
|
|
123
|
+
time_now = Time.now
|
|
124
124
|
max_mtime = nil
|
|
125
125
|
|
|
126
126
|
# Time comparisons are performed with #compare_without_coercion because
|
|
@@ -262,7 +262,9 @@ module ActiveSupport
|
|
|
262
262
|
# hash[:a][:c] # => "c"
|
|
263
263
|
# dup[:a][:c] # => "c"
|
|
264
264
|
def dup
|
|
265
|
-
|
|
265
|
+
self.class.new(self).tap do |new_hash|
|
|
266
|
+
set_defaults(new_hash)
|
|
267
|
+
end
|
|
266
268
|
end
|
|
267
269
|
|
|
268
270
|
# This method has the same semantics of +update+, except it does not
|
|
@@ -311,10 +313,6 @@ module ActiveSupport
|
|
|
311
313
|
end
|
|
312
314
|
alias_method :without, :except
|
|
313
315
|
|
|
314
|
-
def stringify_keys!; self end
|
|
315
|
-
def deep_stringify_keys!; self end
|
|
316
|
-
def stringify_keys; dup end
|
|
317
|
-
def deep_stringify_keys; dup end
|
|
318
316
|
undef :symbolize_keys!
|
|
319
317
|
undef :deep_symbolize_keys!
|
|
320
318
|
def symbolize_keys; to_hash.symbolize_keys! end
|
|
@@ -340,26 +338,21 @@ module ActiveSupport
|
|
|
340
338
|
NOT_GIVEN = Object.new # :nodoc:
|
|
341
339
|
|
|
342
340
|
def transform_keys(hash = NOT_GIVEN, &block)
|
|
343
|
-
if NOT_GIVEN.equal?(hash)
|
|
344
|
-
|
|
345
|
-
self.class.new(super(&block))
|
|
346
|
-
else
|
|
347
|
-
to_enum(:transform_keys)
|
|
348
|
-
end
|
|
349
|
-
else
|
|
350
|
-
self.class.new(super)
|
|
351
|
-
end
|
|
341
|
+
return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
|
|
342
|
+
dup.tap { |h| h.transform_keys!(hash, &block) }
|
|
352
343
|
end
|
|
353
344
|
|
|
354
345
|
def transform_keys!(hash = NOT_GIVEN, &block)
|
|
355
|
-
if NOT_GIVEN.equal?(hash)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
346
|
+
return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
|
|
347
|
+
|
|
348
|
+
if hash.nil?
|
|
349
|
+
super
|
|
350
|
+
elsif NOT_GIVEN.equal?(hash)
|
|
351
|
+
keys.each { |key| self[yield(key)] = delete(key) }
|
|
352
|
+
elsif block_given?
|
|
353
|
+
keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
|
|
361
354
|
else
|
|
362
|
-
|
|
355
|
+
keys.each { |key| self[hash[key] || key] = delete(key) }
|
|
363
356
|
end
|
|
364
357
|
|
|
365
358
|
self
|
|
@@ -381,13 +374,10 @@ module ActiveSupport
|
|
|
381
374
|
|
|
382
375
|
# Convert to a regular hash with string keys.
|
|
383
376
|
def to_hash
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
_new_hash[key] = convert_value(value, conversion: :to_hash)
|
|
389
|
-
end
|
|
390
|
-
_new_hash
|
|
377
|
+
copy = Hash[self]
|
|
378
|
+
copy.transform_values! { |v| convert_value_to_hash(v) }
|
|
379
|
+
set_defaults(copy)
|
|
380
|
+
copy
|
|
391
381
|
end
|
|
392
382
|
|
|
393
383
|
def to_proc
|
|
@@ -401,11 +391,7 @@ module ActiveSupport
|
|
|
401
391
|
|
|
402
392
|
def convert_value(value, conversion: nil)
|
|
403
393
|
if value.is_a? Hash
|
|
404
|
-
|
|
405
|
-
value.to_hash
|
|
406
|
-
else
|
|
407
|
-
value.nested_under_indifferent_access
|
|
408
|
-
end
|
|
394
|
+
value.nested_under_indifferent_access
|
|
409
395
|
elsif value.is_a?(Array)
|
|
410
396
|
if conversion != :assignment || value.frozen?
|
|
411
397
|
value = value.dup
|
|
@@ -416,13 +402,23 @@ module ActiveSupport
|
|
|
416
402
|
end
|
|
417
403
|
end
|
|
418
404
|
|
|
419
|
-
def
|
|
405
|
+
def convert_value_to_hash(value)
|
|
406
|
+
if value.is_a? Hash
|
|
407
|
+
value.to_hash
|
|
408
|
+
elsif value.is_a?(Array)
|
|
409
|
+
value.map { |e| convert_value_to_hash(e) }
|
|
410
|
+
else
|
|
411
|
+
value
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def set_defaults(target)
|
|
420
417
|
if default_proc
|
|
421
418
|
target.default_proc = default_proc.dup
|
|
422
419
|
else
|
|
423
420
|
target.default = default
|
|
424
421
|
end
|
|
425
|
-
target
|
|
426
422
|
end
|
|
427
423
|
|
|
428
424
|
def update_with_single_argument(other_hash, block)
|
|
@@ -14,15 +14,18 @@ module I18n
|
|
|
14
14
|
|
|
15
15
|
config.eager_load_namespaces << I18n
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
config.
|
|
17
|
+
# Make sure i18n is ready before eager loading, in case any eager loaded
|
|
18
|
+
# code needs it.
|
|
19
|
+
config.before_eager_load do |app|
|
|
20
20
|
I18n::Railtie.initialize_i18n(app)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
23
|
+
# i18n initialization needs to run after application initialization, since
|
|
24
|
+
# initializers may configure i18n.
|
|
25
|
+
#
|
|
26
|
+
# If the application eager loaded, this was done on before_eager_load. The
|
|
27
|
+
# hook is still OK, though, because initialize_i18n is idempotent.
|
|
28
|
+
config.after_initialize do |app|
|
|
26
29
|
I18n::Railtie.initialize_i18n(app)
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -49,7 +52,8 @@ module I18n
|
|
|
49
52
|
when :load_path
|
|
50
53
|
I18n.load_path += value
|
|
51
54
|
when :raise_on_missing_translations
|
|
52
|
-
|
|
55
|
+
strict = value == :strict
|
|
56
|
+
setup_raise_on_missing_translations_config(app, strict)
|
|
53
57
|
else
|
|
54
58
|
I18n.public_send("#{setting}=", value)
|
|
55
59
|
end
|
|
@@ -62,8 +66,9 @@ module I18n
|
|
|
62
66
|
|
|
63
67
|
if app.config.reloading_enabled?
|
|
64
68
|
directories = watched_dirs_with_extensions(reloadable_paths)
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
root_load_paths = I18n.load_path.select { |path| path.to_s.start_with?(Rails.root.to_s) }
|
|
70
|
+
reloader = app.config.file_watcher.new(root_load_paths, directories) do
|
|
71
|
+
I18n.load_path.delete_if { |path| path.to_s.start_with?(Rails.root.to_s) && !File.exist?(path) }
|
|
67
72
|
I18n.load_path |= reloadable_paths.flat_map(&:existent)
|
|
68
73
|
end
|
|
69
74
|
|
|
@@ -71,17 +76,20 @@ module I18n
|
|
|
71
76
|
app.reloader.to_run do
|
|
72
77
|
reloader.execute_if_updated { require_unload_lock! }
|
|
73
78
|
end
|
|
74
|
-
reloader.execute
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
@i18n_inited = true
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
def self.setup_raise_on_missing_translations_config(app)
|
|
84
|
+
def self.setup_raise_on_missing_translations_config(app, strict)
|
|
81
85
|
ActiveSupport.on_load(:action_view) do
|
|
82
86
|
ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
|
|
83
87
|
end
|
|
84
88
|
|
|
89
|
+
ActiveSupport.on_load(:active_model_translation) do
|
|
90
|
+
ActiveModel::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations if strict
|
|
91
|
+
end
|
|
92
|
+
|
|
85
93
|
if app.config.i18n.raise_on_missing_translations &&
|
|
86
94
|
I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
|
|
87
95
|
|
|
@@ -18,7 +18,7 @@ module ActiveSupport
|
|
|
18
18
|
# See http://www.json.org for more info.
|
|
19
19
|
#
|
|
20
20
|
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
|
|
21
|
-
#
|
|
21
|
+
# => {"team" => "rails", "players" => "36"}
|
|
22
22
|
def decode(json)
|
|
23
23
|
data = ::JSON.parse(json, quirks_mode: true)
|
|
24
24
|
|
|
@@ -13,30 +13,12 @@ module ActiveSupport
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
module JSON
|
|
16
|
+
# Dumps objects in JSON (JavaScript Object Notation).
|
|
17
|
+
# See http://www.json.org for more info.
|
|
18
|
+
#
|
|
19
|
+
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
20
|
+
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
16
21
|
class << self
|
|
17
|
-
# Dumps objects in JSON (JavaScript Object Notation).
|
|
18
|
-
# See http://www.json.org for more info.
|
|
19
|
-
#
|
|
20
|
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
|
|
21
|
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
22
|
-
#
|
|
23
|
-
# Generates JSON that is safe to include in JavaScript as it escapes
|
|
24
|
-
# U+2028 (Line Separator) and U+2029 (Paragraph Separator):
|
|
25
|
-
#
|
|
26
|
-
# ActiveSupport::JSON.encode({ key: "\u2028" })
|
|
27
|
-
# # => "{\"key\":\"\\u2028\"}"
|
|
28
|
-
#
|
|
29
|
-
# By default, it also generates JSON that is safe to include in HTML, as
|
|
30
|
-
# it escapes <tt><</tt>, <tt>></tt>, and <tt>&</tt>:
|
|
31
|
-
#
|
|
32
|
-
# ActiveSupport::JSON.encode({ key: "<>&" })
|
|
33
|
-
# # => "{\"key\":\"\\u003c\\u003e\\u0026\"}"
|
|
34
|
-
#
|
|
35
|
-
# This can be changed with the +escape_html_entities+ option, or the
|
|
36
|
-
# global escape_html_entities_in_json configuration option.
|
|
37
|
-
#
|
|
38
|
-
# ActiveSupport::JSON.encode({ key: "<>&" }, escape_html_entities: false)
|
|
39
|
-
# # => "{\"key\":\"<>&\"}"
|
|
40
22
|
def encode(value, options = nil)
|
|
41
23
|
Encoding.json_encoder.new(options).encode(value)
|
|
42
24
|
end
|
|
@@ -54,14 +36,14 @@ module ActiveSupport
|
|
|
54
36
|
# Encode the given object into a JSON string
|
|
55
37
|
def encode(value)
|
|
56
38
|
unless options.empty?
|
|
57
|
-
value = value.as_json(options.dup)
|
|
39
|
+
value = value.as_json(options.dup.freeze)
|
|
58
40
|
end
|
|
59
41
|
json = stringify(jsonify(value))
|
|
60
42
|
|
|
61
43
|
# Rails does more escaping than the JSON gem natively does (we
|
|
62
44
|
# escape \u2028 and \u2029 and optionally >, <, & to work around
|
|
63
45
|
# certain browser problems).
|
|
64
|
-
if Encoding.escape_html_entities_in_json
|
|
46
|
+
if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
|
|
65
47
|
json.gsub!(">", '\u003e')
|
|
66
48
|
json.gsub!("<", '\u003c')
|
|
67
49
|
json.gsub!("&", '\u0026')
|
|
@@ -53,7 +53,7 @@ module ActiveSupport
|
|
|
53
53
|
# loaded. If the component has already loaded, the block is executed
|
|
54
54
|
# immediately.
|
|
55
55
|
#
|
|
56
|
-
#
|
|
56
|
+
# Options:
|
|
57
57
|
#
|
|
58
58
|
# * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
|
|
59
59
|
# * <tt>:run_once</tt> - Given +block+ will run only once.
|
|
@@ -28,8 +28,8 @@ module ActiveSupport
|
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
29
|
|
|
30
30
|
##
|
|
31
|
-
# :
|
|
32
|
-
# :call-seq:
|
|
31
|
+
# :method: initialize
|
|
32
|
+
# :call-seq: initialize(&secret_generator)
|
|
33
33
|
#
|
|
34
34
|
# Initializes a new instance. +secret_generator+ must accept a salt and a
|
|
35
35
|
# +secret_length+ kwarg, and return a suitable secret (string) or secrets
|
|
@@ -154,8 +154,6 @@ module ActiveSupport
|
|
|
154
154
|
# not URL-safe. In other words, they can contain "+" and "/". If you want to
|
|
155
155
|
# generate URL-safe strings (in compliance with "Base 64 Encoding with URL
|
|
156
156
|
# and Filename Safe Alphabet" in RFC 4648), you can pass +true+.
|
|
157
|
-
# Note that MessageVerifier will always accept both URL-safe and URL-unsafe
|
|
158
|
-
# encoded messages, to allow a smooth transition between the two settings.
|
|
159
157
|
#
|
|
160
158
|
# [+:force_legacy_metadata_serializer+]
|
|
161
159
|
# Whether to use the legacy metadata serializer, which serializes the
|
|
@@ -320,13 +318,6 @@ module ActiveSupport
|
|
|
320
318
|
end
|
|
321
319
|
|
|
322
320
|
private
|
|
323
|
-
def decode(encoded, url_safe: @url_safe)
|
|
324
|
-
catch :invalid_message_format do
|
|
325
|
-
return super
|
|
326
|
-
end
|
|
327
|
-
super(encoded, url_safe: !url_safe)
|
|
328
|
-
end
|
|
329
|
-
|
|
330
321
|
def sign_encoded(encoded)
|
|
331
322
|
digest = generate_digest(encoded)
|
|
332
323
|
encoded << SEPARATOR << digest
|
|
@@ -28,8 +28,8 @@ module ActiveSupport
|
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
29
|
|
|
30
30
|
##
|
|
31
|
-
# :
|
|
32
|
-
# :call-seq:
|
|
31
|
+
# :method: initialize
|
|
32
|
+
# :call-seq: initialize(&secret_generator)
|
|
33
33
|
#
|
|
34
34
|
# Initializes a new instance. +secret_generator+ must accept a salt, and
|
|
35
35
|
# return a suitable secret (string). +secret_generator+ may also accept
|
|
@@ -59,9 +59,7 @@ module ActiveSupport
|
|
|
59
59
|
|
|
60
60
|
##
|
|
61
61
|
# :method: rotate
|
|
62
|
-
# :call-seq:
|
|
63
|
-
# rotate(**options)
|
|
64
|
-
# rotate(&block)
|
|
62
|
+
# :call-seq: rotate(**options)
|
|
65
63
|
#
|
|
66
64
|
# Adds +options+ to the list of option sets. Messages will be signed using
|
|
67
65
|
# the first set in the list. When verifying, however, each set will be
|
|
@@ -55,10 +55,7 @@ module ActiveSupport # :nodoc:
|
|
|
55
55
|
# Creates a new Chars instance by wrapping _string_.
|
|
56
56
|
def initialize(string)
|
|
57
57
|
@wrapped_string = string
|
|
58
|
-
|
|
59
|
-
@wrapped_string = @wrapped_string.dup
|
|
60
|
-
@wrapped_string.force_encoding(Encoding::UTF_8)
|
|
61
|
-
end
|
|
58
|
+
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
# Forward all undefined methods to the wrapped string.
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ActiveSupport
|
|
4
|
+
# = Number Helper
|
|
5
|
+
#
|
|
6
|
+
# Provides methods for formatting numbers into currencies, percentages,
|
|
7
|
+
# phone numbers, and more.
|
|
8
|
+
#
|
|
9
|
+
# Example usage in a class:
|
|
10
|
+
# class Topic
|
|
11
|
+
# include ActiveSupport::NumberHelper
|
|
12
|
+
#
|
|
13
|
+
# def price
|
|
14
|
+
# number_to_currency(@price)
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# Example usage in a module:
|
|
19
|
+
# require "active_support/number_helper"
|
|
20
|
+
#
|
|
21
|
+
# module NumberFormatting
|
|
22
|
+
# def format_price(price)
|
|
23
|
+
# ActiveSupport::NumberHelper.number_to_currency(price)
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
4
26
|
module NumberHelper
|
|
5
27
|
extend ActiveSupport::Autoload
|
|
6
28
|
|
|
@@ -96,6 +96,10 @@ module ActiveSupport
|
|
|
96
96
|
config.eager_load_namespaces << TZInfo
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
+
initializer "active_support.to_time_preserves_timezone" do |app|
|
|
100
|
+
ActiveSupport.to_time_preserves_timezone = app.config.active_support.to_time_preserves_timezone
|
|
101
|
+
end
|
|
102
|
+
|
|
99
103
|
# Sets the default week start
|
|
100
104
|
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
|
|
101
105
|
initializer "active_support.initialize_beginning_of_week" do |app|
|
|
@@ -113,6 +113,11 @@ module ActiveSupport
|
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
# Returns an `ActiveSupport::Logger` that has already been wrapped with tagged logging concern.
|
|
117
|
+
def self.logger(*args, **kwargs)
|
|
118
|
+
new ActiveSupport::Logger.new(*args, **kwargs)
|
|
119
|
+
end
|
|
120
|
+
|
|
116
121
|
def self.new(logger)
|
|
117
122
|
logger = logger.clone
|
|
118
123
|
|
|
@@ -19,7 +19,7 @@ module ActiveSupport
|
|
|
19
19
|
#
|
|
20
20
|
# assert_not foo, 'foo should be false'
|
|
21
21
|
def assert_not(object, message = nil)
|
|
22
|
-
message ||= "Expected #{mu_pp(object)} to be nil or false"
|
|
22
|
+
message ||= -> { "Expected #{mu_pp(object)} to be nil or false" }
|
|
23
23
|
assert !object, message
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -118,9 +118,13 @@ module ActiveSupport
|
|
|
118
118
|
|
|
119
119
|
expressions.zip(exps, before) do |(code, diff), exp, before_value|
|
|
120
120
|
actual = exp.call
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
rich_message = -> do
|
|
122
|
+
code_string = code.respond_to?(:call) ? _callable_to_source_string(code) : code
|
|
123
|
+
error = "`#{code_string}` didn't change by #{diff}, but by #{actual - before_value}"
|
|
124
|
+
error = "#{message}.\n#{error}" if message
|
|
125
|
+
error
|
|
126
|
+
end
|
|
127
|
+
assert_equal(before_value + diff, actual, rich_message)
|
|
124
128
|
end
|
|
125
129
|
|
|
126
130
|
retval
|
|
@@ -195,22 +199,32 @@ module ActiveSupport
|
|
|
195
199
|
retval = _assert_nothing_raised_or_warn("assert_changes", &block)
|
|
196
200
|
|
|
197
201
|
unless from == UNTRACKED
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
rich_message = -> do
|
|
203
|
+
error = "Expected change from #{from.inspect}, got #{before.inspect}"
|
|
204
|
+
error = "#{message}.\n#{error}" if message
|
|
205
|
+
error
|
|
206
|
+
end
|
|
207
|
+
assert from === before, rich_message
|
|
201
208
|
end
|
|
202
209
|
|
|
203
210
|
after = exp.call
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
rich_message = -> do
|
|
213
|
+
code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
|
|
214
|
+
error = "`#{code_string}` didn't change"
|
|
215
|
+
error = "#{error}. It was already #{to.inspect}" if before == to
|
|
216
|
+
error = "#{message}.\n#{error}" if message
|
|
217
|
+
error
|
|
218
|
+
end
|
|
219
|
+
refute_equal before, after, rich_message
|
|
209
220
|
|
|
210
221
|
unless to == UNTRACKED
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
222
|
+
rich_message = -> do
|
|
223
|
+
error = "Expected change to #{to.inspect}, got #{after.inspect}\n"
|
|
224
|
+
error = "#{message}.\n#{error}" if message
|
|
225
|
+
error
|
|
226
|
+
end
|
|
227
|
+
assert to === after, rich_message
|
|
214
228
|
end
|
|
215
229
|
|
|
216
230
|
retval
|
|
@@ -242,20 +256,27 @@ module ActiveSupport
|
|
|
242
256
|
retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
|
|
243
257
|
|
|
244
258
|
unless from == UNTRACKED
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
259
|
+
rich_message = -> do
|
|
260
|
+
error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
|
|
261
|
+
error = "#{message}.\n#{error}" if message
|
|
262
|
+
error
|
|
263
|
+
end
|
|
264
|
+
assert from === before, rich_message
|
|
248
265
|
end
|
|
249
266
|
|
|
250
267
|
after = exp.call
|
|
251
268
|
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
rich_message = -> do
|
|
270
|
+
code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
|
|
271
|
+
error = "`#{code_string}` changed"
|
|
272
|
+
error = "#{message}.\n#{error}" if message
|
|
273
|
+
error
|
|
274
|
+
end
|
|
254
275
|
|
|
255
276
|
if before.nil?
|
|
256
|
-
assert_nil after,
|
|
277
|
+
assert_nil after, rich_message
|
|
257
278
|
else
|
|
258
|
-
assert_equal before, after,
|
|
279
|
+
assert_equal before, after, rich_message
|
|
259
280
|
end
|
|
260
281
|
|
|
261
282
|
retval
|
|
@@ -276,6 +297,43 @@ module ActiveSupport
|
|
|
276
297
|
|
|
277
298
|
raise
|
|
278
299
|
end
|
|
300
|
+
|
|
301
|
+
def _callable_to_source_string(callable)
|
|
302
|
+
if defined?(RubyVM::InstructionSequence) && callable.is_a?(Proc)
|
|
303
|
+
iseq = RubyVM::InstructionSequence.of(callable)
|
|
304
|
+
source =
|
|
305
|
+
if iseq.script_lines
|
|
306
|
+
iseq.script_lines.join("\n")
|
|
307
|
+
elsif File.readable?(iseq.absolute_path)
|
|
308
|
+
File.read(iseq.absolute_path)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
return callable unless source
|
|
312
|
+
|
|
313
|
+
location = iseq.to_a[4][:code_location]
|
|
314
|
+
return callable unless location
|
|
315
|
+
|
|
316
|
+
lines = source.lines[(location[0] - 1)..(location[2] - 1)]
|
|
317
|
+
lines[-1] = lines[-1].byteslice(...location[3])
|
|
318
|
+
lines[0] = lines[0].byteslice(location[1]...)
|
|
319
|
+
source = lines.join.strip
|
|
320
|
+
|
|
321
|
+
# We ignore procs defined with do/end as they are likely multi-line anyway.
|
|
322
|
+
if source.start_with?("{")
|
|
323
|
+
source.delete_suffix!("}")
|
|
324
|
+
source.delete_prefix!("{")
|
|
325
|
+
source.strip!
|
|
326
|
+
# It won't read nice if the callable contains multiple
|
|
327
|
+
# lines, and it should be a rare occurrence anyway.
|
|
328
|
+
# Same if it takes arguments.
|
|
329
|
+
if !source.include?("\n") && !source.start_with?("|")
|
|
330
|
+
return source
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
callable
|
|
336
|
+
end
|
|
279
337
|
end
|
|
280
338
|
end
|
|
281
339
|
end
|