activesupport 4.2.11.1 → 5.2.4
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 +399 -440
- data/MIT-LICENSE +2 -2
- data/README.rdoc +4 -5
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +7 -5
- data/lib/active_support/benchmarkable.rb +6 -4
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +41 -35
- data/lib/active_support/cache/mem_cache_store.rb +97 -88
- data/lib/active_support/cache/memory_store.rb +27 -30
- data/lib/active_support/cache/null_store.rb +7 -8
- data/lib/active_support/cache/redis_cache_store.rb +461 -0
- data/lib/active_support/cache/strategy/local_cache.rb +67 -34
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
- data/lib/active_support/cache.rb +287 -196
- data/lib/active_support/callbacks.rb +640 -590
- data/lib/active_support/concern.rb +11 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
- data/lib/active_support/concurrency/share_lock.rb +227 -0
- data/lib/active_support/configurable.rb +8 -5
- data/lib/active_support/core_ext/array/access.rb +29 -1
- data/lib/active_support/core_ext/array/conversions.rb +22 -18
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +11 -18
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +9 -6
- data/lib/active_support/core_ext/benchmark.rb +3 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +41 -22
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
- data/lib/active_support/core_ext/class/subclasses.rb +20 -6
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +11 -9
- data/lib/active_support/core_ext/date/conversions.rb +25 -23
- data/lib/active_support/core_ext/date/zones.rb +4 -2
- data/lib/active_support/core_ext/date.rb +6 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +170 -58
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -3
- data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +36 -18
- data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
- data/lib/active_support/core_ext/date_time.rb +7 -5
- data/lib/active_support/core_ext/digest/uuid.rb +7 -5
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +101 -33
- data/lib/active_support/core_ext/file/atomic.rb +38 -31
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/compact.rb +14 -9
- data/lib/active_support/core_ext/hash/conversions.rb +62 -41
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/except.rb +11 -8
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
- data/lib/active_support/core_ext/hash/keys.rb +33 -27
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -8
- data/lib/active_support/core_ext/hash/transform_values.rb +14 -5
- data/lib/active_support/core_ext/hash.rb +11 -9
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +2 -0
- data/lib/active_support/core_ext/integer/time.rb +11 -18
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
- data/lib/active_support/core_ext/kernel/concern.rb +5 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +6 -5
- data/lib/active_support/core_ext/load_error.rb +3 -22
- data/lib/active_support/core_ext/marshal.rb +8 -8
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
- data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
- data/lib/active_support/core_ext/module/concerning.rb +11 -12
- data/lib/active_support/core_ext/module/delegation.rb +99 -29
- data/lib/active_support/core_ext/module/deprecation.rb +4 -2
- data/lib/active_support/core_ext/module/introspection.rb +9 -9
- data/lib/active_support/core_ext/module/reachable.rb +5 -2
- data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +14 -11
- data/lib/active_support/core_ext/name_error.rb +22 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +78 -81
- data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
- data/lib/active_support/core_ext/numeric/time.rb +35 -23
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +27 -2
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
- data/lib/active_support/core_ext/object/duplicable.rb +41 -14
- data/lib/active_support/core_ext/object/inclusion.rb +5 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +49 -19
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +10 -5
- data/lib/active_support/core_ext/object/try.rb +69 -21
- data/lib/active_support/core_ext/object/with_options.rb +16 -3
- data/lib/active_support/core_ext/object.rb +14 -13
- data/lib/active_support/core_ext/range/compare_range.rb +61 -0
- data/lib/active_support/core_ext/range/conversions.rb +27 -7
- data/lib/active_support/core_ext/range/each.rb +19 -17
- data/lib/active_support/core_ext/range/include_range.rb +2 -22
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +6 -0
- data/lib/active_support/core_ext/securerandom.rb +25 -0
- data/lib/active_support/core_ext/string/access.rb +8 -6
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +7 -4
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +6 -5
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +61 -24
- data/lib/active_support/core_ext/string/inquiry.rb +3 -1
- data/lib/active_support/core_ext/string/multibyte.rb +15 -7
- data/lib/active_support/core_ext/string/output_safety.rb +34 -38
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
- data/lib/active_support/core_ext/string/strip.rb +4 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -2
- data/lib/active_support/core_ext/string.rb +15 -13
- data/lib/active_support/core_ext/time/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +85 -51
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +20 -13
- data/lib/active_support/core_ext/time/zones.rb +41 -7
- data/lib/active_support/core_ext/time.rb +7 -6
- data/lib/active_support/core_ext/uri.rb +6 -8
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/current_attributes.rb +195 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/dependencies.rb +152 -161
- data/lib/active_support/deprecation/behaviors.rb +44 -11
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/instance_delegator.rb +17 -2
- data/lib/active_support/deprecation/method_wrappers.rb +66 -20
- data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
- data/lib/active_support/deprecation/reporting.rb +32 -12
- data/lib/active_support/deprecation.rb +12 -9
- data/lib/active_support/descendants_tracker.rb +2 -0
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration/iso8601_parser.rb +125 -0
- data/lib/active_support/duration/iso8601_serializer.rb +55 -0
- data/lib/active_support/duration.rb +307 -35
- data/lib/active_support/encrypted_configuration.rb +49 -0
- data/lib/active_support/encrypted_file.rb +99 -0
- data/lib/active_support/evented_file_update_checker.rb +205 -0
- data/lib/active_support/execution_wrapper.rb +128 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +63 -37
- data/lib/active_support/gem_version.rb +6 -4
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +123 -28
- data/lib/active_support/i18n.rb +8 -6
- data/lib/active_support/i18n_railtie.rb +37 -13
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector/inflections.rb +61 -12
- data/lib/active_support/inflector/methods.rb +163 -136
- data/lib/active_support/inflector/transliterate.rb +48 -27
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/json/decoding.rb +16 -13
- data/lib/active_support/json/encoding.rb +11 -58
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +25 -25
- data/lib/active_support/lazy_load_hooks.rb +50 -20
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +13 -10
- data/lib/active_support/logger.rb +8 -7
- data/lib/active_support/logger_silence.rb +6 -4
- data/lib/active_support/logger_thread_safe_level.rb +7 -5
- data/lib/active_support/message_encryptor.rb +168 -53
- data/lib/active_support/message_verifier.rb +150 -17
- data/lib/active_support/messages/metadata.rb +71 -0
- data/lib/active_support/messages/rotation_configuration.rb +22 -0
- data/lib/active_support/messages/rotator.rb +56 -0
- data/lib/active_support/multibyte/chars.rb +36 -23
- data/lib/active_support/multibyte/unicode.rb +100 -96
- data/lib/active_support/multibyte.rb +4 -2
- data/lib/active_support/notifications/fanout.rb +11 -9
- data/lib/active_support/notifications/instrumenter.rb +27 -7
- data/lib/active_support/notifications.rb +11 -7
- data/lib/active_support/number_helper/number_converter.rb +13 -11
- data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
- data/lib/active_support/number_helper/rounding_helper.rb +66 -0
- data/lib/active_support/number_helper.rb +94 -68
- data/lib/active_support/option_merger.rb +3 -1
- data/lib/active_support/ordered_hash.rb +6 -4
- data/lib/active_support/ordered_options.rb +23 -5
- data/lib/active_support/per_thread_registry.rb +9 -4
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +16 -8
- data/lib/active_support/railtie.rb +43 -9
- data/lib/active_support/reloader.rb +131 -0
- data/lib/active_support/rescuable.rb +108 -53
- data/lib/active_support/security_utils.rb +15 -11
- data/lib/active_support/string_inquirer.rb +11 -3
- data/lib/active_support/subscriber.rb +21 -16
- data/lib/active_support/tagged_logging.rb +14 -11
- data/lib/active_support/test_case.rb +19 -47
- data/lib/active_support/testing/assertions.rb +137 -20
- data/lib/active_support/testing/autorun.rb +4 -2
- data/lib/active_support/testing/constant_lookup.rb +2 -1
- data/lib/active_support/testing/declarative.rb +3 -1
- data/lib/active_support/testing/deprecation.rb +14 -10
- data/lib/active_support/testing/file_fixtures.rb +36 -0
- data/lib/active_support/testing/isolation.rb +34 -25
- data/lib/active_support/testing/method_call_assertions.rb +43 -0
- data/lib/active_support/testing/setup_and_teardown.rb +13 -8
- data/lib/active_support/testing/stream.rb +44 -0
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +81 -15
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +169 -39
- data/lib/active_support/values/time_zone.rb +196 -61
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini/jdom.rb +116 -114
- data/lib/active_support/xml_mini/libxml.rb +16 -13
- data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
- data/lib/active_support/xml_mini/nokogiri.rb +14 -12
- data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
- data/lib/active_support/xml_mini/rexml.rb +11 -9
- data/lib/active_support/xml_mini.rb +37 -37
- data/lib/active_support.rb +12 -11
- metadata +54 -24
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -86
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
require "active_support/core_ext/regexp"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
class Duration
|
8
|
+
# Parses a string formatted according to ISO 8601 Duration into the hash.
|
9
|
+
#
|
10
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
11
|
+
#
|
12
|
+
# This parser allows negative parts to be present in pattern.
|
13
|
+
class ISO8601Parser # :nodoc:
|
14
|
+
class ParsingError < ::ArgumentError; end
|
15
|
+
|
16
|
+
PERIOD_OR_COMMA = /\.|,/
|
17
|
+
PERIOD = ".".freeze
|
18
|
+
COMMA = ",".freeze
|
19
|
+
|
20
|
+
SIGN_MARKER = /\A\-|\+|/
|
21
|
+
DATE_MARKER = /P/
|
22
|
+
TIME_MARKER = /T/
|
23
|
+
DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
|
24
|
+
TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
|
25
|
+
|
26
|
+
DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days }
|
27
|
+
TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds }
|
28
|
+
|
29
|
+
DATE_COMPONENTS = [:years, :months, :days]
|
30
|
+
TIME_COMPONENTS = [:hours, :minutes, :seconds]
|
31
|
+
|
32
|
+
attr_reader :parts, :scanner
|
33
|
+
attr_accessor :mode, :sign
|
34
|
+
|
35
|
+
def initialize(string)
|
36
|
+
@scanner = StringScanner.new(string)
|
37
|
+
@parts = {}
|
38
|
+
@mode = :start
|
39
|
+
@sign = 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse!
|
43
|
+
while !finished?
|
44
|
+
case mode
|
45
|
+
when :start
|
46
|
+
if scan(SIGN_MARKER)
|
47
|
+
self.sign = (scanner.matched == "-") ? -1 : 1
|
48
|
+
self.mode = :sign
|
49
|
+
else
|
50
|
+
raise_parsing_error
|
51
|
+
end
|
52
|
+
|
53
|
+
when :sign
|
54
|
+
if scan(DATE_MARKER)
|
55
|
+
self.mode = :date
|
56
|
+
else
|
57
|
+
raise_parsing_error
|
58
|
+
end
|
59
|
+
|
60
|
+
when :date
|
61
|
+
if scan(TIME_MARKER)
|
62
|
+
self.mode = :time
|
63
|
+
elsif scan(DATE_COMPONENT)
|
64
|
+
parts[DATE_TO_PART[scanner[2]]] = number * sign
|
65
|
+
else
|
66
|
+
raise_parsing_error
|
67
|
+
end
|
68
|
+
|
69
|
+
when :time
|
70
|
+
if scan(TIME_COMPONENT)
|
71
|
+
parts[TIME_TO_PART[scanner[2]]] = number * sign
|
72
|
+
else
|
73
|
+
raise_parsing_error
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
validate!
|
80
|
+
parts
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def finished?
|
86
|
+
scanner.eos?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Parses number which can be a float with either comma or period.
|
90
|
+
def number
|
91
|
+
PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def scan(pattern)
|
95
|
+
scanner.scan(pattern)
|
96
|
+
end
|
97
|
+
|
98
|
+
def raise_parsing_error(reason = nil)
|
99
|
+
raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
|
100
|
+
end
|
101
|
+
|
102
|
+
# Checks for various semantic errors as stated in ISO 8601 standard.
|
103
|
+
def validate!
|
104
|
+
raise_parsing_error("is empty duration") if parts.empty?
|
105
|
+
|
106
|
+
# Mixing any of Y, M, D with W is invalid.
|
107
|
+
if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
|
108
|
+
raise_parsing_error("mixing weeks with other date parts not allowed")
|
109
|
+
end
|
110
|
+
|
111
|
+
# Specifying an empty T part is invalid.
|
112
|
+
if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
|
113
|
+
raise_parsing_error("time part marker is present but time part is empty")
|
114
|
+
end
|
115
|
+
|
116
|
+
fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
|
117
|
+
unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
|
118
|
+
raise_parsing_error "(only last part can be fractional)"
|
119
|
+
end
|
120
|
+
|
121
|
+
true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
require "active_support/core_ext/hash/transform_values"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
class Duration
|
8
|
+
# Serializes duration to string according to ISO 8601 Duration format.
|
9
|
+
class ISO8601Serializer # :nodoc:
|
10
|
+
def initialize(duration, precision: nil)
|
11
|
+
@duration = duration
|
12
|
+
@precision = precision
|
13
|
+
end
|
14
|
+
|
15
|
+
# Builds and returns output string.
|
16
|
+
def serialize
|
17
|
+
parts, sign = normalize
|
18
|
+
return "PT0S".freeze if parts.empty?
|
19
|
+
|
20
|
+
output = "P".dup
|
21
|
+
output << "#{parts[:years]}Y" if parts.key?(:years)
|
22
|
+
output << "#{parts[:months]}M" if parts.key?(:months)
|
23
|
+
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
24
|
+
output << "#{parts[:days]}D" if parts.key?(:days)
|
25
|
+
time = "".dup
|
26
|
+
time << "#{parts[:hours]}H" if parts.key?(:hours)
|
27
|
+
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
|
28
|
+
if parts.key?(:seconds)
|
29
|
+
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
|
30
|
+
end
|
31
|
+
output << "T#{time}" unless time.empty?
|
32
|
+
"#{sign}#{output}"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Return pair of duration's parts and whole duration sign.
|
38
|
+
# Parts are summarized (as they can become repetitive due to addition, etc).
|
39
|
+
# Zero parts are removed as not significant.
|
40
|
+
# If all parts are negative it will negate all of them and return minus as a sign.
|
41
|
+
def normalize
|
42
|
+
parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
|
43
|
+
p[k] += v unless v.zero?
|
44
|
+
end
|
45
|
+
# If all parts are negative - let's make a negative duration
|
46
|
+
sign = ""
|
47
|
+
if parts.values.all? { |v| v < 0 }
|
48
|
+
sign = "-"
|
49
|
+
parts.transform_values!(&:-@)
|
50
|
+
end
|
51
|
+
[parts, sign]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,5 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/conversions"
|
4
|
+
require "active_support/core_ext/module/delegation"
|
5
|
+
require "active_support/core_ext/object/acts_like"
|
6
|
+
require "active_support/core_ext/string/filters"
|
7
|
+
require "active_support/deprecation"
|
3
8
|
|
4
9
|
module ActiveSupport
|
5
10
|
# Provides accurate date and time measurements using Date#advance and
|
@@ -7,19 +12,237 @@ module ActiveSupport
|
|
7
12
|
#
|
8
13
|
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
9
14
|
class Duration
|
15
|
+
class Scalar < Numeric #:nodoc:
|
16
|
+
attr_reader :value
|
17
|
+
delegate :to_i, :to_f, :to_s, to: :value
|
18
|
+
|
19
|
+
def initialize(value)
|
20
|
+
@value = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def coerce(other)
|
24
|
+
[Scalar.new(other), self]
|
25
|
+
end
|
26
|
+
|
27
|
+
def -@
|
28
|
+
Scalar.new(-value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
if Scalar === other || Duration === other
|
33
|
+
value <=> other.value
|
34
|
+
elsif Numeric === other
|
35
|
+
value <=> other
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def +(other)
|
42
|
+
if Duration === other
|
43
|
+
seconds = value + other.parts[:seconds]
|
44
|
+
new_parts = other.parts.merge(seconds: seconds)
|
45
|
+
new_value = value + other.value
|
46
|
+
|
47
|
+
Duration.new(new_value, new_parts)
|
48
|
+
else
|
49
|
+
calculate(:+, other)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def -(other)
|
54
|
+
if Duration === other
|
55
|
+
seconds = value - other.parts[:seconds]
|
56
|
+
new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
|
57
|
+
new_parts = new_parts.merge(seconds: seconds)
|
58
|
+
new_value = value - other.value
|
59
|
+
|
60
|
+
Duration.new(new_value, new_parts)
|
61
|
+
else
|
62
|
+
calculate(:-, other)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def *(other)
|
67
|
+
if Duration === other
|
68
|
+
new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
|
69
|
+
new_value = value * other.value
|
70
|
+
|
71
|
+
Duration.new(new_value, new_parts)
|
72
|
+
else
|
73
|
+
calculate(:*, other)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def /(other)
|
78
|
+
if Duration === other
|
79
|
+
value / other.value
|
80
|
+
else
|
81
|
+
calculate(:/, other)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def %(other)
|
86
|
+
if Duration === other
|
87
|
+
Duration.build(value % other.value)
|
88
|
+
else
|
89
|
+
calculate(:%, other)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def calculate(op, other)
|
95
|
+
if Scalar === other
|
96
|
+
Scalar.new(value.public_send(op, other.value))
|
97
|
+
elsif Numeric === other
|
98
|
+
Scalar.new(value.public_send(op, other))
|
99
|
+
else
|
100
|
+
raise_type_error(other)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def raise_type_error(other)
|
105
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
SECONDS_PER_MINUTE = 60
|
110
|
+
SECONDS_PER_HOUR = 3600
|
111
|
+
SECONDS_PER_DAY = 86400
|
112
|
+
SECONDS_PER_WEEK = 604800
|
113
|
+
SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
|
114
|
+
SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
|
115
|
+
|
116
|
+
PARTS_IN_SECONDS = {
|
117
|
+
seconds: 1,
|
118
|
+
minutes: SECONDS_PER_MINUTE,
|
119
|
+
hours: SECONDS_PER_HOUR,
|
120
|
+
days: SECONDS_PER_DAY,
|
121
|
+
weeks: SECONDS_PER_WEEK,
|
122
|
+
months: SECONDS_PER_MONTH,
|
123
|
+
years: SECONDS_PER_YEAR
|
124
|
+
}.freeze
|
125
|
+
|
126
|
+
PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
|
127
|
+
|
10
128
|
attr_accessor :value, :parts
|
11
129
|
|
130
|
+
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
|
131
|
+
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
|
132
|
+
|
133
|
+
class << self
|
134
|
+
# Creates a new Duration from string formatted according to ISO 8601 Duration.
|
135
|
+
#
|
136
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
137
|
+
# This method allows negative parts to be present in pattern.
|
138
|
+
# If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
|
139
|
+
def parse(iso8601duration)
|
140
|
+
parts = ISO8601Parser.new(iso8601duration).parse!
|
141
|
+
new(calculate_total_seconds(parts), parts)
|
142
|
+
end
|
143
|
+
|
144
|
+
def ===(other) #:nodoc:
|
145
|
+
other.is_a?(Duration)
|
146
|
+
rescue ::NoMethodError
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
def seconds(value) #:nodoc:
|
151
|
+
new(value, [[:seconds, value]])
|
152
|
+
end
|
153
|
+
|
154
|
+
def minutes(value) #:nodoc:
|
155
|
+
new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
|
156
|
+
end
|
157
|
+
|
158
|
+
def hours(value) #:nodoc:
|
159
|
+
new(value * SECONDS_PER_HOUR, [[:hours, value]])
|
160
|
+
end
|
161
|
+
|
162
|
+
def days(value) #:nodoc:
|
163
|
+
new(value * SECONDS_PER_DAY, [[:days, value]])
|
164
|
+
end
|
165
|
+
|
166
|
+
def weeks(value) #:nodoc:
|
167
|
+
new(value * SECONDS_PER_WEEK, [[:weeks, value]])
|
168
|
+
end
|
169
|
+
|
170
|
+
def months(value) #:nodoc:
|
171
|
+
new(value * SECONDS_PER_MONTH, [[:months, value]])
|
172
|
+
end
|
173
|
+
|
174
|
+
def years(value) #:nodoc:
|
175
|
+
new(value * SECONDS_PER_YEAR, [[:years, value]])
|
176
|
+
end
|
177
|
+
|
178
|
+
# Creates a new Duration from a seconds value that is converted
|
179
|
+
# to the individual parts:
|
180
|
+
#
|
181
|
+
# ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
|
182
|
+
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
|
183
|
+
#
|
184
|
+
def build(value)
|
185
|
+
parts = {}
|
186
|
+
remainder = value.to_f
|
187
|
+
|
188
|
+
PARTS.each do |part|
|
189
|
+
unless part == :seconds
|
190
|
+
part_in_seconds = PARTS_IN_SECONDS[part]
|
191
|
+
parts[part] = remainder.div(part_in_seconds)
|
192
|
+
remainder = (remainder % part_in_seconds).round(9)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
parts[:seconds] = remainder
|
197
|
+
|
198
|
+
new(value, parts)
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def calculate_total_seconds(parts)
|
204
|
+
parts.inject(0) do |total, (part, value)|
|
205
|
+
total + value * PARTS_IN_SECONDS[part]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
12
210
|
def initialize(value, parts) #:nodoc:
|
13
|
-
@value, @parts = value, parts
|
211
|
+
@value, @parts = value, parts.to_h
|
212
|
+
@parts.default = 0
|
213
|
+
@parts.reject! { |k, v| v.zero? }
|
214
|
+
end
|
215
|
+
|
216
|
+
def coerce(other) #:nodoc:
|
217
|
+
if Scalar === other
|
218
|
+
[other, self]
|
219
|
+
else
|
220
|
+
[Scalar.new(other), self]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Compares one Duration with another or a Numeric to this Duration.
|
225
|
+
# Numeric values are treated as seconds.
|
226
|
+
def <=>(other)
|
227
|
+
if Duration === other
|
228
|
+
value <=> other.value
|
229
|
+
elsif Numeric === other
|
230
|
+
value <=> other
|
231
|
+
end
|
14
232
|
end
|
15
233
|
|
16
234
|
# Adds another Duration or a Numeric to this Duration. Numeric values
|
17
235
|
# are treated as seconds.
|
18
236
|
def +(other)
|
19
237
|
if Duration === other
|
20
|
-
|
238
|
+
parts = @parts.dup
|
239
|
+
other.parts.each do |(key, value)|
|
240
|
+
parts[key] += value
|
241
|
+
end
|
242
|
+
Duration.new(value + other.value, parts)
|
21
243
|
else
|
22
|
-
|
244
|
+
seconds = @parts[:seconds] + other
|
245
|
+
Duration.new(value + other, @parts.merge(seconds: seconds))
|
23
246
|
end
|
24
247
|
end
|
25
248
|
|
@@ -29,8 +252,44 @@ module ActiveSupport
|
|
29
252
|
self + (-other)
|
30
253
|
end
|
31
254
|
|
255
|
+
# Multiplies this Duration by a Numeric and returns a new Duration.
|
256
|
+
def *(other)
|
257
|
+
if Scalar === other || Duration === other
|
258
|
+
Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
|
259
|
+
elsif Numeric === other
|
260
|
+
Duration.new(value * other, parts.map { |type, number| [type, number * other] })
|
261
|
+
else
|
262
|
+
raise_type_error(other)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Divides this Duration by a Numeric and returns a new Duration.
|
267
|
+
def /(other)
|
268
|
+
if Scalar === other
|
269
|
+
Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
|
270
|
+
elsif Duration === other
|
271
|
+
value / other.value
|
272
|
+
elsif Numeric === other
|
273
|
+
Duration.new(value / other, parts.map { |type, number| [type, number / other] })
|
274
|
+
else
|
275
|
+
raise_type_error(other)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns the modulo of this Duration by another Duration or Numeric.
|
280
|
+
# Numeric values are treated as seconds.
|
281
|
+
def %(other)
|
282
|
+
if Duration === other || Scalar === other
|
283
|
+
Duration.build(value % other.value)
|
284
|
+
elsif Numeric === other
|
285
|
+
Duration.build(value % other)
|
286
|
+
else
|
287
|
+
raise_type_error(other)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
32
291
|
def -@ #:nodoc:
|
33
|
-
Duration.new(-value, parts.map { |type,number| [type, -number] })
|
292
|
+
Duration.new(-value, parts.map { |type, number| [type, -number] })
|
34
293
|
end
|
35
294
|
|
36
295
|
def is_a?(klass) #:nodoc:
|
@@ -52,6 +311,10 @@ module ActiveSupport
|
|
52
311
|
end
|
53
312
|
end
|
54
313
|
|
314
|
+
# Returns the amount of seconds a duration covers as a string.
|
315
|
+
# For more information check to_i method.
|
316
|
+
#
|
317
|
+
# 1.day.to_s # => "86400"
|
55
318
|
def to_s
|
56
319
|
@value.to_s
|
57
320
|
end
|
@@ -63,14 +326,14 @@ module ActiveSupport
|
|
63
326
|
# 1.day.to_i # => 86400
|
64
327
|
#
|
65
328
|
# Note that this conversion makes some assumptions about the
|
66
|
-
# duration of some periods, e.g. months are always
|
67
|
-
# and years are 365.
|
329
|
+
# duration of some periods, e.g. months are always 1/12 of year
|
330
|
+
# and years are 365.2425 days:
|
68
331
|
#
|
69
|
-
# # equivalent to
|
70
|
-
# 1.month.to_i # =>
|
332
|
+
# # equivalent to (1.year / 12).to_i
|
333
|
+
# 1.month.to_i # => 2629746
|
71
334
|
#
|
72
|
-
# # equivalent to 365.
|
73
|
-
# 1.year.to_i # =>
|
335
|
+
# # equivalent to 365.2425.days.to_i
|
336
|
+
# 1.year.to_i # => 31556952
|
74
337
|
#
|
75
338
|
# In such cases, Ruby's core
|
76
339
|
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
@@ -90,18 +353,13 @@ module ActiveSupport
|
|
90
353
|
@value.hash
|
91
354
|
end
|
92
355
|
|
93
|
-
def self.===(other) #:nodoc:
|
94
|
-
other.is_a?(Duration)
|
95
|
-
rescue ::NoMethodError
|
96
|
-
false
|
97
|
-
end
|
98
|
-
|
99
356
|
# Calculates a new Time or Date that is as far in the future
|
100
357
|
# as this Duration represents.
|
101
358
|
def since(time = ::Time.current)
|
102
359
|
sum(1, time)
|
103
360
|
end
|
104
361
|
alias :from_now :since
|
362
|
+
alias :after :since
|
105
363
|
|
106
364
|
# Calculates a new Time or Date that is as far in the past
|
107
365
|
# as this Duration represents.
|
@@ -109,12 +367,15 @@ module ActiveSupport
|
|
109
367
|
sum(-1, time)
|
110
368
|
end
|
111
369
|
alias :until :ago
|
370
|
+
alias :before :ago
|
112
371
|
|
113
372
|
def inspect #:nodoc:
|
373
|
+
return "0 seconds" if parts.empty?
|
374
|
+
|
114
375
|
parts.
|
115
|
-
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
|
116
|
-
sort_by {|unit, _ |
|
117
|
-
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
|
376
|
+
reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
|
377
|
+
sort_by { |unit, _ | PARTS.index(unit) }.
|
378
|
+
map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
|
118
379
|
to_sentence(locale: ::I18n.default_locale)
|
119
380
|
end
|
120
381
|
|
@@ -122,19 +383,31 @@ module ActiveSupport
|
|
122
383
|
to_i
|
123
384
|
end
|
124
385
|
|
125
|
-
def
|
126
|
-
|
386
|
+
def init_with(coder) #:nodoc:
|
387
|
+
initialize(coder["value"], coder["parts"])
|
388
|
+
end
|
389
|
+
|
390
|
+
def encode_with(coder) #:nodoc:
|
391
|
+
coder.map = { "value" => @value, "parts" => @parts }
|
127
392
|
end
|
128
393
|
|
129
|
-
|
394
|
+
# Build ISO 8601 Duration string for this duration.
|
395
|
+
# The +precision+ parameter can be used to limit seconds' precision of duration.
|
396
|
+
def iso8601(precision: nil)
|
397
|
+
ISO8601Serializer.new(self, precision: precision).serialize
|
398
|
+
end
|
130
399
|
|
131
|
-
|
400
|
+
private
|
132
401
|
|
133
|
-
def sum(sign, time = ::Time.current)
|
134
|
-
parts.inject(time) do |t,(type,number)|
|
402
|
+
def sum(sign, time = ::Time.current)
|
403
|
+
parts.inject(time) do |t, (type, number)|
|
135
404
|
if t.acts_like?(:time) || t.acts_like?(:date)
|
136
405
|
if type == :seconds
|
137
406
|
t.since(sign * number)
|
407
|
+
elsif type == :minutes
|
408
|
+
t.since(sign * number * 60)
|
409
|
+
elsif type == :hours
|
410
|
+
t.since(sign * number * 3600)
|
138
411
|
else
|
139
412
|
t.advance(type => sign * number)
|
140
413
|
end
|
@@ -144,17 +417,16 @@ module ActiveSupport
|
|
144
417
|
end
|
145
418
|
end
|
146
419
|
|
147
|
-
|
420
|
+
def respond_to_missing?(method, _)
|
421
|
+
value.respond_to?(method)
|
422
|
+
end
|
148
423
|
|
149
|
-
|
150
|
-
|
151
|
-
# Remove it when we drop support for 2.0.0-p353.
|
152
|
-
def ===(other) #:nodoc:
|
153
|
-
value === other
|
424
|
+
def method_missing(method, *args, &block)
|
425
|
+
value.public_send(method, *args, &block)
|
154
426
|
end
|
155
427
|
|
156
|
-
def
|
157
|
-
|
428
|
+
def raise_type_error(other)
|
429
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
158
430
|
end
|
159
431
|
end
|
160
432
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "active_support/encrypted_file"
|
5
|
+
require "active_support/ordered_options"
|
6
|
+
require "active_support/core_ext/object/inclusion"
|
7
|
+
require "active_support/core_ext/module/delegation"
|
8
|
+
|
9
|
+
module ActiveSupport
|
10
|
+
class EncryptedConfiguration < EncryptedFile
|
11
|
+
delegate :[], :fetch, to: :config
|
12
|
+
delegate_missing_to :options
|
13
|
+
|
14
|
+
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
|
15
|
+
super content_path: config_path, key_path: key_path,
|
16
|
+
env_key: env_key, raise_if_missing_key: raise_if_missing_key
|
17
|
+
end
|
18
|
+
|
19
|
+
# Allow a config to be started without a file present
|
20
|
+
def read
|
21
|
+
super
|
22
|
+
rescue ActiveSupport::EncryptedFile::MissingContentError
|
23
|
+
""
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(contents)
|
27
|
+
deserialize(contents)
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= deserialize(read).deep_symbolize_keys
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def options
|
38
|
+
@options ||= ActiveSupport::InheritableOptions.new(config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def serialize(config)
|
42
|
+
config.present? ? YAML.dump(config) : ""
|
43
|
+
end
|
44
|
+
|
45
|
+
def deserialize(config)
|
46
|
+
YAML.load(config).presence || {}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|