activesupport 8.0.3 → 8.1.0.beta1
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 +237 -175
- data/lib/active_support/backtrace_cleaner.rb +71 -0
- data/lib/active_support/cache/mem_cache_store.rb +13 -13
- data/lib/active_support/cache/redis_cache_store.rb +36 -30
- data/lib/active_support/cache/strategy/local_cache.rb +16 -7
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +69 -6
- data/lib/active_support/configurable.rb +28 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/benchmark.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/erb/util.rb +3 -3
- data/lib/active_support/core_ext/object/json.rb +8 -1
- data/lib/active_support/core_ext/object/to_query.rb +5 -0
- data/lib/active_support/core_ext/range.rb +0 -1
- data/lib/active_support/core_ext/string/multibyte.rb +10 -1
- data/lib/active_support/core_ext/string/output_safety.rb +19 -12
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +13 -10
- data/lib/active_support/deprecation/reporting.rb +4 -2
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/error_reporter.rb +50 -6
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +570 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -1
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/file_update_checker.rb +8 -6
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +27 -7
- data/lib/active_support/i18n_railtie.rb +1 -2
- 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 +7 -13
- data/lib/active_support/json/decoding.rb +2 -2
- data/lib/active_support/json/encoding.rb +103 -14
- data/lib/active_support/log_subscriber.rb +2 -0
- data/lib/active_support/message_encryptors.rb +52 -0
- data/lib/active_support/message_pack/extensions.rb +5 -0
- data/lib/active_support/message_verifiers.rb +52 -0
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/multibyte/chars.rb +8 -1
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/railtie.rb +26 -12
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/test_case.rb +61 -6
- data/lib/active_support/testing/assertions.rb +34 -6
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +217 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/worker.rb +2 -0
- data/lib/active_support/testing/parallelization.rb +13 -0
- data/lib/active_support/testing/tests_without_assertions.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -3
- data/lib/active_support/time_with_zone.rb +19 -5
- data/lib/active_support/values/time_zone.rb +8 -1
- data/lib/active_support/xml_mini.rb +1 -2
- data/lib/active_support.rb +11 -0
- metadata +10 -5
- data/lib/active_support/core_ext/range/each.rb +0 -24
@@ -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
|
@@ -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
|
@@ -11,7 +11,7 @@ module ActiveSupport
|
|
11
11
|
|
12
12
|
if assertions.zero? && !skipped? && !error?
|
13
13
|
file, line = method(name).source_location
|
14
|
-
warn "Test is missing assertions: `#{name}` #{file}:#{line}"
|
14
|
+
warn "Test is missing assertions: `#{name}` #{File.expand_path(file)}:#{line}"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -238,12 +238,16 @@ module ActiveSupport
|
|
238
238
|
end
|
239
239
|
alias_method :unfreeze_time, :travel_back
|
240
240
|
|
241
|
-
# 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.
|
242
243
|
#
|
243
244
|
# Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
244
245
|
# freeze_time
|
245
246
|
# sleep(1)
|
246
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
|
247
251
|
#
|
248
252
|
# This method also accepts a block, which will return the current time back to its original
|
249
253
|
# state at the end of the block:
|
@@ -254,8 +258,8 @@ module ActiveSupport
|
|
254
258
|
# User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
|
255
259
|
# end
|
256
260
|
# Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
|
257
|
-
def freeze_time(with_usec: false, &block)
|
258
|
-
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
|
259
263
|
end
|
260
264
|
|
261
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+.
|
@@ -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
|
|
@@ -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
|
@@ -560,7 +572,9 @@ module ActiveSupport
|
|
560
572
|
SECONDS_PER_DAY = 86400
|
561
573
|
|
562
574
|
def incorporate_utc_offset(time, offset)
|
563
|
-
if
|
575
|
+
if offset.zero?
|
576
|
+
time
|
577
|
+
elsif time.kind_of?(Date)
|
564
578
|
time + Rational(offset, SECONDS_PER_DAY)
|
565
579
|
else
|
566
580
|
time + offset
|
@@ -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",
|
@@ -313,6 +314,12 @@ module ActiveSupport
|
|
313
314
|
end
|
314
315
|
# :startdoc:
|
315
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
|
+
|
316
323
|
# Returns the offset of this time zone from UTC in seconds.
|
317
324
|
def utc_offset
|
318
325
|
@utc_offset || tzinfo&.current_period&.base_utc_offset
|
@@ -62,11 +62,10 @@ module ActiveSupport
|
|
62
62
|
"yaml" => Proc.new { |yaml| yaml.to_yaml }
|
63
63
|
} unless defined?(FORMATTING)
|
64
64
|
|
65
|
-
# TODO use regexp instead of Date.parse
|
66
65
|
unless defined?(PARSING)
|
67
66
|
PARSING = {
|
68
67
|
"symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
|
69
|
-
"date" => Proc.new { |date| ::Date.
|
68
|
+
"date" => Proc.new { |date| ::Date.strptime(date, "%Y-%m-%d") },
|
70
69
|
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
|
71
70
|
"duration" => Proc.new { |duration| Duration.parse(duration) },
|
72
71
|
"integer" => Proc.new { |integer| integer.to_i },
|
data/lib/active_support.rb
CHANGED
@@ -40,12 +40,15 @@ module ActiveSupport
|
|
40
40
|
autoload :CodeGenerator
|
41
41
|
autoload :ActionableError
|
42
42
|
autoload :ConfigurationFile
|
43
|
+
autoload :ContinuousIntegration
|
43
44
|
autoload :CurrentAttributes
|
44
45
|
autoload :Dependencies
|
45
46
|
autoload :DescendantsTracker
|
47
|
+
autoload :Editor
|
46
48
|
autoload :ExecutionWrapper
|
47
49
|
autoload :Executor
|
48
50
|
autoload :ErrorReporter
|
51
|
+
autoload :EventReporter
|
49
52
|
autoload :FileUpdateChecker
|
50
53
|
autoload :EventedFileUpdateChecker
|
51
54
|
autoload :ForkTracker
|
@@ -91,6 +94,10 @@ module ActiveSupport
|
|
91
94
|
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
|
92
95
|
autoload :TestCase
|
93
96
|
|
97
|
+
include Deprecation::DeprecatedConstantAccessor
|
98
|
+
|
99
|
+
deprecate_constant :Configurable, "class_attribute :config, default: {}", deprecator: ActiveSupport.deprecator
|
100
|
+
|
94
101
|
def self.eager_load!
|
95
102
|
super
|
96
103
|
|
@@ -99,10 +106,14 @@ module ActiveSupport
|
|
99
106
|
|
100
107
|
cattr_accessor :test_order # :nodoc:
|
101
108
|
cattr_accessor :test_parallelization_threshold, default: 50 # :nodoc:
|
109
|
+
cattr_accessor :parallelize_test_databases, default: true # :nodoc:
|
102
110
|
|
103
111
|
@error_reporter = ActiveSupport::ErrorReporter.new
|
104
112
|
singleton_class.attr_accessor :error_reporter # :nodoc:
|
105
113
|
|
114
|
+
@event_reporter = ActiveSupport::EventReporter.new
|
115
|
+
singleton_class.attr_accessor :event_reporter # :nodoc:
|
116
|
+
|
106
117
|
def self.cache_format_version
|
107
118
|
Cache.format_version
|
108
119
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activesupport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.
|
4
|
+
version: 8.1.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
@@ -235,6 +235,7 @@ files:
|
|
235
235
|
- lib/active_support/concurrency/share_lock.rb
|
236
236
|
- lib/active_support/configurable.rb
|
237
237
|
- lib/active_support/configuration_file.rb
|
238
|
+
- lib/active_support/continuous_integration.rb
|
238
239
|
- lib/active_support/core_ext.rb
|
239
240
|
- lib/active_support/core_ext/array.rb
|
240
241
|
- lib/active_support/core_ext/array/access.rb
|
@@ -327,7 +328,6 @@ files:
|
|
327
328
|
- lib/active_support/core_ext/range.rb
|
328
329
|
- lib/active_support/core_ext/range/compare_range.rb
|
329
330
|
- lib/active_support/core_ext/range/conversions.rb
|
330
|
-
- lib/active_support/core_ext/range/each.rb
|
331
331
|
- lib/active_support/core_ext/range/overlap.rb
|
332
332
|
- lib/active_support/core_ext/range/sole.rb
|
333
333
|
- lib/active_support/core_ext/regexp.rb
|
@@ -377,11 +377,14 @@ files:
|
|
377
377
|
- lib/active_support/duration.rb
|
378
378
|
- lib/active_support/duration/iso8601_parser.rb
|
379
379
|
- lib/active_support/duration/iso8601_serializer.rb
|
380
|
+
- lib/active_support/editor.rb
|
380
381
|
- lib/active_support/encrypted_configuration.rb
|
381
382
|
- lib/active_support/encrypted_file.rb
|
382
383
|
- lib/active_support/environment_inquirer.rb
|
383
384
|
- lib/active_support/error_reporter.rb
|
384
385
|
- lib/active_support/error_reporter/test_helper.rb
|
386
|
+
- lib/active_support/event_reporter.rb
|
387
|
+
- lib/active_support/event_reporter/test_helper.rb
|
385
388
|
- lib/active_support/evented_file_update_checker.rb
|
386
389
|
- lib/active_support/execution_context.rb
|
387
390
|
- lib/active_support/execution_context/test_helper.rb
|
@@ -466,9 +469,11 @@ files:
|
|
466
469
|
- lib/active_support/testing/declarative.rb
|
467
470
|
- lib/active_support/testing/deprecation.rb
|
468
471
|
- lib/active_support/testing/error_reporter_assertions.rb
|
472
|
+
- lib/active_support/testing/event_reporter_assertions.rb
|
469
473
|
- lib/active_support/testing/file_fixtures.rb
|
470
474
|
- lib/active_support/testing/isolation.rb
|
471
475
|
- lib/active_support/testing/method_call_assertions.rb
|
476
|
+
- lib/active_support/testing/notification_assertions.rb
|
472
477
|
- lib/active_support/testing/parallelization.rb
|
473
478
|
- lib/active_support/testing/parallelization/server.rb
|
474
479
|
- lib/active_support/testing/parallelization/worker.rb
|
@@ -494,10 +499,10 @@ licenses:
|
|
494
499
|
- MIT
|
495
500
|
metadata:
|
496
501
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
497
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.0.
|
498
|
-
documentation_uri: https://api.rubyonrails.org/v8.0.
|
502
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.1.0.beta1/activesupport/CHANGELOG.md
|
503
|
+
documentation_uri: https://api.rubyonrails.org/v8.1.0.beta1/
|
499
504
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
500
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.0.
|
505
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.1.0.beta1/activesupport
|
501
506
|
rubygems_mfa_required: 'true'
|
502
507
|
rdoc_options:
|
503
508
|
- "--encoding"
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "active_support/time_with_zone"
|
4
|
-
|
5
|
-
module ActiveSupport
|
6
|
-
module EachTimeWithZone # :nodoc:
|
7
|
-
def each(&block)
|
8
|
-
ensure_iteration_allowed
|
9
|
-
super
|
10
|
-
end
|
11
|
-
|
12
|
-
def step(n = 1, &block)
|
13
|
-
ensure_iteration_allowed
|
14
|
-
super
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
def ensure_iteration_allowed
|
19
|
-
raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
Range.prepend(ActiveSupport::EachTimeWithZone)
|