activesupport 6.0.0.beta1 → 6.0.1.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 +302 -1
- data/README.rdoc +2 -1
- data/lib/active_support.rb +1 -0
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +5 -1
- data/lib/active_support/cache.rb +5 -5
- data/lib/active_support/cache/file_store.rb +3 -10
- data/lib/active_support/cache/memory_store.rb +4 -2
- data/lib/active_support/cache/redis_cache_store.rb +9 -6
- data/lib/active_support/concern.rb +24 -1
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/class/attribute.rb +10 -15
- data/lib/active_support/core_ext/date_and_time/calculations.rb +0 -30
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +24 -4
- data/lib/active_support/core_ext/hash.rb +1 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +5 -5
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +5 -5
- data/lib/active_support/core_ext/module/delegation.rb +6 -0
- data/lib/active_support/core_ext/object/duplicable.rb +7 -117
- data/lib/active_support/core_ext/range/compare_range.rb +27 -12
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +7 -2
- data/lib/active_support/core_ext/string/output_safety.rb +51 -4
- data/lib/active_support/core_ext/time/calculations.rb +31 -2
- data/lib/active_support/current_attributes.rb +6 -0
- data/lib/active_support/dependencies.rb +41 -5
- data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
- data/lib/active_support/deprecation/method_wrappers.rb +7 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +24 -3
- data/lib/active_support/descendants_tracker.rb +52 -6
- data/lib/active_support/duration.rb +2 -3
- data/lib/active_support/encrypted_file.rb +2 -1
- data/lib/active_support/evented_file_update_checker.rb +14 -2
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +19 -3
- data/lib/active_support/i18n_railtie.rb +2 -1
- data/lib/active_support/inflector/transliterate.rb +43 -14
- data/lib/active_support/logger_thread_safe_level.rb +2 -1
- data/lib/active_support/message_encryptor.rb +1 -1
- data/lib/active_support/message_verifier.rb +1 -1
- data/lib/active_support/notifications.rb +9 -0
- data/lib/active_support/notifications/fanout.rb +60 -13
- data/lib/active_support/notifications/instrumenter.rb +11 -10
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +1 -1
- data/lib/active_support/parameter_filter.rb +6 -1
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/subscriber.rb +55 -6
- data/lib/active_support/testing/parallelization.rb +21 -2
- metadata +27 -7
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
@@ -24,7 +24,7 @@ class Module
|
|
24
24
|
# end
|
25
25
|
# # => NameError: invalid attribute name: 1_Badname
|
26
26
|
#
|
27
|
-
#
|
27
|
+
# To omit the instance reader method, pass
|
28
28
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
29
29
|
#
|
30
30
|
# module HairColors
|
@@ -91,7 +91,7 @@ class Module
|
|
91
91
|
# Person.new.hair_colors = [:blonde, :red]
|
92
92
|
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
93
93
|
#
|
94
|
-
#
|
94
|
+
# To omit the instance writer method, pass
|
95
95
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
96
96
|
#
|
97
97
|
# module HairColors
|
@@ -166,8 +166,8 @@ class Module
|
|
166
166
|
# Citizen.new.hair_colors << :blue
|
167
167
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
168
168
|
#
|
169
|
-
# To
|
170
|
-
# To
|
169
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
170
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
171
171
|
#
|
172
172
|
# module HairColors
|
173
173
|
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
@@ -180,7 +180,7 @@ class Module
|
|
180
180
|
# Person.new.hair_colors = [:brown] # => NoMethodError
|
181
181
|
# Person.new.hair_colors # => NoMethodError
|
182
182
|
#
|
183
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
183
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
184
184
|
#
|
185
185
|
# module HairColors
|
186
186
|
# mattr_accessor :hair_colors, instance_accessor: false
|
@@ -25,7 +25,7 @@ class Module
|
|
25
25
|
# end
|
26
26
|
# # => NameError: invalid attribute name: 1_Badname
|
27
27
|
#
|
28
|
-
#
|
28
|
+
# To omit the instance reader method, pass
|
29
29
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
30
30
|
#
|
31
31
|
# class Current
|
@@ -66,7 +66,7 @@ class Module
|
|
66
66
|
# Current.user = "DHH"
|
67
67
|
# Thread.current[:attr_Current_user] # => "DHH"
|
68
68
|
#
|
69
|
-
#
|
69
|
+
# To omit the instance writer method, pass
|
70
70
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
71
71
|
#
|
72
72
|
# class Current
|
@@ -118,8 +118,8 @@ class Module
|
|
118
118
|
# Customer.user # => "Rafael"
|
119
119
|
# Account.user # => "DHH"
|
120
120
|
#
|
121
|
-
# To
|
122
|
-
# To
|
121
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
122
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
123
123
|
#
|
124
124
|
# class Current
|
125
125
|
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
|
@@ -128,7 +128,7 @@ class Module
|
|
128
128
|
# Current.new.user = "DHH" # => NoMethodError
|
129
129
|
# Current.new.user # => NoMethodError
|
130
130
|
#
|
131
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
131
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
132
132
|
#
|
133
133
|
# class Current
|
134
134
|
# thread_mattr_accessor :user, instance_accessor: false
|
@@ -275,6 +275,11 @@ class Module
|
|
275
275
|
#
|
276
276
|
# The delegated method must be public on the target, otherwise it will
|
277
277
|
# raise +NoMethodError+.
|
278
|
+
#
|
279
|
+
# The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
|
280
|
+
# delegation due to possible interference when calling
|
281
|
+
# <tt>Marshal.dump(object)</tt>, should the delegation target method
|
282
|
+
# of <tt>object</tt> add or remove instance variables.
|
278
283
|
def delegate_missing_to(target)
|
279
284
|
target = target.to_s
|
280
285
|
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
|
@@ -284,6 +289,7 @@ class Module
|
|
284
289
|
# It may look like an oversight, but we deliberately do not pass
|
285
290
|
# +include_private+, because they do not get delegated.
|
286
291
|
|
292
|
+
return false if name == :marshal_dump || name == :_dump
|
287
293
|
#{target}.respond_to?(name) || super
|
288
294
|
end
|
289
295
|
|
@@ -28,96 +28,6 @@ class Object
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
class NilClass
|
32
|
-
begin
|
33
|
-
nil.dup
|
34
|
-
rescue TypeError
|
35
|
-
|
36
|
-
# +nil+ is not duplicable:
|
37
|
-
#
|
38
|
-
# nil.duplicable? # => false
|
39
|
-
# nil.dup # => TypeError: can't dup NilClass
|
40
|
-
def duplicable?
|
41
|
-
false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class FalseClass
|
47
|
-
begin
|
48
|
-
false.dup
|
49
|
-
rescue TypeError
|
50
|
-
|
51
|
-
# +false+ is not duplicable:
|
52
|
-
#
|
53
|
-
# false.duplicable? # => false
|
54
|
-
# false.dup # => TypeError: can't dup FalseClass
|
55
|
-
def duplicable?
|
56
|
-
false
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class TrueClass
|
62
|
-
begin
|
63
|
-
true.dup
|
64
|
-
rescue TypeError
|
65
|
-
|
66
|
-
# +true+ is not duplicable:
|
67
|
-
#
|
68
|
-
# true.duplicable? # => false
|
69
|
-
# true.dup # => TypeError: can't dup TrueClass
|
70
|
-
def duplicable?
|
71
|
-
false
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
class Symbol
|
77
|
-
begin
|
78
|
-
:symbol.dup
|
79
|
-
|
80
|
-
# Some symbols couldn't be duped in Ruby 2.4.0 only, due to a bug.
|
81
|
-
# This feature check catches any regression.
|
82
|
-
"symbol_from_string".to_sym.dup
|
83
|
-
rescue TypeError
|
84
|
-
|
85
|
-
# Symbols are not duplicable:
|
86
|
-
#
|
87
|
-
# :my_symbol.duplicable? # => false
|
88
|
-
# :my_symbol.dup # => TypeError: can't dup Symbol
|
89
|
-
def duplicable?
|
90
|
-
false
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
class Numeric
|
96
|
-
begin
|
97
|
-
1.dup
|
98
|
-
rescue TypeError
|
99
|
-
|
100
|
-
# Numbers are not duplicable:
|
101
|
-
#
|
102
|
-
# 3.duplicable? # => false
|
103
|
-
# 3.dup # => TypeError: can't dup Integer
|
104
|
-
def duplicable?
|
105
|
-
false
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
require "bigdecimal"
|
111
|
-
class BigDecimal
|
112
|
-
# BigDecimals are duplicable:
|
113
|
-
#
|
114
|
-
# BigDecimal("1.2").duplicable? # => true
|
115
|
-
# BigDecimal("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
|
116
|
-
def duplicable?
|
117
|
-
true
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
31
|
class Method
|
122
32
|
# Methods are not duplicable:
|
123
33
|
#
|
@@ -128,32 +38,12 @@ class Method
|
|
128
38
|
end
|
129
39
|
end
|
130
40
|
|
131
|
-
class
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
# Complex(1).duplicable? # => false
|
139
|
-
# Complex(1).dup # => TypeError: can't copy Complex
|
140
|
-
def duplicable?
|
141
|
-
false
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
class Rational
|
147
|
-
begin
|
148
|
-
Rational(1).dup
|
149
|
-
rescue TypeError
|
150
|
-
|
151
|
-
# Rationals are not duplicable:
|
152
|
-
#
|
153
|
-
# Rational(1).duplicable? # => false
|
154
|
-
# Rational(1).dup # => TypeError: can't copy Rational
|
155
|
-
def duplicable?
|
156
|
-
false
|
157
|
-
end
|
41
|
+
class UnboundMethod
|
42
|
+
# Unbound methods are not duplicable:
|
43
|
+
#
|
44
|
+
# method(:puts).unbind.duplicable? # => false
|
45
|
+
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
|
46
|
+
def duplicable?
|
47
|
+
false
|
158
48
|
end
|
159
49
|
end
|
@@ -3,54 +3,69 @@
|
|
3
3
|
module ActiveSupport
|
4
4
|
module CompareWithRange
|
5
5
|
# Extends the default Range#=== to support range comparisons.
|
6
|
-
# (1..5) === (1..5)
|
7
|
-
# (1..5) === (2..3)
|
8
|
-
# (1..5) === (
|
6
|
+
# (1..5) === (1..5) # => true
|
7
|
+
# (1..5) === (2..3) # => true
|
8
|
+
# (1..5) === (1...6) # => true
|
9
|
+
# (1..5) === (2..6) # => false
|
9
10
|
#
|
10
11
|
# The native Range#=== behavior is untouched.
|
11
12
|
# ('a'..'f') === ('c') # => true
|
12
13
|
# (5..9) === (11) # => false
|
14
|
+
#
|
15
|
+
# The given range must be fully bounded, with both start and end.
|
13
16
|
def ===(value)
|
14
17
|
if value.is_a?(::Range)
|
15
18
|
# 1...10 includes 1..9 but it does not include 1..10.
|
19
|
+
# 1..10 includes 1...11 but it does not include 1...12.
|
16
20
|
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
17
|
-
|
21
|
+
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
22
|
+
super(value.first) && (self.end.nil? || value_max.send(operator, last))
|
18
23
|
else
|
19
24
|
super
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
23
28
|
# Extends the default Range#include? to support range comparisons.
|
24
|
-
# (1..5).include?(1..5)
|
25
|
-
# (1..5).include?(2..3)
|
26
|
-
# (1..5).include?(
|
29
|
+
# (1..5).include?(1..5) # => true
|
30
|
+
# (1..5).include?(2..3) # => true
|
31
|
+
# (1..5).include?(1...6) # => true
|
32
|
+
# (1..5).include?(2..6) # => false
|
27
33
|
#
|
28
34
|
# The native Range#include? behavior is untouched.
|
29
35
|
# ('a'..'f').include?('c') # => true
|
30
36
|
# (5..9).include?(11) # => false
|
37
|
+
#
|
38
|
+
# The given range must be fully bounded, with both start and end.
|
31
39
|
def include?(value)
|
32
40
|
if value.is_a?(::Range)
|
33
41
|
# 1...10 includes 1..9 but it does not include 1..10.
|
42
|
+
# 1..10 includes 1...11 but it does not include 1...12.
|
34
43
|
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
35
|
-
|
44
|
+
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
45
|
+
super(value.first) && (self.end.nil? || value_max.send(operator, last))
|
36
46
|
else
|
37
47
|
super
|
38
48
|
end
|
39
49
|
end
|
40
50
|
|
41
51
|
# Extends the default Range#cover? to support range comparisons.
|
42
|
-
# (1..5).cover?(1..5)
|
43
|
-
# (1..5).cover?(2..3)
|
44
|
-
# (1..5).cover?(
|
52
|
+
# (1..5).cover?(1..5) # => true
|
53
|
+
# (1..5).cover?(2..3) # => true
|
54
|
+
# (1..5).cover?(1...6) # => true
|
55
|
+
# (1..5).cover?(2..6) # => false
|
45
56
|
#
|
46
57
|
# The native Range#cover? behavior is untouched.
|
47
58
|
# ('a'..'f').cover?('c') # => true
|
48
59
|
# (5..9).cover?(11) # => false
|
60
|
+
#
|
61
|
+
# The given range must be fully bounded, with both start and end.
|
49
62
|
def cover?(value)
|
50
63
|
if value.is_a?(::Range)
|
51
64
|
# 1...10 covers 1..9 but it does not cover 1..10.
|
65
|
+
# 1..10 covers 1...11 but it does not cover 1...12.
|
52
66
|
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
53
|
-
|
67
|
+
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
68
|
+
super(value.first) && (self.end.nil? || value_max.send(operator, last))
|
54
69
|
else
|
55
70
|
super
|
56
71
|
end
|
@@ -9,9 +9,9 @@ module ActiveSupport
|
|
9
9
|
# (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
|
10
10
|
#
|
11
11
|
def include?(value)
|
12
|
-
if
|
12
|
+
if self.begin.is_a?(TimeWithZone)
|
13
13
|
cover?(value)
|
14
|
-
elsif
|
14
|
+
elsif self.end.is_a?(TimeWithZone)
|
15
15
|
cover?(value)
|
16
16
|
else
|
17
17
|
super
|
@@ -162,6 +162,11 @@ class String
|
|
162
162
|
|
163
163
|
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
|
164
164
|
#
|
165
|
+
# If the optional parameter +locale+ is specified,
|
166
|
+
# the word will be parameterized as a word of that language.
|
167
|
+
# By default, this parameter is set to <tt>nil</tt> and it will use
|
168
|
+
# the configured <tt>I18n.locale</tt>.
|
169
|
+
#
|
165
170
|
# class Person
|
166
171
|
# def to_param
|
167
172
|
# "#{id}-#{name.parameterize}"
|
@@ -187,8 +192,8 @@ class String
|
|
187
192
|
#
|
188
193
|
# <%= link_to(@person.name, person_path) %>
|
189
194
|
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
|
190
|
-
def parameterize(separator: "-", preserve_case: false)
|
191
|
-
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
|
195
|
+
def parameterize(separator: "-", preserve_case: false, locale: nil)
|
196
|
+
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
|
192
197
|
end
|
193
198
|
|
194
199
|
# Creates the name of a table like Rails does for models to table names. This method
|
@@ -135,10 +135,12 @@ module ActiveSupport #:nodoc:
|
|
135
135
|
class SafeBuffer < String
|
136
136
|
UNSAFE_STRING_METHODS = %w(
|
137
137
|
capitalize chomp chop delete delete_prefix delete_suffix
|
138
|
-
downcase
|
139
|
-
|
138
|
+
downcase lstrip next reverse rstrip slice squeeze strip
|
139
|
+
succ swapcase tr tr_s unicode_normalize upcase
|
140
140
|
)
|
141
141
|
|
142
|
+
UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
|
143
|
+
|
142
144
|
alias_method :original_concat, :concat
|
143
145
|
private :original_concat
|
144
146
|
|
@@ -199,14 +201,24 @@ module ActiveSupport #:nodoc:
|
|
199
201
|
super(html_escape_interpolated_argument(value))
|
200
202
|
end
|
201
203
|
|
202
|
-
def []=(
|
203
|
-
|
204
|
+
def []=(*args)
|
205
|
+
if args.count == 3
|
206
|
+
super(args[0], args[1], html_escape_interpolated_argument(args[2]))
|
207
|
+
else
|
208
|
+
super(args[0], html_escape_interpolated_argument(args[1]))
|
209
|
+
end
|
204
210
|
end
|
205
211
|
|
206
212
|
def +(other)
|
207
213
|
dup.concat(other)
|
208
214
|
end
|
209
215
|
|
216
|
+
def *(*)
|
217
|
+
new_safe_buffer = super
|
218
|
+
new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
|
219
|
+
new_safe_buffer
|
220
|
+
end
|
221
|
+
|
210
222
|
def %(args)
|
211
223
|
case args
|
212
224
|
when Hash
|
@@ -249,11 +261,46 @@ module ActiveSupport #:nodoc:
|
|
249
261
|
end
|
250
262
|
end
|
251
263
|
|
264
|
+
UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
|
265
|
+
if unsafe_method.respond_to?(unsafe_method)
|
266
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
267
|
+
def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
|
268
|
+
if block # if block
|
269
|
+
to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
|
270
|
+
set_block_back_references(block, $~) # set_block_back_references(block, $~)
|
271
|
+
block.call(*params) # block.call(*params)
|
272
|
+
} # }
|
273
|
+
else # else
|
274
|
+
to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
|
275
|
+
end # end
|
276
|
+
end # end
|
277
|
+
|
278
|
+
def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
|
279
|
+
@html_safe = false # @html_safe = false
|
280
|
+
if block # if block
|
281
|
+
super(*args) { |*params| # super(*args) { |*params|
|
282
|
+
set_block_back_references(block, $~) # set_block_back_references(block, $~)
|
283
|
+
block.call(*params) # block.call(*params)
|
284
|
+
} # }
|
285
|
+
else # else
|
286
|
+
super # super
|
287
|
+
end # end
|
288
|
+
end # end
|
289
|
+
EOT
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
252
293
|
private
|
253
294
|
|
254
295
|
def html_escape_interpolated_argument(arg)
|
255
296
|
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
|
256
297
|
end
|
298
|
+
|
299
|
+
def set_block_back_references(block, match_data)
|
300
|
+
block.binding.eval("proc { |m| $~ = m }").call(match_data)
|
301
|
+
rescue ArgumentError
|
302
|
+
# Can't create binding from C level Proc
|
303
|
+
end
|
257
304
|
end
|
258
305
|
end
|
259
306
|
|
@@ -170,8 +170,7 @@ class Time
|
|
170
170
|
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
|
171
171
|
end
|
172
172
|
|
173
|
-
d = to_date.advance(options)
|
174
|
-
d = d.gregorian if d.julian?
|
173
|
+
d = to_date.gregorian.advance(options)
|
175
174
|
time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
|
176
175
|
seconds_to_advance = \
|
177
176
|
options.fetch(:seconds, 0) +
|
@@ -312,4 +311,34 @@ class Time
|
|
312
311
|
end
|
313
312
|
alias_method :eql_without_coercion, :eql?
|
314
313
|
alias_method :eql?, :eql_with_coercion
|
314
|
+
|
315
|
+
# Returns a new time the specified number of days ago.
|
316
|
+
def prev_day(days = 1)
|
317
|
+
advance(days: -days)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns a new time the specified number of days in the future.
|
321
|
+
def next_day(days = 1)
|
322
|
+
advance(days: days)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Returns a new time the specified number of months ago.
|
326
|
+
def prev_month(months = 1)
|
327
|
+
advance(months: -months)
|
328
|
+
end
|
329
|
+
|
330
|
+
# Returns a new time the specified number of months in the future.
|
331
|
+
def next_month(months = 1)
|
332
|
+
advance(months: months)
|
333
|
+
end
|
334
|
+
|
335
|
+
# Returns a new time the specified number of years ago.
|
336
|
+
def prev_year(years = 1)
|
337
|
+
advance(years: -years)
|
338
|
+
end
|
339
|
+
|
340
|
+
# Returns a new time the specified number of years in the future.
|
341
|
+
def next_year(years = 1)
|
342
|
+
advance(years: years)
|
343
|
+
end
|
315
344
|
end
|