activesupport 4.2.11.1 → 6.0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +399 -411
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +34 -6
- data/lib/active_support/benchmarkable.rb +6 -4
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +58 -53
- data/lib/active_support/cache/mem_cache_store.rb +95 -91
- data/lib/active_support/cache/memory_store.rb +39 -36
- data/lib/active_support/cache/null_store.rb +11 -7
- data/lib/active_support/cache/redis_cache_store.rb +493 -0
- data/lib/active_support/cache/strategy/local_cache.rb +75 -42
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
- data/lib/active_support/cache.rb +331 -217
- data/lib/active_support/callbacks.rb +650 -592
- data/lib/active_support/concern.rb +35 -6
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +13 -14
- data/lib/active_support/core_ext/array/access.rb +41 -1
- data/lib/active_support/core_ext/array/conversions.rb +24 -20
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- 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 +4 -6
- 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 +45 -31
- 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 +17 -14
- 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 +154 -65
- 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 -13
- 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 +37 -19
- data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
- 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 +114 -22
- 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 +4 -23
- 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/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +12 -9
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
- data/lib/active_support/core_ext/hash/keys.rb +19 -42
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +5 -27
- data/lib/active_support/core_ext/hash/transform_values.rb +4 -22
- data/lib/active_support/core_ext/hash.rb +10 -9
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- 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/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 +5 -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 +46 -46
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
- data/lib/active_support/core_ext/module/concerning.rb +11 -12
- data/lib/active_support/core_ext/module/delegation.rb +133 -30
- data/lib/active_support/core_ext/module/deprecation.rb +4 -2
- data/lib/active_support/core_ext/module/introspection.rb +44 -19
- data/lib/active_support/core_ext/module/reachable.rb +5 -7
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +13 -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 +129 -136
- data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
- data/lib/active_support/core_ext/numeric/time.rb +35 -23
- data/lib/active_support/core_ext/numeric.rb +5 -3
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +27 -3
- 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 +13 -93
- 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 +51 -20
- 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 +81 -23
- 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 +76 -0
- data/lib/active_support/core_ext/range/conversions.rb +37 -15
- data/lib/active_support/core_ext/range/each.rb +18 -17
- data/lib/active_support/core_ext/range/include_range.rb +7 -21
- 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 +2 -0
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string/access.rb +16 -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 +48 -6
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +66 -24
- data/lib/active_support/core_ext/string/inquiry.rb +3 -1
- data/lib/active_support/core_ext/string/multibyte.rb +16 -7
- data/lib/active_support/core_ext/string/output_safety.rb +93 -40
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
- data/lib/active_support/core_ext/string/strip.rb +6 -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 +115 -52
- 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 -7
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/current_attributes.rb +203 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +208 -166
- 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 +61 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
- data/lib/active_support/deprecation/reporting.rb +32 -12
- data/lib/active_support/deprecation.rb +12 -9
- data/lib/active_support/descendants_tracker.rb +57 -9
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +53 -0
- data/lib/active_support/duration.rb +315 -40
- data/lib/active_support/encrypted_configuration.rb +45 -0
- data/lib/active_support/encrypted_file.rb +100 -0
- data/lib/active_support/evented_file_update_checker.rb +234 -0
- data/lib/active_support/execution_wrapper.rb +129 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +62 -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 +129 -30
- data/lib/active_support/i18n.rb +9 -6
- data/lib/active_support/i18n_railtie.rb +50 -14
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector/inflections.rb +58 -13
- data/lib/active_support/inflector/methods.rb +159 -145
- data/lib/active_support/inflector/transliterate.rb +84 -34
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/json/decoding.rb +32 -30
- data/lib/active_support/json/encoding.rb +17 -60
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +11 -43
- data/lib/active_support/lazy_load_hooks.rb +53 -20
- data/lib/active_support/locale/en.rb +33 -0
- 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 +44 -19
- data/lib/active_support/logger.rb +9 -23
- data/lib/active_support/logger_silence.rb +32 -14
- data/lib/active_support/logger_thread_safe_level.rb +32 -8
- data/lib/active_support/message_encryptor.rb +166 -53
- data/lib/active_support/message_verifier.rb +149 -16
- data/lib/active_support/messages/metadata.rb +72 -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 +56 -63
- data/lib/active_support/multibyte/unicode.rb +56 -290
- data/lib/active_support/multibyte.rb +4 -2
- data/lib/active_support/notifications/fanout.rb +109 -22
- data/lib/active_support/notifications/instrumenter.rb +107 -16
- data/lib/active_support/notifications.rb +51 -10
- data/lib/active_support/number_helper/number_converter.rb +16 -15
- data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -15
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
- data/lib/active_support/number_helper/number_to_human_converter.rb +13 -10
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -9
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +25 -57
- data/lib/active_support/number_helper/rounding_helper.rb +66 -0
- data/lib/active_support/number_helper.rb +105 -68
- data/lib/active_support/option_merger.rb +24 -4
- data/lib/active_support/ordered_hash.rb +7 -5
- data/lib/active_support/ordered_options.rb +27 -5
- data/lib/active_support/parameter_filter.rb +128 -0
- 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 +10 -8
- data/lib/active_support/railtie.rb +43 -9
- data/lib/active_support/reloader.rb +130 -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 -4
- data/lib/active_support/subscriber.rb +74 -30
- data/lib/active_support/tagged_logging.rb +25 -13
- data/lib/active_support/test_case.rb +107 -44
- data/lib/active_support/testing/assertions.rb +151 -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 +13 -10
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +35 -26
- data/lib/active_support/testing/method_call_assertions.rb +70 -0
- data/lib/active_support/testing/parallelization.rb +134 -0
- data/lib/active_support/testing/setup_and_teardown.rb +13 -8
- data/lib/active_support/testing/stream.rb +43 -0
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +84 -20
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +179 -39
- data/lib/active_support/values/time_zone.rb +203 -63
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini/jdom.rb +116 -115
- 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 +38 -46
- data/lib/active_support.rb +13 -11
- metadata +84 -26
- 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/agnostics.rb +0 -11
- 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
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "weakref"
|
4
|
+
|
1
5
|
module ActiveSupport
|
2
6
|
# This module provides an internal implementation to track descendants
|
3
7
|
# which is faster than iterating through ObjectSpace.
|
@@ -6,7 +10,8 @@ module ActiveSupport
|
|
6
10
|
|
7
11
|
class << self
|
8
12
|
def direct_descendants(klass)
|
9
|
-
@@direct_descendants[klass]
|
13
|
+
descendants = @@direct_descendants[klass]
|
14
|
+
descendants ? descendants.to_a : []
|
10
15
|
end
|
11
16
|
|
12
17
|
def descendants(klass)
|
@@ -18,10 +23,10 @@ module ActiveSupport
|
|
18
23
|
def clear
|
19
24
|
if defined? ActiveSupport::Dependencies
|
20
25
|
@@direct_descendants.each do |klass, descendants|
|
21
|
-
if
|
26
|
+
if Dependencies.autoloaded?(klass)
|
22
27
|
@@direct_descendants.delete(klass)
|
23
28
|
else
|
24
|
-
descendants.reject! { |v|
|
29
|
+
descendants.reject! { |v| Dependencies.autoloaded?(v) }
|
25
30
|
end
|
26
31
|
end
|
27
32
|
else
|
@@ -32,16 +37,18 @@ module ActiveSupport
|
|
32
37
|
# This is the only method that is not thread safe, but is only ever called
|
33
38
|
# during the eager loading phase.
|
34
39
|
def store_inherited(klass, descendant)
|
35
|
-
(@@direct_descendants[klass] ||=
|
40
|
+
(@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
|
36
41
|
end
|
37
42
|
|
38
43
|
private
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
def accumulate_descendants(klass, acc)
|
45
|
+
if direct_descendants = @@direct_descendants[klass]
|
46
|
+
direct_descendants.each do |direct_descendant|
|
47
|
+
acc << direct_descendant
|
48
|
+
accumulate_descendants(direct_descendant, acc)
|
49
|
+
end
|
50
|
+
end
|
43
51
|
end
|
44
|
-
end
|
45
52
|
end
|
46
53
|
|
47
54
|
def inherited(base)
|
@@ -56,5 +63,46 @@ module ActiveSupport
|
|
56
63
|
def descendants
|
57
64
|
DescendantsTracker.descendants(self)
|
58
65
|
end
|
66
|
+
|
67
|
+
# DescendantsArray is an array that contains weak references to classes.
|
68
|
+
class DescendantsArray # :nodoc:
|
69
|
+
include Enumerable
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@refs = []
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize_copy(orig)
|
76
|
+
@refs = @refs.dup
|
77
|
+
end
|
78
|
+
|
79
|
+
def <<(klass)
|
80
|
+
cleanup!
|
81
|
+
@refs << WeakRef.new(klass)
|
82
|
+
end
|
83
|
+
|
84
|
+
def each
|
85
|
+
@refs.each do |ref|
|
86
|
+
yield ref.__getobj__
|
87
|
+
rescue WeakRef::RefError
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def refs_size
|
92
|
+
@refs.size
|
93
|
+
end
|
94
|
+
|
95
|
+
def cleanup!
|
96
|
+
@refs.delete_if { |ref| !ref.weakref_alive? }
|
97
|
+
end
|
98
|
+
|
99
|
+
def reject!
|
100
|
+
@refs.reject! do |ref|
|
101
|
+
yield ref.__getobj__
|
102
|
+
rescue WeakRef::RefError
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
59
107
|
end
|
60
108
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
class Digest #:nodoc:
|
5
|
+
class <<self
|
6
|
+
def hash_digest_class
|
7
|
+
@hash_digest_class ||= ::Digest::MD5
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash_digest_class=(klass)
|
11
|
+
raise ArgumentError, "#{klass} is expected to implement hexdigest class method" unless klass.respond_to?(:hexdigest)
|
12
|
+
@hash_digest_class = klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def hexdigest(arg)
|
16
|
+
hash_digest_class.hexdigest(arg)[0...32]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
class Duration
|
7
|
+
# Parses a string formatted according to ISO 8601 Duration into the hash.
|
8
|
+
#
|
9
|
+
# See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
|
10
|
+
#
|
11
|
+
# This parser allows negative parts to be present in pattern.
|
12
|
+
class ISO8601Parser # :nodoc:
|
13
|
+
class ParsingError < ::ArgumentError; end
|
14
|
+
|
15
|
+
PERIOD_OR_COMMA = /\.|,/
|
16
|
+
PERIOD = "."
|
17
|
+
COMMA = ","
|
18
|
+
|
19
|
+
SIGN_MARKER = /\A\-|\+|/
|
20
|
+
DATE_MARKER = /P/
|
21
|
+
TIME_MARKER = /T/
|
22
|
+
DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
|
23
|
+
TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
|
24
|
+
|
25
|
+
DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days }
|
26
|
+
TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds }
|
27
|
+
|
28
|
+
DATE_COMPONENTS = [:years, :months, :days]
|
29
|
+
TIME_COMPONENTS = [:hours, :minutes, :seconds]
|
30
|
+
|
31
|
+
attr_reader :parts, :scanner
|
32
|
+
attr_accessor :mode, :sign
|
33
|
+
|
34
|
+
def initialize(string)
|
35
|
+
@scanner = StringScanner.new(string)
|
36
|
+
@parts = {}
|
37
|
+
@mode = :start
|
38
|
+
@sign = 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse!
|
42
|
+
while !finished?
|
43
|
+
case mode
|
44
|
+
when :start
|
45
|
+
if scan(SIGN_MARKER)
|
46
|
+
self.sign = (scanner.matched == "-") ? -1 : 1
|
47
|
+
self.mode = :sign
|
48
|
+
else
|
49
|
+
raise_parsing_error
|
50
|
+
end
|
51
|
+
|
52
|
+
when :sign
|
53
|
+
if scan(DATE_MARKER)
|
54
|
+
self.mode = :date
|
55
|
+
else
|
56
|
+
raise_parsing_error
|
57
|
+
end
|
58
|
+
|
59
|
+
when :date
|
60
|
+
if scan(TIME_MARKER)
|
61
|
+
self.mode = :time
|
62
|
+
elsif scan(DATE_COMPONENT)
|
63
|
+
parts[DATE_TO_PART[scanner[2]]] = number * sign
|
64
|
+
else
|
65
|
+
raise_parsing_error
|
66
|
+
end
|
67
|
+
|
68
|
+
when :time
|
69
|
+
if scan(TIME_COMPONENT)
|
70
|
+
parts[TIME_TO_PART[scanner[2]]] = number * sign
|
71
|
+
else
|
72
|
+
raise_parsing_error
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
validate!
|
79
|
+
parts
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def finished?
|
84
|
+
scanner.eos?
|
85
|
+
end
|
86
|
+
|
87
|
+
# Parses number which can be a float with either comma or period.
|
88
|
+
def number
|
89
|
+
PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
def scan(pattern)
|
93
|
+
scanner.scan(pattern)
|
94
|
+
end
|
95
|
+
|
96
|
+
def raise_parsing_error(reason = nil)
|
97
|
+
raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
|
98
|
+
end
|
99
|
+
|
100
|
+
# Checks for various semantic errors as stated in ISO 8601 standard.
|
101
|
+
def validate!
|
102
|
+
raise_parsing_error("is empty duration") if parts.empty?
|
103
|
+
|
104
|
+
# Mixing any of Y, M, D with W is invalid.
|
105
|
+
if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
|
106
|
+
raise_parsing_error("mixing weeks with other date parts not allowed")
|
107
|
+
end
|
108
|
+
|
109
|
+
# Specifying an empty T part is invalid.
|
110
|
+
if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
|
111
|
+
raise_parsing_error("time part marker is present but time part is empty")
|
112
|
+
end
|
113
|
+
|
114
|
+
fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
|
115
|
+
unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
|
116
|
+
raise_parsing_error "(only last part can be fractional)"
|
117
|
+
end
|
118
|
+
|
119
|
+
true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
class Duration
|
7
|
+
# Serializes duration to string according to ISO 8601 Duration format.
|
8
|
+
class ISO8601Serializer # :nodoc:
|
9
|
+
def initialize(duration, precision: nil)
|
10
|
+
@duration = duration
|
11
|
+
@precision = precision
|
12
|
+
end
|
13
|
+
|
14
|
+
# Builds and returns output string.
|
15
|
+
def serialize
|
16
|
+
parts, sign = normalize
|
17
|
+
return "PT0S" if parts.empty?
|
18
|
+
|
19
|
+
output = +"P"
|
20
|
+
output << "#{parts[:years]}Y" if parts.key?(:years)
|
21
|
+
output << "#{parts[:months]}M" if parts.key?(:months)
|
22
|
+
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
|
23
|
+
output << "#{parts[:days]}D" if parts.key?(:days)
|
24
|
+
time = +""
|
25
|
+
time << "#{parts[:hours]}H" if parts.key?(:hours)
|
26
|
+
time << "#{parts[:minutes]}M" if parts.key?(:minutes)
|
27
|
+
if parts.key?(:seconds)
|
28
|
+
time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
|
29
|
+
end
|
30
|
+
output << "T#{time}" unless time.empty?
|
31
|
+
"#{sign}#{output}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
# Return pair of duration's parts and whole duration sign.
|
36
|
+
# Parts are summarized (as they can become repetitive due to addition, etc).
|
37
|
+
# Zero parts are removed as not significant.
|
38
|
+
# If all parts are negative it will negate all of them and return minus as a sign.
|
39
|
+
def normalize
|
40
|
+
parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
|
41
|
+
p[k] += v unless v.zero?
|
42
|
+
end
|
43
|
+
# If all parts are negative - let's make a negative duration
|
44
|
+
sign = ""
|
45
|
+
if parts.values.all? { |v| v < 0 }
|
46
|
+
sign = "-"
|
47
|
+
parts.transform_values!(&:-@)
|
48
|
+
end
|
49
|
+
[parts, sign]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|