activesupport 7.1.6 → 8.1.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 +256 -1133
- data/README.rdoc +1 -1
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +81 -3
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +65 -78
- data/lib/active_support/cache/file_store.rb +29 -14
- data/lib/active_support/cache/mem_cache_store.rb +42 -102
- data/lib/active_support/cache/memory_store.rb +11 -6
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +58 -46
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache/strategy/local_cache.rb +72 -27
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +146 -86
- data/lib/active_support/callbacks.rb +102 -126
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +9 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +34 -0
- data/lib/active_support/configuration_file.rb +15 -6
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/conversions.rb +3 -5
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +4 -14
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +26 -19
- data/lib/active_support/core_ext/class/subclasses.rb +15 -35
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -9
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -6
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest/uuid.rb +6 -0
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/erb/util.rb +10 -5
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
- data/lib/active_support/core_ext/hash/except.rb +0 -12
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/hash.rb +8 -8
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/kernel.rb +3 -3
- data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -163
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/module/introspection.rb +3 -0
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +24 -11
- data/lib/active_support/core_ext/object/to_query.rb +7 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname/blank.rb +4 -0
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range/overlap.rb +4 -4
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +4 -4
- data/lib/active_support/core_ext/securerandom.rb +4 -4
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +4 -4
- data/lib/active_support/core_ext/string/multibyte.rb +13 -4
- data/lib/active_support/core_ext/string/output_safety.rb +19 -19
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
- data/lib/active_support/core_ext/time/calculations.rb +25 -30
- data/lib/active_support/core_ext/time/compatibility.rb +2 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -2
- data/lib/active_support/core_ext/time/zones.rb +1 -1
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/core_ext.rb +1 -2
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +58 -50
- data/lib/active_support/delegation.rb +200 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -2
- data/lib/active_support/deprecation/constant_accessor.rb +47 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
- data/lib/active_support/deprecation/reporting.rb +5 -17
- data/lib/active_support/deprecation.rb +8 -5
- data/lib/active_support/descendants_tracker.rb +9 -87
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -2
- data/lib/active_support/duration.rb +25 -16
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +20 -2
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/error_reporter.rb +121 -6
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -3
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/execution_wrapper.rb +1 -2
- data/lib/active_support/file_update_checker.rb +9 -7
- data/lib/active_support/fork_tracker.rb +2 -38
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +66 -45
- data/lib/active_support/html_safe_translation.rb +3 -0
- data/lib/active_support/i18n_railtie.rb +19 -11
- data/lib/active_support/inflector/inflections.rb +31 -15
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +12 -17
- data/lib/active_support/json/decoding.rb +6 -4
- data/lib/active_support/json/encoding.rb +157 -21
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber.rb +2 -18
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +4 -9
- data/lib/active_support/message_encryptors.rb +54 -2
- data/lib/active_support/message_pack/extensions.rb +20 -2
- data/lib/active_support/message_verifier.rb +21 -0
- data/lib/active_support/message_verifiers.rb +57 -3
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +10 -0
- data/lib/active_support/multibyte/chars.rb +14 -4
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +68 -50
- data/lib/active_support/notifications/instrumenter.rb +22 -19
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/number_helper.rb +22 -0
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/railtie.rb +36 -20
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +1 -5
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/tagged_logging.rb +5 -1
- data/lib/active_support/test_case.rb +63 -6
- data/lib/active_support/testing/assertions.rb +113 -27
- data/lib/active_support/testing/constant_stubbing.rb +30 -8
- data/lib/active_support/testing/deprecation.rb +5 -12
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/isolation.rb +19 -9
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +18 -2
- data/lib/active_support/testing/parallelization/worker.rb +4 -2
- data/lib/active_support/testing/parallelization.rb +25 -1
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +11 -6
- data/lib/active_support/time_with_zone.rb +39 -26
- data/lib/active_support/values/time_zone.rb +26 -17
- data/lib/active_support/xml_mini.rb +14 -4
- data/lib/active_support.rb +22 -9
- metadata +31 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/proxy_object.rb +0 -17
- data/lib/active_support/ruby_features.rb +0 -7
- data/lib/active_support/testing/strict_warnings.rb +0 -39
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
module NotificationAssertions
|
|
6
|
+
# Assert a notification was emitted with a given +pattern+ and optional +payload+.
|
|
7
|
+
#
|
|
8
|
+
# You can assert that a notification was emitted by passing a pattern, which accepts
|
|
9
|
+
# either a string or regexp, an optional payload, and a block. While the block
|
|
10
|
+
# is executed, if a matching notification is emitted, the assertion will pass
|
|
11
|
+
# and the notification will be returned.
|
|
12
|
+
#
|
|
13
|
+
# Note that the payload is matched as a subset, meaning that the notification must
|
|
14
|
+
# contain at least the specified keys and values, but may contain additional ones.
|
|
15
|
+
#
|
|
16
|
+
# assert_notification("post.submitted", title: "Cool Post") do
|
|
17
|
+
# post.submit(title: "Cool Post", body: "Cool Body") # => emits matching notification
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# Using the returned notification, you can make more customized assertions.
|
|
21
|
+
#
|
|
22
|
+
# notification = assert_notification("post.submitted", title: "Cool Post") do
|
|
23
|
+
# ActiveSupport::Notifications.instrument("post.submitted", title: "Cool Post", body: Body.new("Cool Body"))
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# assert_instance_of(Body, notification.payload[:body])
|
|
27
|
+
#
|
|
28
|
+
def assert_notification(pattern, payload = nil, &block)
|
|
29
|
+
notifications = capture_notifications(pattern, &block)
|
|
30
|
+
assert_not_empty(notifications, "No #{pattern} notifications were found")
|
|
31
|
+
|
|
32
|
+
return notifications.first if payload.nil?
|
|
33
|
+
|
|
34
|
+
notification = notifications.find { |notification| notification.payload.slice(*payload.keys) == payload }
|
|
35
|
+
assert_not_nil(notification, "No #{pattern} notification with payload #{payload} was found")
|
|
36
|
+
|
|
37
|
+
notification
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Assert the number of notifications emitted with a given +pattern+.
|
|
41
|
+
#
|
|
42
|
+
# You can assert the number of notifications emitted by passing a pattern, which accepts
|
|
43
|
+
# either a string or regexp, a count, and a block. While the block is executed,
|
|
44
|
+
# the number of matching notifications emitted will be counted. After the block's
|
|
45
|
+
# execution completes, the assertion will pass if the count matches.
|
|
46
|
+
#
|
|
47
|
+
# assert_notifications_count("post.submitted", 1) do
|
|
48
|
+
# post.submit(title: "Cool Post") # => emits matching notification
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
def assert_notifications_count(pattern, count, &block)
|
|
52
|
+
actual_count = capture_notifications(pattern, &block).count
|
|
53
|
+
assert_equal(count, actual_count, "Expected #{count} instead of #{actual_count} notifications for #{pattern}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Assert no notifications were emitted for a given +pattern+.
|
|
57
|
+
#
|
|
58
|
+
# You can assert no notifications were emitted by passing a pattern, which accepts
|
|
59
|
+
# either a string or regexp, and a block. While the block is executed, if no
|
|
60
|
+
# matching notifications are emitted, the assertion will pass.
|
|
61
|
+
#
|
|
62
|
+
# assert_no_notifications("post.submitted") do
|
|
63
|
+
# post.destroy # => emits non-matching notification
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
def assert_no_notifications(pattern = nil, &block)
|
|
67
|
+
notifications = capture_notifications(pattern, &block)
|
|
68
|
+
error_message = if pattern
|
|
69
|
+
"Expected no notifications for #{pattern} but found #{notifications.size}"
|
|
70
|
+
else
|
|
71
|
+
"Expected no notifications but found #{notifications.size}"
|
|
72
|
+
end
|
|
73
|
+
assert_empty(notifications, error_message)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Capture emitted notifications, optionally filtered by a +pattern+.
|
|
77
|
+
#
|
|
78
|
+
# You can capture emitted notifications, optionally filtered by a pattern,
|
|
79
|
+
# which accepts either a string or regexp, and a block.
|
|
80
|
+
#
|
|
81
|
+
# notifications = capture_notifications("post.submitted") do
|
|
82
|
+
# post.submit(title: "Cool Post") # => emits matching notification
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
def capture_notifications(pattern = nil, &block)
|
|
86
|
+
notifications = []
|
|
87
|
+
ActiveSupport::Notifications.subscribed(->(n) { notifications << n }, pattern, &block)
|
|
88
|
+
notifications
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -6,12 +6,15 @@ require "drb/unix" unless Gem.win_platform?
|
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
module Testing
|
|
8
8
|
class Parallelization # :nodoc:
|
|
9
|
+
PrerecordResultClass = Struct.new(:name)
|
|
10
|
+
|
|
9
11
|
class Server
|
|
10
12
|
include DRb::DRbUndumped
|
|
11
13
|
|
|
12
14
|
def initialize
|
|
13
15
|
@queue = Queue.new
|
|
14
16
|
@active_workers = Concurrent::Map.new
|
|
17
|
+
@worker_pids = Concurrent::Map.new
|
|
15
18
|
@in_flight = Concurrent::Map.new
|
|
16
19
|
end
|
|
17
20
|
|
|
@@ -21,6 +24,7 @@ module ActiveSupport
|
|
|
21
24
|
@in_flight.delete([result.klass, result.name])
|
|
22
25
|
|
|
23
26
|
reporter.synchronize do
|
|
27
|
+
reporter.prerecord(PrerecordResultClass.new(result.klass), result.name)
|
|
24
28
|
reporter.record(result)
|
|
25
29
|
end
|
|
26
30
|
end
|
|
@@ -37,12 +41,24 @@ module ActiveSupport
|
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
def start_worker(worker_id)
|
|
44
|
+
def start_worker(worker_id, worker_pid)
|
|
41
45
|
@active_workers[worker_id] = true
|
|
46
|
+
@worker_pids[worker_id] = worker_pid
|
|
42
47
|
end
|
|
43
48
|
|
|
44
|
-
def stop_worker(worker_id)
|
|
49
|
+
def stop_worker(worker_id, worker_pid)
|
|
45
50
|
@active_workers.delete(worker_id)
|
|
51
|
+
@worker_pids.delete(worker_id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def remove_dead_workers(dead_pids)
|
|
55
|
+
dead_pids.each do |dead_pid|
|
|
56
|
+
worker_id = @worker_pids.key(dead_pid)
|
|
57
|
+
if worker_id
|
|
58
|
+
@active_workers.delete(worker_id)
|
|
59
|
+
@worker_pids.delete(worker_id)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
46
62
|
end
|
|
47
63
|
|
|
48
64
|
def active_workers?
|
|
@@ -18,7 +18,7 @@ module ActiveSupport
|
|
|
18
18
|
DRb.stop_service
|
|
19
19
|
|
|
20
20
|
@queue = DRbObject.new_with_uri(@url)
|
|
21
|
-
@queue.start_worker(@id)
|
|
21
|
+
@queue.start_worker(@id, Process.pid)
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
after_fork
|
|
@@ -29,7 +29,7 @@ module ActiveSupport
|
|
|
29
29
|
set_process_title("(stopping)")
|
|
30
30
|
|
|
31
31
|
run_cleanup
|
|
32
|
-
@queue.stop_worker(@id)
|
|
32
|
+
@queue.stop_worker(@id, Process.pid)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -78,6 +78,8 @@ module ActiveSupport
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def after_fork
|
|
81
|
+
ActiveSupport::TestCase.parallel_worker_id = @number
|
|
82
|
+
|
|
81
83
|
Parallelization.after_fork_hooks.each do |cb|
|
|
82
84
|
cb.call(@number)
|
|
83
85
|
end
|
|
@@ -9,6 +9,14 @@ require "active_support/testing/parallelization/worker"
|
|
|
9
9
|
module ActiveSupport
|
|
10
10
|
module Testing
|
|
11
11
|
class Parallelization # :nodoc:
|
|
12
|
+
@@before_fork_hooks = []
|
|
13
|
+
|
|
14
|
+
def self.before_fork_hook(&blk)
|
|
15
|
+
@@before_fork_hooks << blk
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
cattr_reader :before_fork_hooks
|
|
19
|
+
|
|
12
20
|
@@after_fork_hooks = []
|
|
13
21
|
|
|
14
22
|
def self.after_fork_hook(&blk)
|
|
@@ -32,7 +40,12 @@ module ActiveSupport
|
|
|
32
40
|
@url = DRb.start_service("drbunix:", @queue_server).uri
|
|
33
41
|
end
|
|
34
42
|
|
|
43
|
+
def before_fork
|
|
44
|
+
Parallelization.before_fork_hooks.each(&:call)
|
|
45
|
+
end
|
|
46
|
+
|
|
35
47
|
def start
|
|
48
|
+
before_fork
|
|
36
49
|
@worker_pool = @worker_count.times.map do |worker|
|
|
37
50
|
Worker.new(worker, @url).start
|
|
38
51
|
end
|
|
@@ -47,8 +60,19 @@ module ActiveSupport
|
|
|
47
60
|
end
|
|
48
61
|
|
|
49
62
|
def shutdown
|
|
63
|
+
dead_worker_pids = @worker_pool.filter_map do |pid|
|
|
64
|
+
Process.waitpid(pid, Process::WNOHANG)
|
|
65
|
+
rescue Errno::ECHILD
|
|
66
|
+
pid
|
|
67
|
+
end
|
|
68
|
+
@queue_server.remove_dead_workers(dead_worker_pids)
|
|
69
|
+
|
|
50
70
|
@queue_server.shutdown
|
|
51
|
-
@worker_pool.each
|
|
71
|
+
@worker_pool.each do |pid|
|
|
72
|
+
Process.waitpid(pid)
|
|
73
|
+
rescue Errno::ECHILD
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
52
76
|
end
|
|
53
77
|
end
|
|
54
78
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
# Warns when a test case does not perform any assertions.
|
|
6
|
+
#
|
|
7
|
+
# This is helpful in detecting broken tests that do not perform intended assertions.
|
|
8
|
+
module TestsWithoutAssertions # :nodoc:
|
|
9
|
+
def after_teardown
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
if assertions.zero? && !skipped? && !error?
|
|
13
|
+
file, line = method(name).source_location
|
|
14
|
+
warn "Test is missing assertions: `#{name}` #{File.expand_path(file)}:#{line}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -163,12 +163,13 @@ module ActiveSupport
|
|
|
163
163
|
now = date_or_time.midnight.to_time
|
|
164
164
|
elsif date_or_time.is_a?(String)
|
|
165
165
|
now = Time.zone.parse(date_or_time)
|
|
166
|
-
elsif with_usec
|
|
167
|
-
now = date_or_time.to_time
|
|
168
166
|
else
|
|
169
|
-
now = date_or_time
|
|
167
|
+
now = date_or_time
|
|
168
|
+
now = now.to_time unless now.is_a?(Time)
|
|
170
169
|
end
|
|
171
170
|
|
|
171
|
+
now = now.change(usec: 0) unless with_usec
|
|
172
|
+
|
|
172
173
|
# +now+ must be in local system timezone, because +Time.at(now)+
|
|
173
174
|
# and +now.to_date+ (see stubs below) will use +now+'s timezone too!
|
|
174
175
|
now = now.getlocal
|
|
@@ -237,12 +238,16 @@ module ActiveSupport
|
|
|
237
238
|
end
|
|
238
239
|
alias_method :unfreeze_time, :travel_back
|
|
239
240
|
|
|
240
|
-
# Calls +travel_to+ with +
|
|
241
|
+
# Calls +travel_to+ with +date_or_time+, which defaults to +Time.now+.
|
|
242
|
+
# Forwards optional <tt>with_usec</tt> argument.
|
|
241
243
|
#
|
|
242
244
|
# Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
|
243
245
|
# freeze_time
|
|
244
246
|
# sleep(1)
|
|
245
247
|
# Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
|
248
|
+
# freeze_time Time.current + 1.day
|
|
249
|
+
# sleep(1)
|
|
250
|
+
# Time.current # => Mon, 10 Jul 2017 15:34:49 EST -05:00
|
|
246
251
|
#
|
|
247
252
|
# This method also accepts a block, which will return the current time back to its original
|
|
248
253
|
# state at the end of the block:
|
|
@@ -253,8 +258,8 @@ module ActiveSupport
|
|
|
253
258
|
# User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
|
254
259
|
# end
|
|
255
260
|
# Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
|
|
256
|
-
def freeze_time(with_usec: false, &block)
|
|
257
|
-
travel_to
|
|
261
|
+
def freeze_time(date_or_time = Time.now, with_usec: false, &block)
|
|
262
|
+
travel_to date_or_time, with_usec: with_usec, &block
|
|
258
263
|
end
|
|
259
264
|
|
|
260
265
|
private
|
|
@@ -49,9 +49,15 @@ module ActiveSupport
|
|
|
49
49
|
attr_reader :time_zone
|
|
50
50
|
|
|
51
51
|
def initialize(utc_time, time_zone, local_time = nil, period = nil)
|
|
52
|
-
@utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
|
|
53
52
|
@time_zone, @time = time_zone, local_time
|
|
54
|
-
|
|
53
|
+
if utc_time
|
|
54
|
+
@utc = transfer_time_values_to_utc_constructor(utc_time)
|
|
55
|
+
@period = period
|
|
56
|
+
else
|
|
57
|
+
@utc = nil
|
|
58
|
+
@period = get_period_and_ensure_valid_local_time(period)
|
|
59
|
+
end
|
|
60
|
+
@is_utc = zone == "UTC" || zone == "UCT"
|
|
55
61
|
end
|
|
56
62
|
|
|
57
63
|
# Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
|
|
@@ -85,7 +91,7 @@ module ActiveSupport
|
|
|
85
91
|
end
|
|
86
92
|
alias_method :getlocal, :localtime
|
|
87
93
|
|
|
88
|
-
# Returns true if the current time is within Daylight Savings Time for the
|
|
94
|
+
# Returns true if the current time is within Daylight Savings \Time for the
|
|
89
95
|
# specified time zone.
|
|
90
96
|
#
|
|
91
97
|
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
|
|
@@ -103,7 +109,7 @@ module ActiveSupport
|
|
|
103
109
|
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
|
|
104
110
|
# Time.zone.now.utc? # => false
|
|
105
111
|
def utc?
|
|
106
|
-
|
|
112
|
+
@is_utc
|
|
107
113
|
end
|
|
108
114
|
alias_method :gmt?, :utc?
|
|
109
115
|
|
|
@@ -136,9 +142,9 @@ module ActiveSupport
|
|
|
136
142
|
|
|
137
143
|
# Returns a string of the object's date, time, zone, and offset from UTC.
|
|
138
144
|
#
|
|
139
|
-
# Time.zone.now.inspect # => "
|
|
145
|
+
# Time.zone.now.inspect # => "2024-11-13 07:00:10.528054960 UTC +00:00"
|
|
140
146
|
def inspect
|
|
141
|
-
"#{time.strftime('%
|
|
147
|
+
"#{time.strftime('%F %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
# Returns a string of the object's date and time in the ISO 8601 standard
|
|
@@ -146,7 +152,13 @@ module ActiveSupport
|
|
|
146
152
|
#
|
|
147
153
|
# Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
|
|
148
154
|
def xmlschema(fraction_digits = 0)
|
|
149
|
-
|
|
155
|
+
if @is_utc
|
|
156
|
+
utc.iso8601(fraction_digits || 0)
|
|
157
|
+
else
|
|
158
|
+
str = time.iso8601(fraction_digits || 0)
|
|
159
|
+
str[-1] = formatted_offset(true, "Z")
|
|
160
|
+
str
|
|
161
|
+
end
|
|
150
162
|
end
|
|
151
163
|
alias_method :iso8601, :xmlschema
|
|
152
164
|
alias_method :rfc3339, :xmlschema
|
|
@@ -157,11 +169,11 @@ module ActiveSupport
|
|
|
157
169
|
# to +false+.
|
|
158
170
|
#
|
|
159
171
|
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
|
|
160
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").
|
|
172
|
+
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").as_json
|
|
161
173
|
# # => "2005-02-01T05:15:10.000-10:00"
|
|
162
174
|
#
|
|
163
175
|
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
|
|
164
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").
|
|
176
|
+
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").as_json
|
|
165
177
|
# # => "2005/02/01 05:15:10 -1000"
|
|
166
178
|
def as_json(options = nil)
|
|
167
179
|
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
|
@@ -215,8 +227,7 @@ module ActiveSupport
|
|
|
215
227
|
elsif formatter = ::Time::DATE_FORMATS[format]
|
|
216
228
|
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
|
217
229
|
else
|
|
218
|
-
|
|
219
|
-
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}"
|
|
230
|
+
to_s
|
|
220
231
|
end
|
|
221
232
|
end
|
|
222
233
|
alias_method :to_formatted_s, :to_fs
|
|
@@ -300,7 +311,8 @@ module ActiveSupport
|
|
|
300
311
|
if duration_of_variable_length?(other)
|
|
301
312
|
method_missing(:+, other)
|
|
302
313
|
else
|
|
303
|
-
result = utc
|
|
314
|
+
result = utc + other
|
|
315
|
+
|
|
304
316
|
result.in_time_zone(time_zone)
|
|
305
317
|
end
|
|
306
318
|
end
|
|
@@ -332,11 +344,11 @@ module ActiveSupport
|
|
|
332
344
|
#
|
|
333
345
|
def -(other)
|
|
334
346
|
if other.acts_like?(:time)
|
|
335
|
-
|
|
347
|
+
getutc - other.getutc
|
|
336
348
|
elsif duration_of_variable_length?(other)
|
|
337
349
|
method_missing(:-, other)
|
|
338
350
|
else
|
|
339
|
-
result = utc
|
|
351
|
+
result = utc - other
|
|
340
352
|
result.in_time_zone(time_zone)
|
|
341
353
|
end
|
|
342
354
|
end
|
|
@@ -375,8 +387,8 @@ module ActiveSupport
|
|
|
375
387
|
#
|
|
376
388
|
# t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15.116992711 EST -05:00
|
|
377
389
|
# t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15.116992711 EST -05:00
|
|
378
|
-
# t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.
|
|
379
|
-
# t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.
|
|
390
|
+
# t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.000000000 EST -05:00
|
|
391
|
+
# t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.000000000 EST -05:00
|
|
380
392
|
# t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
|
|
381
393
|
# t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
|
|
382
394
|
def change(options)
|
|
@@ -479,15 +491,11 @@ module ActiveSupport
|
|
|
479
491
|
@to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
|
|
480
492
|
end
|
|
481
493
|
|
|
482
|
-
# Returns an instance of +Time+, either with the same
|
|
483
|
-
# as +self+ or in the local system timezone
|
|
484
|
-
# of +ActiveSupport.to_time_preserves_timezone+.
|
|
494
|
+
# Returns an instance of +Time+, either with the same timezone as +self+,
|
|
495
|
+
# with the same UTC offset as +self+ or in the local system timezone
|
|
496
|
+
# depending on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
|
485
497
|
def to_time
|
|
486
|
-
|
|
487
|
-
@to_time_with_instance_offset ||= getlocal(utc_offset)
|
|
488
|
-
else
|
|
489
|
-
@to_time_with_system_offset ||= getlocal
|
|
490
|
-
end
|
|
498
|
+
@to_time_with_timezone ||= getlocal(time_zone)
|
|
491
499
|
end
|
|
492
500
|
|
|
493
501
|
# So that +self+ <tt>acts_like?(:time)</tt>.
|
|
@@ -506,6 +514,10 @@ module ActiveSupport
|
|
|
506
514
|
false
|
|
507
515
|
end
|
|
508
516
|
|
|
517
|
+
def present? # :nodoc:
|
|
518
|
+
true
|
|
519
|
+
end
|
|
520
|
+
|
|
509
521
|
def freeze
|
|
510
522
|
# preload instance variables before freezing
|
|
511
523
|
period; utc; time; to_datetime; to_time
|
|
@@ -531,7 +543,6 @@ module ActiveSupport
|
|
|
531
543
|
# Ensure proxy class responds to all methods that underlying time instance
|
|
532
544
|
# responds to.
|
|
533
545
|
def respond_to_missing?(sym, include_priv)
|
|
534
|
-
return false if sym.to_sym == :acts_like_date?
|
|
535
546
|
time.respond_to?(sym, include_priv)
|
|
536
547
|
end
|
|
537
548
|
|
|
@@ -547,7 +558,9 @@ module ActiveSupport
|
|
|
547
558
|
SECONDS_PER_DAY = 86400
|
|
548
559
|
|
|
549
560
|
def incorporate_utc_offset(time, offset)
|
|
550
|
-
if
|
|
561
|
+
if offset.zero?
|
|
562
|
+
time
|
|
563
|
+
elsif time.kind_of?(Date)
|
|
551
564
|
time + Rational(offset, SECONDS_PER_DAY)
|
|
552
565
|
else
|
|
553
566
|
time + offset
|
|
@@ -12,7 +12,7 @@ module ActiveSupport
|
|
|
12
12
|
# * Limit the set of zones provided by TZInfo to a meaningful subset of 134
|
|
13
13
|
# zones.
|
|
14
14
|
# * Retrieve and display zones with a friendlier name
|
|
15
|
-
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
|
|
15
|
+
# (e.g., "Eastern \Time (US & Canada)" instead of "America/New_York").
|
|
16
16
|
# * Lazily load +TZInfo::Timezone+ instances only when they're needed.
|
|
17
17
|
# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
|
|
18
18
|
# +parse+, +at+, and +now+ methods.
|
|
@@ -57,13 +57,14 @@ module ActiveSupport
|
|
|
57
57
|
"Caracas" => "America/Caracas",
|
|
58
58
|
"La Paz" => "America/La_Paz",
|
|
59
59
|
"Santiago" => "America/Santiago",
|
|
60
|
+
"Asuncion" => "America/Asuncion",
|
|
60
61
|
"Newfoundland" => "America/St_Johns",
|
|
61
62
|
"Brasilia" => "America/Sao_Paulo",
|
|
62
63
|
"Buenos Aires" => "America/Argentina/Buenos_Aires",
|
|
63
64
|
"Montevideo" => "America/Montevideo",
|
|
64
65
|
"Georgetown" => "America/Guyana",
|
|
65
66
|
"Puerto Rico" => "America/Puerto_Rico",
|
|
66
|
-
"Greenland" => "America/
|
|
67
|
+
"Greenland" => "America/Nuuk",
|
|
67
68
|
"Mid-Atlantic" => "Atlantic/South_Georgia",
|
|
68
69
|
"Azores" => "Atlantic/Azores",
|
|
69
70
|
"Cape Verde Is." => "Atlantic/Cape_Verde",
|
|
@@ -134,10 +135,10 @@ module ActiveSupport
|
|
|
134
135
|
"Mumbai" => "Asia/Kolkata",
|
|
135
136
|
"New Delhi" => "Asia/Kolkata",
|
|
136
137
|
"Kathmandu" => "Asia/Kathmandu",
|
|
137
|
-
"Astana" => "Asia/Dhaka",
|
|
138
138
|
"Dhaka" => "Asia/Dhaka",
|
|
139
139
|
"Sri Jayawardenepura" => "Asia/Colombo",
|
|
140
140
|
"Almaty" => "Asia/Almaty",
|
|
141
|
+
"Astana" => "Asia/Almaty",
|
|
141
142
|
"Novosibirsk" => "Asia/Novosibirsk",
|
|
142
143
|
"Rangoon" => "Asia/Rangoon",
|
|
143
144
|
"Bangkok" => "Asia/Bangkok",
|
|
@@ -208,9 +209,7 @@ module ActiveSupport
|
|
|
208
209
|
TZInfo::Timezone.get(MAPPING[name] || name)
|
|
209
210
|
end
|
|
210
211
|
|
|
211
|
-
# :
|
|
212
|
-
alias_method :create, :new
|
|
213
|
-
# :startdoc:
|
|
212
|
+
alias_method :create, :new # :nodoc:
|
|
214
213
|
|
|
215
214
|
# Returns a TimeZone instance with the given name, or +nil+ if no
|
|
216
215
|
# such TimeZone instance exists. (This exists to support the use of
|
|
@@ -315,6 +314,12 @@ module ActiveSupport
|
|
|
315
314
|
end
|
|
316
315
|
# :startdoc:
|
|
317
316
|
|
|
317
|
+
# Returns a standard time zone name defined by IANA
|
|
318
|
+
# https://www.iana.org/time-zones
|
|
319
|
+
def standard_name
|
|
320
|
+
MAPPING[name] || name
|
|
321
|
+
end
|
|
322
|
+
|
|
318
323
|
# Returns the offset of this time zone from UTC in seconds.
|
|
319
324
|
def utc_offset
|
|
320
325
|
@utc_offset || tzinfo&.current_period&.base_utc_offset
|
|
@@ -357,7 +362,7 @@ module ActiveSupport
|
|
|
357
362
|
"(GMT#{formatted_offset}) #{name}"
|
|
358
363
|
end
|
|
359
364
|
|
|
360
|
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
365
|
+
# \Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
361
366
|
# of +self+ from given values.
|
|
362
367
|
#
|
|
363
368
|
# Time.zone = 'Hawaii' # => "Hawaii"
|
|
@@ -367,7 +372,7 @@ module ActiveSupport
|
|
|
367
372
|
ActiveSupport::TimeWithZone.new(nil, self, time)
|
|
368
373
|
end
|
|
369
374
|
|
|
370
|
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
375
|
+
# \Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
371
376
|
# of +self+ from number of seconds since the Unix epoch.
|
|
372
377
|
#
|
|
373
378
|
# Time.zone = 'Hawaii' # => "Hawaii"
|
|
@@ -382,7 +387,7 @@ module ActiveSupport
|
|
|
382
387
|
Time.at(*args).utc.in_time_zone(self)
|
|
383
388
|
end
|
|
384
389
|
|
|
385
|
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
390
|
+
# \Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
386
391
|
# of +self+ from an ISO 8601 string.
|
|
387
392
|
#
|
|
388
393
|
# Time.zone = 'Hawaii' # => "Hawaii"
|
|
@@ -434,7 +439,7 @@ module ActiveSupport
|
|
|
434
439
|
raise ArgumentError, "invalid date"
|
|
435
440
|
end
|
|
436
441
|
|
|
437
|
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
442
|
+
# \Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
438
443
|
# of +self+ from parsed string.
|
|
439
444
|
#
|
|
440
445
|
# Time.zone = 'Hawaii' # => "Hawaii"
|
|
@@ -456,7 +461,7 @@ module ActiveSupport
|
|
|
456
461
|
parts_to_time(Date._parse(str, false), now)
|
|
457
462
|
end
|
|
458
463
|
|
|
459
|
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
464
|
+
# \Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
|
460
465
|
# of +self+ from an RFC 3339 string.
|
|
461
466
|
#
|
|
462
467
|
# Time.zone = 'Hawaii' # => "Hawaii"
|
|
@@ -554,15 +559,11 @@ module ActiveSupport
|
|
|
554
559
|
tzinfo.local_to_utc(time, dst)
|
|
555
560
|
end
|
|
556
561
|
|
|
557
|
-
|
|
558
|
-
# instances.
|
|
559
|
-
def period_for_utc(time)
|
|
562
|
+
def period_for_utc(time) # :nodoc:
|
|
560
563
|
tzinfo.period_for_utc(time)
|
|
561
564
|
end
|
|
562
565
|
|
|
563
|
-
|
|
564
|
-
# instances.
|
|
565
|
-
def period_for_local(time, dst = true)
|
|
566
|
+
def period_for_local(time, dst = true) # :nodoc:
|
|
566
567
|
tzinfo.period_for_local(time, dst) { |periods| periods.last }
|
|
567
568
|
end
|
|
568
569
|
|
|
@@ -570,6 +571,14 @@ module ActiveSupport
|
|
|
570
571
|
tzinfo.periods_for_local(time)
|
|
571
572
|
end
|
|
572
573
|
|
|
574
|
+
def abbr(time) # :nodoc:
|
|
575
|
+
tzinfo.abbr(time)
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def dst?(time) # :nodoc:
|
|
579
|
+
tzinfo.dst?(time)
|
|
580
|
+
end
|
|
581
|
+
|
|
573
582
|
def init_with(coder) # :nodoc:
|
|
574
583
|
initialize(coder["name"])
|
|
575
584
|
end
|
|
@@ -12,7 +12,7 @@ module ActiveSupport
|
|
|
12
12
|
# = \XmlMini
|
|
13
13
|
#
|
|
14
14
|
# To use the much faster libxml parser:
|
|
15
|
-
# gem
|
|
15
|
+
# gem "libxml-ruby"
|
|
16
16
|
# XmlMini.backend = 'LibXML'
|
|
17
17
|
module XmlMini
|
|
18
18
|
extend self
|
|
@@ -46,6 +46,7 @@ module ActiveSupport
|
|
|
46
46
|
"Date" => "date",
|
|
47
47
|
"DateTime" => "dateTime",
|
|
48
48
|
"Time" => "dateTime",
|
|
49
|
+
"ActiveSupport::Duration" => "duration",
|
|
49
50
|
"Array" => "array",
|
|
50
51
|
"Hash" => "hash"
|
|
51
52
|
}
|
|
@@ -56,21 +57,24 @@ module ActiveSupport
|
|
|
56
57
|
"symbol" => Proc.new { |symbol| symbol.to_s },
|
|
57
58
|
"date" => Proc.new { |date| date.to_fs(:db) },
|
|
58
59
|
"dateTime" => Proc.new { |time| time.xmlschema },
|
|
60
|
+
"duration" => Proc.new { |duration| duration.iso8601 },
|
|
59
61
|
"binary" => Proc.new { |binary| ::Base64.encode64(binary) },
|
|
60
62
|
"yaml" => Proc.new { |yaml| yaml.to_yaml }
|
|
61
63
|
} unless defined?(FORMATTING)
|
|
62
64
|
|
|
63
|
-
# TODO use regexp instead of Date.parse
|
|
64
65
|
unless defined?(PARSING)
|
|
65
66
|
PARSING = {
|
|
66
67
|
"symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
|
|
67
|
-
"date" => Proc.new { |date| ::Date.
|
|
68
|
+
"date" => Proc.new { |date| ::Date.strptime(date, "%Y-%m-%d") },
|
|
68
69
|
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
|
|
70
|
+
"duration" => Proc.new { |duration| Duration.parse(duration) },
|
|
69
71
|
"integer" => Proc.new { |integer| integer.to_i },
|
|
70
72
|
"float" => Proc.new { |float| float.to_f },
|
|
71
73
|
"decimal" => Proc.new do |number|
|
|
72
74
|
if String === number
|
|
73
75
|
number.to_d
|
|
76
|
+
elsif Float === number
|
|
77
|
+
BigDecimal(number, 0)
|
|
74
78
|
else
|
|
75
79
|
BigDecimal(number)
|
|
76
80
|
end
|
|
@@ -79,6 +83,7 @@ module ActiveSupport
|
|
|
79
83
|
"string" => Proc.new { |string| string.to_s },
|
|
80
84
|
"yaml" => Proc.new { |yaml| YAML.load(yaml) rescue yaml },
|
|
81
85
|
"base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
|
|
86
|
+
"hexBinary" => Proc.new { |bin| _parse_hex_binary(bin) },
|
|
82
87
|
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
|
|
83
88
|
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
|
|
84
89
|
}
|
|
@@ -162,11 +167,12 @@ module ActiveSupport
|
|
|
162
167
|
"#{left}#{middle.tr('_ ', '--')}#{right}"
|
|
163
168
|
end
|
|
164
169
|
|
|
165
|
-
# TODO: Add support for other encodings
|
|
166
170
|
def _parse_binary(bin, entity)
|
|
167
171
|
case entity["encoding"]
|
|
168
172
|
when "base64"
|
|
169
173
|
::Base64.decode64(bin)
|
|
174
|
+
when "hex", "hexBinary"
|
|
175
|
+
_parse_hex_binary(bin)
|
|
170
176
|
else
|
|
171
177
|
bin
|
|
172
178
|
end
|
|
@@ -180,6 +186,10 @@ module ActiveSupport
|
|
|
180
186
|
f
|
|
181
187
|
end
|
|
182
188
|
|
|
189
|
+
def _parse_hex_binary(bin)
|
|
190
|
+
[bin].pack("H*")
|
|
191
|
+
end
|
|
192
|
+
|
|
183
193
|
def current_thread_backend
|
|
184
194
|
IsolatedExecutionState[:xml_mini_backend]
|
|
185
195
|
end
|