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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b24faa7a716ac5041f15e7d350045c0d924f3d2f
4
- data.tar.gz: '062183011cca6201592cac435ae85aa7c17d4125'
3
+ metadata.gz: cdb51a155bf19eb4c458b9e685b69ee9dc14955f
4
+ data.tar.gz: 6e55441daa607f1a1fe428013ad6c4f2dbae9add
5
5
  SHA512:
6
- metadata.gz: 11e55fb810a57d7661ac3b426e5b720a814ba276e93221d4dbc2b35676e3006a58d8ea74b0ecd1603d83e1b520abf27a157ea3a17aa6eb955fdd5ed3d3bce1c0
7
- data.tar.gz: f4cf5759de9351d52827c9070581ea463295d62e83bd345c91dc81b8db4605efb4264dcbad8f88f0e8c8d6b5a3b5952120be6f4bfcc814f22cf1a25884e706b4
6
+ metadata.gz: bc85b3606636ab5930381604d3475a69eadb3df97dca0f8b41f32fe383573e522ccac3d993930946af27649c1a272258db7726c3b7467bbd38af43ff3834a8b5
7
+ data.tar.gz: f67b616745e51658bb4144c1cb4c38f402f7341b2ecbb7a7fdf0e3dfb74244f0566c321b77c8e74ba5904d5474527616cc03cbd2b29551998d24d3d8464b4b4e
@@ -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 `.halt_callback_chains_on_return_false`.
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 duplicating of duration constants through the codebase and
287
+ to avoid duplication of duration constants through the codebase and
71
288
  eliminate creation of intermediate durations.
72
289
 
73
- *Andrey Novikov, Andrew White*
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
- as this can lead to confusing time stubbing.
529
+ as this can lead to confusing time stubbing.
313
530
 
314
- Instead of:
531
+ Instead of:
315
532
 
316
- travel_to 2.days.from_now do
317
- # 2 days from today
318
- travel_to 3.days.from_now do
319
- # 5 days from today
320
- end
321
- end
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
- preferred way to achieve above is:
540
+ preferred way to achieve above is:
324
541
 
325
- travel 2.days do
326
- # 2 days from today
327
- end
542
+ travel 2.days do
543
+ # 2 days from today
544
+ end
328
545
 
329
- travel 5.days do
330
- # 5 days from today
331
- end
546
+ travel 5.days do
547
+ # 5 days from today
548
+ end
332
549
 
333
- *Vipul A M*
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
- rescue Exception
36
- LocalCacheRegistry.set_cache_for(local_cache_key, nil)
37
- raise
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 + sec_fraction),
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
- prepend DateAndTime::Compatibility
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
- prepend DateAndTime::Compatibility
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
@@ -8,7 +8,7 @@ module ActiveSupport
8
8
  MAJOR = 5
9
9
  MINOR = 1
10
10
  TINY = 0
11
- PRE = "beta1"
11
+ PRE = "rc1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
 
22
22
  # Decompresses a gzipped string.
23
23
  def self.decompress(source)
24
- Zlib::GzipReader.new(StringIO.new(source)).read
24
+ Zlib::GzipReader.wrap(StringIO.new(source), &:read)
25
25
  end
26
26
 
27
27
  # Compresses a string using gzip.
@@ -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(?<!['’`])[a-z]/) { |match| match.capitalize }
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] || "aes-256-cbc"
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.beta1
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-02-23 00:00:00.000000000 Z
11
+ date: 2017-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n