activesupport 5.2.4.4 → 6.1.1
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 +353 -435
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support.rb +14 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +142 -78
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +32 -20
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +70 -43
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +171 -75
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +14 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +34 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +70 -13
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +16 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +64 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +50 -6
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +32 -17
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- metadata +58 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,39 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# C.descendants # => [B, A, D]
|
21
|
-
def descendants
|
22
|
-
descendants = []
|
23
|
-
ObjectSpace.each_object(singleton_class) do |k|
|
24
|
-
next if k.singleton_class?
|
25
|
-
descendants.unshift k unless k == self
|
26
|
-
end
|
27
|
-
descendants
|
28
|
-
end
|
29
|
-
rescue StandardError # JRuby 9.0.4.0 and earlier
|
30
|
-
def descendants
|
31
|
-
descendants = []
|
32
|
-
ObjectSpace.each_object(Class) do |k|
|
33
|
-
descendants.unshift k if k < self
|
34
|
-
end
|
35
|
-
descendants.uniq!
|
36
|
-
descendants
|
4
|
+
# Returns an array with all classes that are < than its receiver.
|
5
|
+
#
|
6
|
+
# class C; end
|
7
|
+
# C.descendants # => []
|
8
|
+
#
|
9
|
+
# class B < C; end
|
10
|
+
# C.descendants # => [B]
|
11
|
+
#
|
12
|
+
# class A < B; end
|
13
|
+
# C.descendants # => [B, A]
|
14
|
+
#
|
15
|
+
# class D < C; end
|
16
|
+
# C.descendants # => [B, A, D]
|
17
|
+
def descendants
|
18
|
+
ObjectSpace.each_object(singleton_class).reject do |k|
|
19
|
+
k.singleton_class? || k == self
|
37
20
|
end
|
38
21
|
end
|
39
22
|
|
@@ -45,10 +28,6 @@ class Class
|
|
45
28
|
#
|
46
29
|
# Foo.subclasses # => [Bar]
|
47
30
|
def subclasses
|
48
|
-
|
49
|
-
chain.each do |k|
|
50
|
-
subclasses << k unless chain.any? { |c| c > k }
|
51
|
-
end
|
52
|
-
subclasses
|
31
|
+
descendants.select { |descendant| descendant.superclass == self }
|
53
32
|
end
|
54
33
|
end
|
@@ -110,12 +110,13 @@ class Date
|
|
110
110
|
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
|
111
111
|
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
|
112
112
|
def advance(options)
|
113
|
-
options = options.dup
|
114
113
|
d = self
|
115
|
-
|
116
|
-
d = d >> options
|
117
|
-
d = d
|
118
|
-
d = d +
|
114
|
+
|
115
|
+
d = d >> options[:years] * 12 if options[:years]
|
116
|
+
d = d >> options[:months] if options[:months]
|
117
|
+
d = d + options[:weeks] * 7 if options[:weeks]
|
118
|
+
d = d + options[:days] if options[:days]
|
119
|
+
|
119
120
|
d
|
120
121
|
end
|
121
122
|
|
@@ -10,6 +10,7 @@ class Date
|
|
10
10
|
short: "%d %b",
|
11
11
|
long: "%B %d, %Y",
|
12
12
|
db: "%Y-%m-%d",
|
13
|
+
inspect: "%Y-%m-%d",
|
13
14
|
number: "%Y%m%d",
|
14
15
|
long_ordinal: lambda { |date|
|
15
16
|
day_format = ActiveSupport::Inflector.ordinalize(date.day)
|
@@ -80,7 +81,7 @@ class Date
|
|
80
81
|
# If the *application's* timezone is needed, then use +in_time_zone+ instead.
|
81
82
|
def to_time(form = :local)
|
82
83
|
raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
|
83
|
-
::Time.
|
84
|
+
::Time.public_send(form, year, month, day)
|
84
85
|
end
|
85
86
|
|
86
87
|
silence_redefinition_of_method :xmlschema
|
@@ -1,17 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/object/try"
|
4
|
+
require "active_support/core_ext/date_time/conversions"
|
4
5
|
|
5
6
|
module DateAndTime
|
6
7
|
module Calculations
|
7
8
|
DAYS_INTO_WEEK = {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
sunday: 0,
|
10
|
+
monday: 1,
|
11
|
+
tuesday: 2,
|
12
|
+
wednesday: 3,
|
13
|
+
thursday: 4,
|
14
|
+
friday: 5,
|
15
|
+
saturday: 6
|
15
16
|
}
|
16
17
|
WEEKEND_DAYS = [ 6, 0 ]
|
17
18
|
|
@@ -20,26 +21,28 @@ module DateAndTime
|
|
20
21
|
advance(days: -1)
|
21
22
|
end
|
22
23
|
|
23
|
-
# Returns a new date/time the specified number of days ago.
|
24
|
-
def prev_day(days = 1)
|
25
|
-
advance(days: -days)
|
26
|
-
end
|
27
|
-
|
28
24
|
# Returns a new date/time representing tomorrow.
|
29
25
|
def tomorrow
|
30
26
|
advance(days: 1)
|
31
27
|
end
|
32
28
|
|
33
|
-
# Returns a new date/time the specified number of days in the future.
|
34
|
-
def next_day(days = 1)
|
35
|
-
advance(days: days)
|
36
|
-
end
|
37
|
-
|
38
29
|
# Returns true if the date/time is today.
|
39
30
|
def today?
|
40
31
|
to_date == ::Date.current
|
41
32
|
end
|
42
33
|
|
34
|
+
# Returns true if the date/time is tomorrow.
|
35
|
+
def tomorrow?
|
36
|
+
to_date == ::Date.current.tomorrow
|
37
|
+
end
|
38
|
+
alias :next_day? :tomorrow?
|
39
|
+
|
40
|
+
# Returns true if the date/time is yesterday.
|
41
|
+
def yesterday?
|
42
|
+
to_date == ::Date.current.yesterday
|
43
|
+
end
|
44
|
+
alias :prev_day? :yesterday?
|
45
|
+
|
43
46
|
# Returns true if the date/time is in the past.
|
44
47
|
def past?
|
45
48
|
self < self.class.current
|
@@ -60,6 +63,16 @@ module DateAndTime
|
|
60
63
|
!WEEKEND_DAYS.include?(wday)
|
61
64
|
end
|
62
65
|
|
66
|
+
# Returns true if the date/time falls before <tt>date_or_time</tt>.
|
67
|
+
def before?(date_or_time)
|
68
|
+
self < date_or_time
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if the date/time falls after <tt>date_or_time</tt>.
|
72
|
+
def after?(date_or_time)
|
73
|
+
self > date_or_time
|
74
|
+
end
|
75
|
+
|
63
76
|
# Returns a new date/time the specified number of days ago.
|
64
77
|
def days_ago(days)
|
65
78
|
advance(days: -days)
|
@@ -124,7 +137,7 @@ module DateAndTime
|
|
124
137
|
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
|
125
138
|
# now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
|
126
139
|
def beginning_of_quarter
|
127
|
-
first_quarter_month =
|
140
|
+
first_quarter_month = month - (2 + month) % 3
|
128
141
|
beginning_of_month.change(month: first_quarter_month)
|
129
142
|
end
|
130
143
|
alias :at_beginning_of_quarter :beginning_of_quarter
|
@@ -139,7 +152,7 @@ module DateAndTime
|
|
139
152
|
# now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
|
140
153
|
# now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
|
141
154
|
def end_of_quarter
|
142
|
-
last_quarter_month =
|
155
|
+
last_quarter_month = month + (12 - month) % 3
|
143
156
|
beginning_of_month.change(month: last_quarter_month).end_of_month
|
144
157
|
end
|
145
158
|
alias :at_end_of_quarter :end_of_quarter
|
@@ -188,21 +201,11 @@ module DateAndTime
|
|
188
201
|
end
|
189
202
|
end
|
190
203
|
|
191
|
-
# Returns a new date/time the specified number of months in the future.
|
192
|
-
def next_month(months = 1)
|
193
|
-
advance(months: months)
|
194
|
-
end
|
195
|
-
|
196
204
|
# Short-hand for months_since(3)
|
197
205
|
def next_quarter
|
198
206
|
months_since(3)
|
199
207
|
end
|
200
208
|
|
201
|
-
# Returns a new date/time the specified number of years in the future.
|
202
|
-
def next_year(years = 1)
|
203
|
-
advance(years: years)
|
204
|
-
end
|
205
|
-
|
206
209
|
# Returns a new date/time representing the given day in the previous week.
|
207
210
|
# Week is assumed to start on +start_day+, default is
|
208
211
|
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
@@ -223,11 +226,6 @@ module DateAndTime
|
|
223
226
|
end
|
224
227
|
alias_method :last_weekday, :prev_weekday
|
225
228
|
|
226
|
-
# Returns a new date/time the specified number of months ago.
|
227
|
-
def prev_month(months = 1)
|
228
|
-
advance(months: -months)
|
229
|
-
end
|
230
|
-
|
231
229
|
# Short-hand for months_ago(1).
|
232
230
|
def last_month
|
233
231
|
months_ago(1)
|
@@ -239,11 +237,6 @@ module DateAndTime
|
|
239
237
|
end
|
240
238
|
alias_method :last_quarter, :prev_quarter
|
241
239
|
|
242
|
-
# Returns a new date/time the specified number of years ago.
|
243
|
-
def prev_year(years = 1)
|
244
|
-
advance(years: -years)
|
245
|
-
end
|
246
|
-
|
247
240
|
# Short-hand for years_ago(1).
|
248
241
|
def last_year
|
249
242
|
years_ago(1)
|
@@ -253,9 +246,8 @@ module DateAndTime
|
|
253
246
|
# Week is assumed to start on +start_day+, default is
|
254
247
|
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
255
248
|
def days_to_week_start(start_day = Date.beginning_of_week)
|
256
|
-
start_day_number = DAYS_INTO_WEEK
|
257
|
-
|
258
|
-
(current_day_number - start_day_number) % 7
|
249
|
+
start_day_number = DAYS_INTO_WEEK.fetch(start_day)
|
250
|
+
(wday - start_day_number) % 7
|
259
251
|
end
|
260
252
|
|
261
253
|
# Returns a new date/time representing the start of this week on the given day.
|
@@ -336,8 +328,7 @@ module DateAndTime
|
|
336
328
|
# today.next_occurring(:monday) # => Mon, 18 Dec 2017
|
337
329
|
# today.next_occurring(:thursday) # => Thu, 21 Dec 2017
|
338
330
|
def next_occurring(day_of_week)
|
339
|
-
|
340
|
-
from_now = DAYS_INTO_WEEK.fetch(day_of_week) - current_day_number
|
331
|
+
from_now = DAYS_INTO_WEEK.fetch(day_of_week) - wday
|
341
332
|
from_now += 7 unless from_now > 0
|
342
333
|
advance(days: from_now)
|
343
334
|
end
|
@@ -348,8 +339,7 @@ module DateAndTime
|
|
348
339
|
# today.prev_occurring(:monday) # => Mon, 11 Dec 2017
|
349
340
|
# today.prev_occurring(:thursday) # => Thu, 07 Dec 2017
|
350
341
|
def prev_occurring(day_of_week)
|
351
|
-
|
352
|
-
ago = current_day_number - DAYS_INTO_WEEK.fetch(day_of_week)
|
342
|
+
ago = wday - DAYS_INTO_WEEK.fetch(day_of_week)
|
353
343
|
ago += 7 unless ago > 0
|
354
344
|
advance(days: -ago)
|
355
345
|
end
|
@@ -364,7 +354,7 @@ module DateAndTime
|
|
364
354
|
end
|
365
355
|
|
366
356
|
def days_span(day)
|
367
|
-
(DAYS_INTO_WEEK
|
357
|
+
(DAYS_INTO_WEEK.fetch(day) - DAYS_INTO_WEEK.fetch(Date.beginning_of_week)) % 7
|
368
358
|
end
|
369
359
|
|
370
360
|
def copy_time_to(other)
|
@@ -12,5 +12,20 @@ module DateAndTime
|
|
12
12
|
# this behavior, but new apps will have an initializer that sets
|
13
13
|
# this to true, because the new behavior is preferred.
|
14
14
|
mattr_accessor :preserve_timezone, instance_writer: false, default: false
|
15
|
+
|
16
|
+
# Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
|
17
|
+
#
|
18
|
+
# When `true`, it returns local times with an UTC offset, with `false` local
|
19
|
+
# times are returned as UTC.
|
20
|
+
#
|
21
|
+
# # Given this zone:
|
22
|
+
# zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
|
23
|
+
#
|
24
|
+
# # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC:
|
25
|
+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC
|
26
|
+
#
|
27
|
+
# # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset:
|
28
|
+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500
|
29
|
+
mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false
|
15
30
|
end
|
16
31
|
end
|
@@ -110,7 +110,7 @@ class DateTime
|
|
110
110
|
# instance time. Do not use this method in combination with x.months, use
|
111
111
|
# months_since instead!
|
112
112
|
def since(seconds)
|
113
|
-
self + Rational(seconds
|
113
|
+
self + Rational(seconds, 86400)
|
114
114
|
end
|
115
115
|
alias :in :since
|
116
116
|
|
@@ -1,64 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Enumerable
|
4
|
+
INDEX_WITH_DEFAULT = Object.new
|
5
|
+
private_constant :INDEX_WITH_DEFAULT
|
6
|
+
|
4
7
|
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
5
8
|
# when we omit an identity.
|
9
|
+
|
10
|
+
# :stopdoc:
|
11
|
+
|
12
|
+
# We can't use Refinements here because Refinements with Module which will be prepended
|
13
|
+
# doesn't work well https://bugs.ruby-lang.org/issues/13446
|
14
|
+
alias :_original_sum_with_required_identity :sum
|
15
|
+
private :_original_sum_with_required_identity
|
16
|
+
|
17
|
+
# :startdoc:
|
18
|
+
|
19
|
+
# Calculates a sum from the elements.
|
6
20
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
# [5, 15, 10].sum # => 30
|
32
|
-
# ['foo', 'bar'].sum # => "foobar"
|
33
|
-
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
34
|
-
#
|
35
|
-
# The default sum of an empty list is zero. You can override this default:
|
36
|
-
#
|
37
|
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
38
|
-
def sum(identity = nil, &block)
|
39
|
-
if identity
|
40
|
-
_original_sum_with_required_identity(identity, &block)
|
41
|
-
elsif block_given?
|
42
|
-
map(&block).sum(identity)
|
43
|
-
else
|
44
|
-
inject(:+) || 0
|
45
|
-
end
|
46
|
-
end
|
47
|
-
else
|
48
|
-
def sum(identity = nil, &block)
|
49
|
-
if block_given?
|
50
|
-
map(&block).sum(identity)
|
51
|
-
else
|
52
|
-
sum = identity ? inject(identity, :+) : inject(:+)
|
53
|
-
sum || identity || 0
|
54
|
-
end
|
21
|
+
# payments.sum { |p| p.price * p.tax_rate }
|
22
|
+
# payments.sum(&:price)
|
23
|
+
#
|
24
|
+
# The latter is a shortcut for:
|
25
|
+
#
|
26
|
+
# payments.inject(0) { |sum, p| sum + p.price }
|
27
|
+
#
|
28
|
+
# It can also calculate the sum without the use of a block.
|
29
|
+
#
|
30
|
+
# [5, 15, 10].sum # => 30
|
31
|
+
# ['foo', 'bar'].sum # => "foobar"
|
32
|
+
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
33
|
+
#
|
34
|
+
# The default sum of an empty list is zero. You can override this default:
|
35
|
+
#
|
36
|
+
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
37
|
+
def sum(identity = nil, &block)
|
38
|
+
if identity
|
39
|
+
_original_sum_with_required_identity(identity, &block)
|
40
|
+
elsif block_given?
|
41
|
+
map(&block).sum(identity)
|
42
|
+
else
|
43
|
+
inject(:+) || 0
|
55
44
|
end
|
56
45
|
end
|
57
46
|
|
58
|
-
# Convert an enumerable to a hash
|
47
|
+
# Convert an enumerable to a hash, using the block result as the key and the
|
48
|
+
# element as the value.
|
59
49
|
#
|
60
50
|
# people.index_by(&:login)
|
61
51
|
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
52
|
+
#
|
62
53
|
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
63
54
|
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
64
55
|
def index_by
|
@@ -71,6 +62,33 @@ module Enumerable
|
|
71
62
|
end
|
72
63
|
end
|
73
64
|
|
65
|
+
# Convert an enumerable to a hash, using the element as the key and the block
|
66
|
+
# result as the value.
|
67
|
+
#
|
68
|
+
# post = Post.new(title: "hey there", body: "what's up?")
|
69
|
+
#
|
70
|
+
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
71
|
+
# # => { title: "hey there", body: "what's up?" }
|
72
|
+
#
|
73
|
+
# If an argument is passed instead of a block, it will be used as the value
|
74
|
+
# for all elements:
|
75
|
+
#
|
76
|
+
# %i( created_at updated_at ).index_with(Time.now)
|
77
|
+
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
78
|
+
def index_with(default = INDEX_WITH_DEFAULT)
|
79
|
+
if block_given?
|
80
|
+
result = {}
|
81
|
+
each { |elem| result[elem] = yield(elem) }
|
82
|
+
result
|
83
|
+
elsif default != INDEX_WITH_DEFAULT
|
84
|
+
result = {}
|
85
|
+
each { |elem| result[elem] = default }
|
86
|
+
result
|
87
|
+
else
|
88
|
+
to_enum(:index_with) { size if respond_to?(:size) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
74
92
|
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
75
93
|
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
76
94
|
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
@@ -87,24 +105,44 @@ module Enumerable
|
|
87
105
|
end
|
88
106
|
end
|
89
107
|
|
108
|
+
# Returns a new array that includes the passed elements.
|
109
|
+
#
|
110
|
+
# [ 1, 2, 3 ].including(4, 5)
|
111
|
+
# # => [ 1, 2, 3, 4, 5 ]
|
112
|
+
#
|
113
|
+
# ["David", "Rafael"].including %w[ Aaron Todd ]
|
114
|
+
# # => ["David", "Rafael", "Aaron", "Todd"]
|
115
|
+
def including(*elements)
|
116
|
+
to_a.including(*elements)
|
117
|
+
end
|
118
|
+
|
90
119
|
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
91
120
|
# collection does not include the object.
|
92
121
|
def exclude?(object)
|
93
122
|
!include?(object)
|
94
123
|
end
|
95
124
|
|
96
|
-
# Returns a copy of the enumerable
|
125
|
+
# Returns a copy of the enumerable excluding the specified elements.
|
97
126
|
#
|
98
|
-
# ["David", "Rafael", "Aaron", "Todd"].
|
127
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
|
99
128
|
# # => ["David", "Rafael"]
|
100
129
|
#
|
101
|
-
#
|
130
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
|
131
|
+
# # => ["David", "Rafael"]
|
132
|
+
#
|
133
|
+
# {foo: 1, bar: 2, baz: 3}.excluding :bar
|
102
134
|
# # => {foo: 1, baz: 3}
|
103
|
-
def
|
135
|
+
def excluding(*elements)
|
136
|
+
elements.flatten!(1)
|
104
137
|
reject { |element| elements.include?(element) }
|
105
138
|
end
|
106
139
|
|
107
|
-
#
|
140
|
+
# Alias for #excluding.
|
141
|
+
def without(*elements)
|
142
|
+
excluding(*elements)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Extract the given key from each element in the enumerable.
|
108
146
|
#
|
109
147
|
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
110
148
|
# # => ["David", "Rafael", "Aaron"]
|
@@ -115,9 +153,62 @@ module Enumerable
|
|
115
153
|
if keys.many?
|
116
154
|
map { |element| keys.map { |key| element[key] } }
|
117
155
|
else
|
118
|
-
|
156
|
+
key = keys.first
|
157
|
+
map { |element| element[key] }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Extract the given key from the first element in the enumerable.
|
162
|
+
#
|
163
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
|
164
|
+
# # => "David"
|
165
|
+
#
|
166
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
|
167
|
+
# # => [1, "David"]
|
168
|
+
def pick(*keys)
|
169
|
+
return if none?
|
170
|
+
|
171
|
+
if keys.many?
|
172
|
+
keys.map { |key| first[key] }
|
173
|
+
else
|
174
|
+
first[keys.first]
|
119
175
|
end
|
120
176
|
end
|
177
|
+
|
178
|
+
# Returns a new +Array+ without the blank items.
|
179
|
+
# Uses Object#blank? for determining if an item is blank.
|
180
|
+
#
|
181
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
182
|
+
# # => [1, 2, true]
|
183
|
+
#
|
184
|
+
# Set.new([nil, "", 1, 2])
|
185
|
+
# # => [2, 1] (or [1, 2])
|
186
|
+
#
|
187
|
+
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
188
|
+
#
|
189
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
190
|
+
# #=> { b: 1, f: true }
|
191
|
+
def compact_blank
|
192
|
+
reject(&:blank?)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class Hash
|
197
|
+
# Hash#reject has its own definition, so this needs one too.
|
198
|
+
def compact_blank #:nodoc:
|
199
|
+
reject { |_k, v| v.blank? }
|
200
|
+
end
|
201
|
+
|
202
|
+
# Removes all blank values from the +Hash+ in place and returns self.
|
203
|
+
# Uses Object#blank? for determining if a value is blank.
|
204
|
+
#
|
205
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
206
|
+
# h.compact_blank!
|
207
|
+
# # => { b: 1, f: true }
|
208
|
+
def compact_blank!
|
209
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
210
|
+
delete_if { |_k, v| v.blank? }
|
211
|
+
end
|
121
212
|
end
|
122
213
|
|
123
214
|
class Range #:nodoc:
|
@@ -138,27 +229,32 @@ class Range #:nodoc:
|
|
138
229
|
end
|
139
230
|
end
|
140
231
|
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# Using Refinements here in order not to expose our internal method
|
148
|
-
using Module.new {
|
149
|
-
refine Array do
|
150
|
-
alias :orig_sum :sum
|
151
|
-
end
|
152
|
-
}
|
232
|
+
# Using Refinements here in order not to expose our internal method
|
233
|
+
using Module.new {
|
234
|
+
refine Array do
|
235
|
+
alias :orig_sum :sum
|
236
|
+
end
|
237
|
+
}
|
153
238
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
239
|
+
class Array #:nodoc:
|
240
|
+
# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
|
241
|
+
def sum(init = nil, &block)
|
242
|
+
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
243
|
+
init ||= 0
|
244
|
+
orig_sum(init, &block)
|
245
|
+
else
|
246
|
+
super
|
162
247
|
end
|
163
248
|
end
|
249
|
+
|
250
|
+
# Removes all blank elements from the +Array+ in place and returns self.
|
251
|
+
# Uses Object#blank? for determining if an item is blank.
|
252
|
+
#
|
253
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
254
|
+
# a.compact_blank!
|
255
|
+
# # => [1, 2, true]
|
256
|
+
def compact_blank!
|
257
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
258
|
+
delete_if(&:blank?)
|
259
|
+
end
|
164
260
|
end
|