activesupport 5.1.0.beta1 → 5.1.0.rc1
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 +236 -19
- data/lib/active_support/cache/memory_store.rb +2 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -8
- data/lib/active_support/core_ext/date_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_time/compatibility.rb +11 -1
- data/lib/active_support/core_ext/marshal.rb +2 -2
- data/lib/active_support/core_ext/object/with_options.rb +11 -0
- data/lib/active_support/core_ext/time/calculations.rb +23 -0
- data/lib/active_support/core_ext/time/compatibility.rb +10 -1
- data/lib/active_support/core_ext/time/conversions.rb +3 -0
- data/lib/active_support/duration.rb +109 -2
- data/lib/active_support/gem_version.rb +1 -1
- data/lib/active_support/gzip.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +3 -1
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/message_encryptor.rb +6 -1
- data/lib/active_support/time_with_zone.rb +13 -1
- data/lib/active_support/values/time_zone.rb +65 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdb51a155bf19eb4c458b9e685b69ee9dc14955f
|
4
|
+
data.tar.gz: 6e55441daa607f1a1fe428013ad6c4f2dbae9add
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc85b3606636ab5930381604d3475a69eadb3df97dca0f8b41f32fe383573e522ccac3d993930946af27649c1a272258db7726c3b7467bbd38af43ff3834a8b5
|
7
|
+
data.tar.gz: f67b616745e51658bb4144c1cb4c38f402f7341b2ecbb7a7fdf0e3dfb74244f0566c321b77c8e74ba5904d5474527616cc03cbd2b29551998d24d3d8464b4b4e
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,227 @@
|
|
1
|
+
## Rails 5.1.0.rc1 (March 20, 2017) ##
|
2
|
+
|
3
|
+
* Fixed bug in `DateAndTime::Compatibility#to_time` that caused it to
|
4
|
+
raise `RuntimeError: can't modify frozen Time` when called on any frozen `Time`.
|
5
|
+
Properly pass through the frozen `Time` or `ActiveSupport::TimeWithZone` object
|
6
|
+
when calling `#to_time`.
|
7
|
+
|
8
|
+
*Kevin McPhillips* & *Andrew White*
|
9
|
+
|
10
|
+
* Remove implicit coercion deprecation of durations
|
11
|
+
|
12
|
+
In #28204 we deprecated implicit conversion of durations to a numeric which
|
13
|
+
represented the number of seconds in the duration because of unwanted side
|
14
|
+
effects with calculations on durations and dates. This unfortunately had
|
15
|
+
the side effect of forcing a explicit cast when configuring third-party
|
16
|
+
libraries like expiration in Redis, e.g:
|
17
|
+
|
18
|
+
redis.expire("foo", 5.minutes)
|
19
|
+
|
20
|
+
To work around this we've removed the deprecation and added a private class
|
21
|
+
that wraps the numeric and can perform calculation involving durations and
|
22
|
+
ensure that they remain a duration irrespective of the order of operations.
|
23
|
+
|
24
|
+
*Andrew White*
|
25
|
+
|
26
|
+
* Update `titleize` regex to allow apostrophes
|
27
|
+
|
28
|
+
In 4b685aa the regex in `titleize` was updated to not match apostrophes to
|
29
|
+
better reflect the nature of the transformation. Unfortunately, this had the
|
30
|
+
side effect of breaking capitalization on the first word of a sub-string, e.g:
|
31
|
+
|
32
|
+
>> "This was 'fake news'".titleize
|
33
|
+
=> "This Was 'fake News'"
|
34
|
+
|
35
|
+
This is fixed by extending the look-behind to also check for a word
|
36
|
+
character on the other side of the apostrophe.
|
37
|
+
|
38
|
+
Fixes #28312.
|
39
|
+
|
40
|
+
*Andrew White*
|
41
|
+
|
42
|
+
* Add `rfc3339` aliases to `xmlschema` for `Time` and `ActiveSupport::TimeWithZone`
|
43
|
+
|
44
|
+
For naming consistency when using the RFC 3339 profile of ISO 8601 in applications.
|
45
|
+
|
46
|
+
*Andrew White*
|
47
|
+
|
48
|
+
* Add `Time.rfc3339` parsing method
|
49
|
+
|
50
|
+
`Time.xmlschema` and consequently its alias `iso8601` accepts timestamps
|
51
|
+
without a offset in contravention of the RFC 3339 standard. This method
|
52
|
+
enforces that constraint and raises an `ArgumentError` if it doesn't.
|
53
|
+
|
54
|
+
*Andrew White*
|
55
|
+
|
56
|
+
* Add `ActiveSupport::TimeZone.rfc3339` parsing method
|
57
|
+
|
58
|
+
Previously, there was no way to get a RFC 3339 timestamp into a specific
|
59
|
+
timezone without either using `parse` or chaining methods. The new method
|
60
|
+
allows parsing directly into the timezone, e.g:
|
61
|
+
|
62
|
+
>> Time.zone = "Hawaii"
|
63
|
+
=> "Hawaii"
|
64
|
+
>> Time.zone.rfc3339("1999-12-31T14:00:00Z")
|
65
|
+
=> Fri, 31 Dec 1999 14:00:00 HST -10:00
|
66
|
+
|
67
|
+
This new method has stricter semantics than the current `parse` method,
|
68
|
+
and will raise an `ArgumentError` instead of returning nil, e.g:
|
69
|
+
|
70
|
+
>> Time.zone = "Hawaii"
|
71
|
+
=> "Hawaii"
|
72
|
+
>> Time.zone.rfc3339("foobar")
|
73
|
+
ArgumentError: invalid date
|
74
|
+
>> Time.zone.parse("foobar")
|
75
|
+
=> nil
|
76
|
+
|
77
|
+
It will also raise an `ArgumentError` when either the time or offset
|
78
|
+
components are missing, e.g:
|
79
|
+
|
80
|
+
>> Time.zone = "Hawaii"
|
81
|
+
=> "Hawaii"
|
82
|
+
>> Time.zone.rfc3339("1999-12-31")
|
83
|
+
ArgumentError: invalid date
|
84
|
+
>> Time.zone.rfc3339("1999-12-31T14:00:00")
|
85
|
+
ArgumentError: invalid date
|
86
|
+
|
87
|
+
*Andrew White*
|
88
|
+
|
89
|
+
* Add `ActiveSupport::TimeZone.iso8601` parsing method
|
90
|
+
|
91
|
+
Previously, there was no way to get a ISO 8601 timestamp into a specific
|
92
|
+
timezone without either using `parse` or chaining methods. The new method
|
93
|
+
allows parsing directly into the timezone, e.g:
|
94
|
+
|
95
|
+
>> Time.zone = "Hawaii"
|
96
|
+
=> "Hawaii"
|
97
|
+
>> Time.zone.iso8601("1999-12-31T14:00:00Z")
|
98
|
+
=> Fri, 31 Dec 1999 14:00:00 HST -10:00
|
99
|
+
|
100
|
+
If the timestamp is a ISO 8601 date (YYYY-MM-DD), then the time is set
|
101
|
+
to midnight, e.g:
|
102
|
+
|
103
|
+
>> Time.zone = "Hawaii"
|
104
|
+
=> "Hawaii"
|
105
|
+
>> Time.zone.iso8601("1999-12-31")
|
106
|
+
=> Fri, 31 Dec 1999 00:00:00 HST -10:00
|
107
|
+
|
108
|
+
This new method has stricter semantics than the current `parse` method,
|
109
|
+
and will raise an `ArgumentError` instead of returning nil, e.g:
|
110
|
+
|
111
|
+
>> Time.zone = "Hawaii"
|
112
|
+
=> "Hawaii"
|
113
|
+
>> Time.zone.iso8601("foobar")
|
114
|
+
ArgumentError: invalid date
|
115
|
+
>> Time.zone.parse("foobar")
|
116
|
+
=> nil
|
117
|
+
|
118
|
+
*Andrew White*
|
119
|
+
|
120
|
+
* Deprecate implicit coercion of `ActiveSupport::Duration`
|
121
|
+
|
122
|
+
Currently `ActiveSupport::Duration` implicitly converts to a seconds
|
123
|
+
value when used in a calculation except for the explicit examples of
|
124
|
+
addition and subtraction where the duration is the receiver, e.g:
|
125
|
+
|
126
|
+
>> 2 * 1.day
|
127
|
+
=> 172800
|
128
|
+
|
129
|
+
This results in lots of confusion especially when using durations
|
130
|
+
with dates because adding/subtracting a value from a date treats
|
131
|
+
integers as a day and not a second, e.g:
|
132
|
+
|
133
|
+
>> Date.today
|
134
|
+
=> Wed, 01 Mar 2017
|
135
|
+
>> Date.today + 2 * 1.day
|
136
|
+
=> Mon, 10 Apr 2490
|
137
|
+
|
138
|
+
To fix this we're implementing `coerce` so that we can provide a
|
139
|
+
deprecation warning with the intent of removing the implicit coercion
|
140
|
+
in Rails 5.2, e.g:
|
141
|
+
|
142
|
+
>> 2 * 1.day
|
143
|
+
DEPRECATION WARNING: Implicit coercion of ActiveSupport::Duration
|
144
|
+
to a Numeric is deprecated and will raise a TypeError in Rails 5.2.
|
145
|
+
=> 172800
|
146
|
+
|
147
|
+
In Rails 5.2 it will raise `TypeError`, e.g:
|
148
|
+
|
149
|
+
>> 2 * 1.day
|
150
|
+
TypeError: ActiveSupport::Duration can't be coerced into Integer
|
151
|
+
|
152
|
+
This is the same behavior as with other types in Ruby, e.g:
|
153
|
+
|
154
|
+
>> 2 * "foo"
|
155
|
+
TypeError: String can't be coerced into Integer
|
156
|
+
>> "foo" * 2
|
157
|
+
=> "foofoo"
|
158
|
+
|
159
|
+
As part of this deprecation add `*` and `/` methods to `AS::Duration`
|
160
|
+
so that calculations that keep the duration as the receiver work
|
161
|
+
correctly whether the final receiver is a `Date` or `Time`, e.g:
|
162
|
+
|
163
|
+
>> Date.today
|
164
|
+
=> Wed, 01 Mar 2017
|
165
|
+
>> Date.today + 1.day * 2
|
166
|
+
=> Fri, 03 Mar 2017
|
167
|
+
|
168
|
+
Fixes #27457.
|
169
|
+
|
170
|
+
*Andrew White*
|
171
|
+
|
172
|
+
* Update `DateTime#change` to support `:usec` and `:nsec` options.
|
173
|
+
|
174
|
+
Adding support for these options now allows us to update the `DateTime#end_of`
|
175
|
+
methods to match the equivalent `Time#end_of` methods, e.g:
|
176
|
+
|
177
|
+
datetime = DateTime.now.end_of_day
|
178
|
+
datetime.nsec == 999999999 # => true
|
179
|
+
|
180
|
+
Fixes #21424.
|
181
|
+
|
182
|
+
*Dan Moore*, *Andrew White*
|
183
|
+
|
184
|
+
* Add `ActiveSupport::Duration#before` and `#after` as aliases for `#until` and `#since`
|
185
|
+
|
186
|
+
These read more like English and require less mental gymnastics to read and write.
|
187
|
+
|
188
|
+
Before:
|
189
|
+
|
190
|
+
2.weeks.since(customer_start_date)
|
191
|
+
5.days.until(today)
|
192
|
+
|
193
|
+
After:
|
194
|
+
|
195
|
+
2.weeks.after(customer_start_date)
|
196
|
+
5.days.before(today)
|
197
|
+
|
198
|
+
*Nick Johnstone*
|
199
|
+
|
200
|
+
* Soft-deprecated the top-level `HashWithIndifferentAccess` constant.
|
201
|
+
`ActiveSupport::HashWithIndifferentAccess` should be used instead.
|
202
|
+
|
203
|
+
Fixes #28157.
|
204
|
+
|
205
|
+
*Robin Dupret*
|
206
|
+
|
207
|
+
* In Core Extensions, make `MarshalWithAutoloading#load` pass through the second, optional
|
208
|
+
argument for `Marshal#load( source [, proc] )`. This way we don't have to do
|
209
|
+
`Marshal.method(:load).super_method.call(source, proc)` just to be able to pass a proc.
|
210
|
+
|
211
|
+
*Jeff Latz*
|
212
|
+
|
213
|
+
* `ActiveSupport::Gzip.decompress` now checks checksum and length in footer.
|
214
|
+
|
215
|
+
*Dylan Thacker-Smith*
|
216
|
+
|
217
|
+
|
1
218
|
## Rails 5.1.0.beta1 (February 23, 2017) ##
|
2
219
|
|
3
220
|
* Cache `ActiveSupport::TimeWithZone#to_datetime` before freezing.
|
4
221
|
|
5
222
|
*Adam Rice*
|
6
223
|
|
7
|
-
* Deprecate
|
224
|
+
* Deprecate `ActiveSupport.halt_callback_chains_on_return_false`.
|
8
225
|
|
9
226
|
*Rafael Mendonça França*
|
10
227
|
|
@@ -67,10 +284,10 @@
|
|
67
284
|
duration's numeric value isn't used in calculations, only parts are used.
|
68
285
|
|
69
286
|
Methods on `Numeric` like `2.days` now use these predefined durations
|
70
|
-
to avoid
|
287
|
+
to avoid duplication of duration constants through the codebase and
|
71
288
|
eliminate creation of intermediate durations.
|
72
289
|
|
73
|
-
*Andrey Novikov
|
290
|
+
*Andrey Novikov*, *Andrew White*
|
74
291
|
|
75
292
|
* Change return value of `Rational#duplicable?`, `ComplexClass#duplicable?`
|
76
293
|
to false.
|
@@ -309,28 +526,28 @@
|
|
309
526
|
*John Gesimondo*
|
310
527
|
|
311
528
|
* `travel/travel_to` travel time helpers, now raise on nested calls,
|
312
|
-
|
529
|
+
as this can lead to confusing time stubbing.
|
313
530
|
|
314
|
-
|
531
|
+
Instead of:
|
315
532
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
533
|
+
travel_to 2.days.from_now do
|
534
|
+
# 2 days from today
|
535
|
+
travel_to 3.days.from_now do
|
536
|
+
# 5 days from today
|
537
|
+
end
|
538
|
+
end
|
322
539
|
|
323
|
-
|
540
|
+
preferred way to achieve above is:
|
324
541
|
|
325
|
-
|
326
|
-
|
327
|
-
|
542
|
+
travel 2.days do
|
543
|
+
# 2 days from today
|
544
|
+
end
|
328
545
|
|
329
|
-
|
330
|
-
|
331
|
-
|
546
|
+
travel 5.days do
|
547
|
+
# 5 days from today
|
548
|
+
end
|
332
549
|
|
333
|
-
|
550
|
+
*Vipul A M*
|
334
551
|
|
335
552
|
* Support parsing JSON time in ISO8601 local time strings in
|
336
553
|
`ActiveSupport::JSON.decode` when `parse_json_times` is enabled.
|
@@ -28,6 +28,7 @@ module ActiveSupport
|
|
28
28
|
@pruning = false
|
29
29
|
end
|
30
30
|
|
31
|
+
# Delete all data stored in a given cache store.
|
31
32
|
def clear(options = nil)
|
32
33
|
synchronize do
|
33
34
|
@data.clear
|
@@ -83,6 +84,7 @@ module ActiveSupport
|
|
83
84
|
modify_value(name, -amount, options)
|
84
85
|
end
|
85
86
|
|
87
|
+
# Deletes cache entries if the cache key matches a given pattern.
|
86
88
|
def delete_matched(matcher, options = nil)
|
87
89
|
options = merged_options(options)
|
88
90
|
instrument(:delete_matched, matcher.inspect) do
|
@@ -28,13 +28,13 @@ module ActiveSupport
|
|
28
28
|
response[2] = ::Rack::BodyProxy.new(response[2]) do
|
29
29
|
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
|
30
30
|
end
|
31
|
+
cleanup_on_body_close = true
|
31
32
|
response
|
32
33
|
rescue Rack::Utils::InvalidParameterError
|
33
|
-
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
|
34
34
|
[400, {}, []]
|
35
|
-
|
36
|
-
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
|
37
|
-
|
35
|
+
ensure
|
36
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless
|
37
|
+
cleanup_on_body_close
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -10,13 +10,5 @@ module DateAndTime
|
|
10
10
|
# this behavior, but new apps will have an initializer that sets
|
11
11
|
# this to true, because the new behavior is preferred.
|
12
12
|
mattr_accessor(:preserve_timezone, instance_writer: false) { false }
|
13
|
-
|
14
|
-
def to_time
|
15
|
-
if preserve_timezone
|
16
|
-
@_to_time_with_instance_offset ||= getlocal(utc_offset)
|
17
|
-
else
|
18
|
-
@_to_time_with_system_offset ||= getlocal
|
19
|
-
end
|
20
|
-
end
|
21
13
|
end
|
22
14
|
end
|
@@ -47,13 +47,23 @@ class DateTime
|
|
47
47
|
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
|
48
48
|
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
|
49
49
|
def change(options)
|
50
|
+
if new_nsec = options[:nsec]
|
51
|
+
raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec]
|
52
|
+
new_fraction = Rational(new_nsec, 1000000000)
|
53
|
+
else
|
54
|
+
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
|
55
|
+
new_fraction = Rational(new_usec, 1000000)
|
56
|
+
end
|
57
|
+
|
58
|
+
raise ArgumentError, "argument out of range" if new_fraction >= 1
|
59
|
+
|
50
60
|
::DateTime.civil(
|
51
61
|
options.fetch(:year, year),
|
52
62
|
options.fetch(:month, month),
|
53
63
|
options.fetch(:day, day),
|
54
64
|
options.fetch(:hour, hour),
|
55
65
|
options.fetch(:min, options[:hour] ? 0 : min),
|
56
|
-
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec +
|
66
|
+
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) + new_fraction,
|
57
67
|
options.fetch(:offset, offset),
|
58
68
|
options.fetch(:start, start)
|
59
69
|
)
|
@@ -122,7 +132,7 @@ class DateTime
|
|
122
132
|
|
123
133
|
# Returns a new DateTime representing the end of the day (23:59:59).
|
124
134
|
def end_of_day
|
125
|
-
change(hour: 23, min: 59, sec: 59)
|
135
|
+
change(hour: 23, min: 59, sec: 59, usec: Rational(999999999, 1000))
|
126
136
|
end
|
127
137
|
alias :at_end_of_day :end_of_day
|
128
138
|
|
@@ -134,7 +144,7 @@ class DateTime
|
|
134
144
|
|
135
145
|
# Returns a new DateTime representing the end of the hour (hh:59:59).
|
136
146
|
def end_of_hour
|
137
|
-
change(min: 59, sec: 59)
|
147
|
+
change(min: 59, sec: 59, usec: Rational(999999999, 1000))
|
138
148
|
end
|
139
149
|
alias :at_end_of_hour :end_of_hour
|
140
150
|
|
@@ -146,7 +156,7 @@ class DateTime
|
|
146
156
|
|
147
157
|
# Returns a new DateTime representing the end of the minute (hh:mm:59).
|
148
158
|
def end_of_minute
|
149
|
-
change(sec: 59)
|
159
|
+
change(sec: 59, usec: Rational(999999999, 1000))
|
150
160
|
end
|
151
161
|
alias :at_end_of_minute :end_of_minute
|
152
162
|
|
@@ -1,5 +1,15 @@
|
|
1
1
|
require "active_support/core_ext/date_and_time/compatibility"
|
2
2
|
|
3
3
|
class DateTime
|
4
|
-
|
4
|
+
include DateAndTime::Compatibility
|
5
|
+
|
6
|
+
remove_possible_method :to_time
|
7
|
+
|
8
|
+
# Either return an instance of `Time` with the same UTC offset
|
9
|
+
# as +self+ or an instance of `Time` representing the same time
|
10
|
+
# in the the local system timezone depending on the setting of
|
11
|
+
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
12
|
+
def to_time
|
13
|
+
preserve_timezone ? getlocal(utc_offset) : getlocal
|
14
|
+
end
|
5
15
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveSupport
|
2
2
|
module MarshalWithAutoloading # :nodoc:
|
3
|
-
def load(source)
|
4
|
-
super(source)
|
3
|
+
def load(source, proc = nil)
|
4
|
+
super(source, proc)
|
5
5
|
rescue ArgumentError, NameError => exc
|
6
6
|
if exc.message.match(%r|undefined class/module (.+?)(?:::)?\z|)
|
7
7
|
# try loading the class/module
|
@@ -62,6 +62,17 @@ class Object
|
|
62
62
|
#
|
63
63
|
# Hence the inherited default for `if` key is ignored.
|
64
64
|
#
|
65
|
+
# NOTE: You cannot call class methods implicitly inside of with_options.
|
66
|
+
# You can access these methods using the class name instead:
|
67
|
+
#
|
68
|
+
# class Phone < ActiveRecord::Base
|
69
|
+
# enum phone_number_type: [home: 0, office: 1, mobile: 2]
|
70
|
+
#
|
71
|
+
# with_options presence: true do
|
72
|
+
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
65
76
|
def with_options(options, &block)
|
66
77
|
option_merger = ActiveSupport::OptionMerger.new(self, options)
|
67
78
|
block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
|
@@ -53,6 +53,29 @@ class Time
|
|
53
53
|
end
|
54
54
|
alias_method :at_without_coercion, :at
|
55
55
|
alias_method :at, :at_with_coercion
|
56
|
+
|
57
|
+
# Creates a +Time+ instance from an RFC 3339 string.
|
58
|
+
#
|
59
|
+
# Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000
|
60
|
+
#
|
61
|
+
# If the time or offset components are missing then an +ArgumentError+ will be raised.
|
62
|
+
#
|
63
|
+
# Time.rfc3339('1999-12-31') # => ArgumentError: invalid date
|
64
|
+
def rfc3339(str)
|
65
|
+
parts = Date._rfc3339(str)
|
66
|
+
|
67
|
+
raise ArgumentError, "invalid date" if parts.empty?
|
68
|
+
|
69
|
+
Time.new(
|
70
|
+
parts.fetch(:year),
|
71
|
+
parts.fetch(:mon),
|
72
|
+
parts.fetch(:mday),
|
73
|
+
parts.fetch(:hour),
|
74
|
+
parts.fetch(:min),
|
75
|
+
parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
|
76
|
+
parts.fetch(:offset)
|
77
|
+
)
|
78
|
+
end
|
56
79
|
end
|
57
80
|
|
58
81
|
# Returns the number of seconds since 00:00:00.
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require "active_support/core_ext/date_and_time/compatibility"
|
2
|
+
require "active_support/core_ext/module/remove_method"
|
2
3
|
|
3
4
|
class Time
|
4
|
-
|
5
|
+
include DateAndTime::Compatibility
|
6
|
+
|
7
|
+
remove_possible_method :to_time
|
8
|
+
|
9
|
+
# Either return +self+ or the time in the local system timezone depending
|
10
|
+
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
11
|
+
def to_time
|
12
|
+
preserve_timezone ? self : getlocal
|
13
|
+
end
|
5
14
|
end
|
@@ -64,4 +64,7 @@ class Time
|
|
64
64
|
def formatted_offset(colon = true, alternate_utc_string = nil)
|
65
65
|
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
|
66
66
|
end
|
67
|
+
|
68
|
+
# Aliased to +xmlschema+ for compatibility with +DateTime+
|
69
|
+
alias_method :rfc3339, :xmlschema
|
67
70
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
require "active_support/core_ext/array/conversions"
|
2
|
+
require "active_support/core_ext/module/delegation"
|
2
3
|
require "active_support/core_ext/object/acts_like"
|
4
|
+
require "active_support/core_ext/string/filters"
|
5
|
+
require "active_support/deprecation"
|
3
6
|
|
4
7
|
module ActiveSupport
|
5
8
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -7,6 +10,66 @@ module ActiveSupport
|
|
7
10
|
#
|
8
11
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
9
12
|
class Duration
|
13
|
+
class Scalar < Numeric #:nodoc:
|
14
|
+
attr_reader :value
|
15
|
+
delegate :to_i, :to_f, :to_s, to: :value
|
16
|
+
|
17
|
+
def initialize(value)
|
18
|
+
@value = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def coerce(other)
|
22
|
+
[Scalar.new(other), self]
|
23
|
+
end
|
24
|
+
|
25
|
+
def -@
|
26
|
+
Scalar.new(-value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other)
|
30
|
+
if Scalar === other || Duration === other
|
31
|
+
value <=> other.value
|
32
|
+
elsif Numeric === other
|
33
|
+
value <=> other
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def +(other)
|
40
|
+
calculate(:+, other)
|
41
|
+
end
|
42
|
+
|
43
|
+
def -(other)
|
44
|
+
calculate(:-, other)
|
45
|
+
end
|
46
|
+
|
47
|
+
def *(other)
|
48
|
+
calculate(:*, other)
|
49
|
+
end
|
50
|
+
|
51
|
+
def /(other)
|
52
|
+
calculate(:/, other)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def calculate(op, other)
|
57
|
+
if Scalar === other
|
58
|
+
Scalar.new(value.public_send(op, other.value))
|
59
|
+
elsif Duration === other
|
60
|
+
Duration.seconds(value).public_send(op, other)
|
61
|
+
elsif Numeric === other
|
62
|
+
Scalar.new(value.public_send(op, other))
|
63
|
+
else
|
64
|
+
raise_type_error(other)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def raise_type_error(other)
|
69
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
10
73
|
SECONDS_PER_MINUTE = 60
|
11
74
|
SECONDS_PER_HOUR = 3600
|
12
75
|
SECONDS_PER_DAY = 86400
|
@@ -88,6 +151,24 @@ module ActiveSupport
|
|
88
151
|
@parts.default = 0
|
89
152
|
end
|
90
153
|
|
154
|
+
def coerce(other) #:nodoc:
|
155
|
+
if Scalar === other
|
156
|
+
[other, self]
|
157
|
+
else
|
158
|
+
[Scalar.new(other), self]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Compares one Duration with another or a Numeric to this Duration.
|
163
|
+
# Numeric values are treated as seconds.
|
164
|
+
def <=>(other)
|
165
|
+
if Duration === other
|
166
|
+
value <=> other.value
|
167
|
+
elsif Numeric === other
|
168
|
+
value <=> other
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
91
172
|
# Adds another Duration or a Numeric to this Duration. Numeric values
|
92
173
|
# are treated as seconds.
|
93
174
|
def +(other)
|
@@ -109,6 +190,28 @@ module ActiveSupport
|
|
109
190
|
self + (-other)
|
110
191
|
end
|
111
192
|
|
193
|
+
# Multiplies this Duration by a Numeric and returns a new Duration.
|
194
|
+
def *(other)
|
195
|
+
if Scalar === other || Duration === other
|
196
|
+
Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
|
197
|
+
elsif Numeric === other
|
198
|
+
Duration.new(value * other, parts.map { |type, number| [type, number * other] })
|
199
|
+
else
|
200
|
+
raise_type_error(other)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Divides this Duration by a Numeric and returns a new Duration.
|
205
|
+
def /(other)
|
206
|
+
if Scalar === other || Duration === other
|
207
|
+
Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
|
208
|
+
elsif Numeric === other
|
209
|
+
Duration.new(value / other, parts.map { |type, number| [type, number / other] })
|
210
|
+
else
|
211
|
+
raise_type_error(other)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
112
215
|
def -@ #:nodoc:
|
113
216
|
Duration.new(-value, parts.map { |type, number| [type, -number] })
|
114
217
|
end
|
@@ -180,6 +283,7 @@ module ActiveSupport
|
|
180
283
|
sum(1, time)
|
181
284
|
end
|
182
285
|
alias :from_now :since
|
286
|
+
alias :after :since
|
183
287
|
|
184
288
|
# Calculates a new Time or Date that is as far in the past
|
185
289
|
# as this Duration represents.
|
@@ -187,6 +291,7 @@ module ActiveSupport
|
|
187
291
|
sum(-1, time)
|
188
292
|
end
|
189
293
|
alias :until :ago
|
294
|
+
alias :before :ago
|
190
295
|
|
191
296
|
def inspect #:nodoc:
|
192
297
|
parts.
|
@@ -210,8 +315,6 @@ module ActiveSupport
|
|
210
315
|
ISO8601Serializer.new(self, precision: precision).serialize
|
211
316
|
end
|
212
317
|
|
213
|
-
delegate :<=>, to: :value
|
214
|
-
|
215
318
|
private
|
216
319
|
|
217
320
|
def sum(sign, time = ::Time.current)
|
@@ -235,5 +338,9 @@ module ActiveSupport
|
|
235
338
|
def method_missing(method, *args, &block)
|
236
339
|
value.send(method, *args, &block)
|
237
340
|
end
|
341
|
+
|
342
|
+
def raise_type_error(other)
|
343
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
344
|
+
end
|
238
345
|
end
|
239
346
|
end
|
data/lib/active_support/gzip.rb
CHANGED
@@ -270,7 +270,7 @@ module ActiveSupport
|
|
270
270
|
end
|
271
271
|
|
272
272
|
def compact
|
273
|
-
dup.compact!
|
273
|
+
dup.tap(&:compact!)
|
274
274
|
end
|
275
275
|
|
276
276
|
# Convert to a regular hash with string keys.
|
@@ -316,4 +316,6 @@ module ActiveSupport
|
|
316
316
|
end
|
317
317
|
end
|
318
318
|
|
319
|
+
# :stopdoc:
|
320
|
+
|
319
321
|
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
|
@@ -161,7 +161,7 @@ module ActiveSupport
|
|
161
161
|
# titleize('TheManWithoutAPast') # => "The Man Without A Past"
|
162
162
|
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
|
163
163
|
def titleize(word)
|
164
|
-
humanize(underscore(word)).gsub(/\b(
|
164
|
+
humanize(underscore(word)).gsub(/\b(?<!\w['’`])[a-z]/) { |match| match.capitalize }
|
165
165
|
end
|
166
166
|
|
167
167
|
# Creates the name of a table like Rails does for models to table names.
|
@@ -50,6 +50,11 @@ module ActiveSupport
|
|
50
50
|
# key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
|
51
51
|
# derivation function.
|
52
52
|
#
|
53
|
+
# First additional parameter is used as the signature key for +MessageVerifier+.
|
54
|
+
# This allows you to specify keys to encrypt and sign data.
|
55
|
+
#
|
56
|
+
# ActiveSupport::MessageEncryptor.new('secret', 'signature_secret')
|
57
|
+
#
|
53
58
|
# Options:
|
54
59
|
# * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
|
55
60
|
# <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
|
@@ -61,7 +66,7 @@ module ActiveSupport
|
|
61
66
|
sign_secret = signature_key_or_options.first
|
62
67
|
@secret = secret
|
63
68
|
@sign_secret = sign_secret
|
64
|
-
@cipher = options[:cipher] ||
|
69
|
+
@cipher = options[:cipher] || DEFAULT_CIPHER
|
65
70
|
@digest = options[:digest] || "SHA1" unless aead_mode?
|
66
71
|
@verifier = resolve_verifier
|
67
72
|
@serializer = options[:serializer] || Marshal
|
@@ -148,6 +148,7 @@ module ActiveSupport
|
|
148
148
|
"#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
|
149
149
|
end
|
150
150
|
alias_method :iso8601, :xmlschema
|
151
|
+
alias_method :rfc3339, :xmlschema
|
151
152
|
|
152
153
|
# Coerces time to a string for JSON encoding. The default format is ISO 8601.
|
153
154
|
# You can get %Y/%m/%d %H:%M:%S +offset style by setting
|
@@ -410,6 +411,17 @@ module ActiveSupport
|
|
410
411
|
@to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
|
411
412
|
end
|
412
413
|
|
414
|
+
# Returns an instance of +Time+, either with the same UTC offset
|
415
|
+
# as +self+ or in the local system timezone depending on the setting
|
416
|
+
# of +ActiveSupport.to_time_preserves_timezone+.
|
417
|
+
def to_time
|
418
|
+
if preserve_timezone
|
419
|
+
@to_time_with_instance_offset ||= getlocal(utc_offset)
|
420
|
+
else
|
421
|
+
@to_time_with_system_offset ||= getlocal
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
413
425
|
# So that +self+ <tt>acts_like?(:time)</tt>.
|
414
426
|
def acts_like_time?
|
415
427
|
true
|
@@ -428,7 +440,7 @@ module ActiveSupport
|
|
428
440
|
|
429
441
|
def freeze
|
430
442
|
# preload instance variables before freezing
|
431
|
-
period; utc; time; to_datetime
|
443
|
+
period; utc; time; to_datetime; to_time
|
432
444
|
super
|
433
445
|
end
|
434
446
|
|
@@ -339,6 +339,41 @@ module ActiveSupport
|
|
339
339
|
Time.at(secs).utc.in_time_zone(self)
|
340
340
|
end
|
341
341
|
|
342
|
+
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
343
|
+
# of +self+ from an ISO 8601 string.
|
344
|
+
#
|
345
|
+
# Time.zone = 'Hawaii' # => "Hawaii"
|
346
|
+
# Time.zone.iso8601('1999-12-31T14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
|
347
|
+
#
|
348
|
+
# If the time components are missing then they will be set to zero.
|
349
|
+
#
|
350
|
+
# Time.zone = 'Hawaii' # => "Hawaii"
|
351
|
+
# Time.zone.iso8601('1999-12-31') # => Fri, 31 Dec 1999 00:00:00 HST -10:00
|
352
|
+
#
|
353
|
+
# If the string is invalid then an +ArgumentError+ will be raised unlike +parse+
|
354
|
+
# which returns +nil+ when given an invalid date string.
|
355
|
+
def iso8601(str)
|
356
|
+
parts = Date._iso8601(str)
|
357
|
+
|
358
|
+
raise ArgumentError, "invalid date" if parts.empty?
|
359
|
+
|
360
|
+
time = Time.new(
|
361
|
+
parts.fetch(:year),
|
362
|
+
parts.fetch(:mon),
|
363
|
+
parts.fetch(:mday),
|
364
|
+
parts.fetch(:hour, 0),
|
365
|
+
parts.fetch(:min, 0),
|
366
|
+
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
|
367
|
+
parts.fetch(:offset, 0)
|
368
|
+
)
|
369
|
+
|
370
|
+
if parts[:offset]
|
371
|
+
TimeWithZone.new(time.utc, self)
|
372
|
+
else
|
373
|
+
TimeWithZone.new(nil, self, time)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
342
377
|
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
343
378
|
# of +self+ from parsed string.
|
344
379
|
#
|
@@ -359,6 +394,36 @@ module ActiveSupport
|
|
359
394
|
parts_to_time(Date._parse(str, false), now)
|
360
395
|
end
|
361
396
|
|
397
|
+
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
|
398
|
+
# of +self+ from an RFC 3339 string.
|
399
|
+
#
|
400
|
+
# Time.zone = 'Hawaii' # => "Hawaii"
|
401
|
+
# Time.zone.rfc3339('2000-01-01T00:00:00Z') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
|
402
|
+
#
|
403
|
+
# If the time or zone components are missing then an +ArgumentError+ will
|
404
|
+
# be raised. This is much stricter than either +parse+ or +iso8601+ which
|
405
|
+
# allow for missing components.
|
406
|
+
#
|
407
|
+
# Time.zone = 'Hawaii' # => "Hawaii"
|
408
|
+
# Time.zone.rfc3339('1999-12-31') # => ArgumentError: invalid date
|
409
|
+
def rfc3339(str)
|
410
|
+
parts = Date._rfc3339(str)
|
411
|
+
|
412
|
+
raise ArgumentError, "invalid date" if parts.empty?
|
413
|
+
|
414
|
+
time = Time.new(
|
415
|
+
parts.fetch(:year),
|
416
|
+
parts.fetch(:mon),
|
417
|
+
parts.fetch(:mday),
|
418
|
+
parts.fetch(:hour),
|
419
|
+
parts.fetch(:min),
|
420
|
+
parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
|
421
|
+
parts.fetch(:offset)
|
422
|
+
)
|
423
|
+
|
424
|
+
TimeWithZone.new(time.utc, self)
|
425
|
+
end
|
426
|
+
|
362
427
|
# Parses +str+ according to +format+ and returns an ActiveSupport::TimeWithZone.
|
363
428
|
#
|
364
429
|
# Assumes that +str+ is a time in the time zone +self+,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activesupport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.0.
|
4
|
+
version: 5.1.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|