activesupport 4.2.0 → 5.2.0
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 +5 -5
- data/CHANGELOG.md +366 -232
- data/MIT-LICENSE +2 -2
- data/README.rdoc +4 -5
- data/lib/active_support.rb +17 -7
- 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.rb +271 -177
- 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 +454 -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/callbacks.rb +654 -560
- data/lib/active_support/concern.rb +5 -3
- 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.rb +3 -1
- data/lib/active_support/core_ext/array.rb +9 -6
- 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/benchmark.rb +3 -1
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
- data/lib/active_support/core_ext/class.rb +4 -3
- 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 -8
- data/lib/active_support/core_ext/date.rb +6 -4
- 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 +31 -23
- data/lib/active_support/core_ext/date/zones.rb +4 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +179 -56
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
- data/lib/active_support/core_ext/date_time.rb +7 -4
- 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 +58 -20
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
- data/lib/active_support/core_ext/digest/uuid.rb +7 -5
- data/lib/active_support/core_ext/enumerable.rb +107 -28
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/file/atomic.rb +38 -31
- data/lib/active_support/core_ext/hash.rb +11 -9
- data/lib/active_support/core_ext/hash/compact.rb +24 -15
- data/lib/active_support/core_ext/hash/conversions.rb +63 -43
- 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 +16 -7
- data/lib/active_support/core_ext/integer.rb +5 -3
- 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 -33
- data/lib/active_support/core_ext/kernel.rb +6 -5
- 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 -83
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/load_error.rb +3 -22
- data/lib/active_support/core_ext/marshal.rb +13 -10
- data/lib/active_support/core_ext/module.rb +14 -11
- 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 +121 -39
- 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/name_error.rb +22 -2
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
- data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
- data/lib/active_support/core_ext/numeric/time.rb +35 -38
- data/lib/active_support/core_ext/object.rb +14 -13
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +29 -4
- 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 +98 -45
- 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 +6 -4
- data/lib/active_support/core_ext/object/try.rb +70 -22
- data/lib/active_support/core_ext/object/with_options.rb +16 -3
- data/lib/active_support/core_ext/range.rb +7 -4
- 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 +21 -19
- 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/regexp.rb +6 -0
- data/lib/active_support/core_ext/securerandom.rb +25 -0
- data/lib/active_support/core_ext/string.rb +15 -13
- data/lib/active_support/core_ext/string/access.rb +9 -7
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +8 -5
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +10 -8
- 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 +35 -35
- 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/time.rb +7 -5
- data/lib/active_support/core_ext/time/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +101 -51
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- 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/uri.rb +5 -4
- data/lib/active_support/current_attributes.rb +195 -0
- data/lib/active_support/dependencies.rb +143 -160
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/deprecation.rb +12 -9
- data/lib/active_support/deprecation/behaviors.rb +41 -12
- 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 +54 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
- data/lib/active_support/deprecation/reporting.rb +32 -12
- data/lib/active_support/descendants_tracker.rb +2 -0
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration.rb +326 -30
- data/lib/active_support/duration/iso8601_parser.rb +125 -0
- data/lib/active_support/duration/iso8601_serializer.rb +55 -0
- 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 +4 -2
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +130 -30
- data/lib/active_support/i18n.rb +8 -6
- data/lib/active_support/i18n_railtie.rb +34 -14
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/inflector/inflections.rb +61 -12
- data/lib/active_support/inflector/methods.rb +161 -136
- data/lib/active_support/inflector/transliterate.rb +48 -27
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/json/decoding.rb +16 -13
- data/lib/active_support/json/encoding.rb +15 -57
- 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.rb +13 -10
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/logger.rb +54 -3
- data/lib/active_support/logger_silence.rb +12 -7
- data/lib/active_support/logger_thread_safe_level.rb +33 -0
- data/lib/active_support/message_encryptor.rb +173 -51
- 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.rb +4 -2
- data/lib/active_support/multibyte/chars.rb +37 -24
- data/lib/active_support/multibyte/unicode.rb +100 -96
- data/lib/active_support/notifications.rb +11 -7
- data/lib/active_support/notifications/fanout.rb +10 -8
- data/lib/active_support/notifications/instrumenter.rb +27 -7
- data/lib/active_support/number_helper.rb +94 -68
- 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/option_merger.rb +3 -1
- data/lib/active_support/ordered_hash.rb +6 -4
- data/lib/active_support/ordered_options.rb +22 -4
- data/lib/active_support/per_thread_registry.rb +13 -6
- 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 +17 -6
- data/lib/active_support/string_inquirer.rb +11 -3
- data/lib/active_support/subscriber.rb +15 -14
- data/lib/active_support/tagged_logging.rb +14 -11
- data/lib/active_support/test_case.rb +18 -46
- 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 +12 -3
- 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 +96 -27
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +195 -53
- data/lib/active_support/values/time_zone.rb +200 -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.rb +69 -51
- data/lib/active_support/xml_mini/jdom.rb +116 -113
- data/lib/active_support/xml_mini/libxml.rb +17 -16
- data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
- data/lib/active_support/xml_mini/nokogiri.rb +15 -15
- data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
- data/lib/active_support/xml_mini/rexml.rb +17 -16
- metadata +55 -43
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
- 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 -11
- 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
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbconfig"
|
4
|
+
|
1
5
|
module ActiveSupport
|
2
6
|
class Deprecation
|
3
7
|
module Reporting
|
@@ -14,9 +18,9 @@ module ActiveSupport
|
|
14
18
|
def warn(message = nil, callstack = nil)
|
15
19
|
return if silenced
|
16
20
|
|
17
|
-
callstack ||=
|
21
|
+
callstack ||= caller_locations(2)
|
18
22
|
deprecation_message(callstack, message).tap do |m|
|
19
|
-
behavior.each { |b| b.call(m, callstack) }
|
23
|
+
behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -37,7 +41,7 @@ module ActiveSupport
|
|
37
41
|
end
|
38
42
|
|
39
43
|
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
|
40
|
-
caller_backtrace ||=
|
44
|
+
caller_backtrace ||= caller_locations(2)
|
41
45
|
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
|
42
46
|
warn(msg, caller_backtrace)
|
43
47
|
end
|
@@ -46,24 +50,23 @@ module ActiveSupport
|
|
46
50
|
private
|
47
51
|
# Outputs a deprecation warning message
|
48
52
|
#
|
49
|
-
#
|
53
|
+
# deprecated_method_warning(:method_name)
|
50
54
|
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
|
51
|
-
#
|
55
|
+
# deprecated_method_warning(:method_name, :another_method)
|
52
56
|
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
|
53
|
-
#
|
57
|
+
# deprecated_method_warning(:method_name, "Optional message")
|
54
58
|
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
|
55
59
|
def deprecated_method_warning(method_name, message = nil)
|
56
60
|
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
|
57
61
|
case message
|
58
|
-
|
59
|
-
|
60
|
-
|
62
|
+
when Symbol then "#{warning} (use #{message} instead)"
|
63
|
+
when String then "#{warning} (#{message})"
|
64
|
+
else warning
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
68
|
def deprecation_message(callstack, message = nil)
|
65
69
|
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
|
66
|
-
message += '.' unless message =~ /\.$/
|
67
70
|
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
|
68
71
|
end
|
69
72
|
|
@@ -79,8 +82,19 @@ module ActiveSupport
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def extract_callstack(callstack)
|
82
|
-
|
83
|
-
|
85
|
+
return _extract_callstack(callstack) if callstack.first.is_a? String
|
86
|
+
|
87
|
+
offending_line = callstack.find { |frame|
|
88
|
+
frame.absolute_path && !ignored_callstack(frame.absolute_path)
|
89
|
+
} || callstack.first
|
90
|
+
|
91
|
+
[offending_line.path, offending_line.lineno, offending_line.label]
|
92
|
+
end
|
93
|
+
|
94
|
+
def _extract_callstack(callstack)
|
95
|
+
warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
|
96
|
+
offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
|
97
|
+
|
84
98
|
if offending_line
|
85
99
|
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
|
86
100
|
md.captures
|
@@ -89,6 +103,12 @@ module ActiveSupport
|
|
89
103
|
end
|
90
104
|
end
|
91
105
|
end
|
106
|
+
|
107
|
+
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__)
|
108
|
+
|
109
|
+
def ignored_callstack(path)
|
110
|
+
path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
|
111
|
+
end
|
92
112
|
end
|
93
113
|
end
|
94
114
|
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
|
@@ -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,10 +311,38 @@ 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
|
58
321
|
|
322
|
+
# Returns the number of seconds that this Duration represents.
|
323
|
+
#
|
324
|
+
# 1.minute.to_i # => 60
|
325
|
+
# 1.hour.to_i # => 3600
|
326
|
+
# 1.day.to_i # => 86400
|
327
|
+
#
|
328
|
+
# Note that this conversion makes some assumptions about the
|
329
|
+
# duration of some periods, e.g. months are always 1/12 of year
|
330
|
+
# and years are 365.2425 days:
|
331
|
+
#
|
332
|
+
# # equivalent to (1.year / 12).to_i
|
333
|
+
# 1.month.to_i # => 2629746
|
334
|
+
#
|
335
|
+
# # equivalent to 365.2425.days.to_i
|
336
|
+
# 1.year.to_i # => 31556952
|
337
|
+
#
|
338
|
+
# In such cases, Ruby's core
|
339
|
+
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
|
340
|
+
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
|
341
|
+
# date and time arithmetic.
|
342
|
+
def to_i
|
343
|
+
@value.to_i
|
344
|
+
end
|
345
|
+
|
59
346
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
60
347
|
# same parts as this one.
|
61
348
|
def eql?(other)
|
@@ -66,18 +353,13 @@ module ActiveSupport
|
|
66
353
|
@value.hash
|
67
354
|
end
|
68
355
|
|
69
|
-
def self.===(other) #:nodoc:
|
70
|
-
other.is_a?(Duration)
|
71
|
-
rescue ::NoMethodError
|
72
|
-
false
|
73
|
-
end
|
74
|
-
|
75
356
|
# Calculates a new Time or Date that is as far in the future
|
76
357
|
# as this Duration represents.
|
77
358
|
def since(time = ::Time.current)
|
78
359
|
sum(1, time)
|
79
360
|
end
|
80
361
|
alias :from_now :since
|
362
|
+
alias :after :since
|
81
363
|
|
82
364
|
# Calculates a new Time or Date that is as far in the past
|
83
365
|
# as this Duration represents.
|
@@ -85,32 +367,47 @@ module ActiveSupport
|
|
85
367
|
sum(-1, time)
|
86
368
|
end
|
87
369
|
alias :until :ago
|
370
|
+
alias :before :ago
|
88
371
|
|
89
372
|
def inspect #:nodoc:
|
373
|
+
return "0 seconds" if parts.empty?
|
374
|
+
|
90
375
|
parts.
|
91
|
-
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
|
92
|
-
sort_by {|unit, _ |
|
93
|
-
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
|
94
|
-
to_sentence(:
|
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}" }.
|
379
|
+
to_sentence(locale: ::I18n.default_locale)
|
95
380
|
end
|
96
381
|
|
97
382
|
def as_json(options = nil) #:nodoc:
|
98
383
|
to_i
|
99
384
|
end
|
100
385
|
|
101
|
-
def
|
102
|
-
|
386
|
+
def init_with(coder) #:nodoc:
|
387
|
+
initialize(coder["value"], coder["parts"])
|
103
388
|
end
|
104
389
|
|
105
|
-
|
390
|
+
def encode_with(coder) #:nodoc:
|
391
|
+
coder.map = { "value" => @value, "parts" => @parts }
|
392
|
+
end
|
106
393
|
|
107
|
-
|
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
|
399
|
+
|
400
|
+
private
|
108
401
|
|
109
|
-
def sum(sign, time = ::Time.current)
|
110
|
-
parts.inject(time) do |t,(type,number)|
|
402
|
+
def sum(sign, time = ::Time.current)
|
403
|
+
parts.inject(time) do |t, (type, number)|
|
111
404
|
if t.acts_like?(:time) || t.acts_like?(:date)
|
112
405
|
if type == :seconds
|
113
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)
|
114
411
|
else
|
115
412
|
t.advance(type => sign * number)
|
116
413
|
end
|
@@ -120,17 +417,16 @@ module ActiveSupport
|
|
120
417
|
end
|
121
418
|
end
|
122
419
|
|
123
|
-
|
420
|
+
def respond_to_missing?(method, _)
|
421
|
+
value.respond_to?(method)
|
422
|
+
end
|
124
423
|
|
125
|
-
|
126
|
-
|
127
|
-
# Remove it when we drop support for 2.0.0-p353.
|
128
|
-
def ===(other) #:nodoc:
|
129
|
-
value === other
|
424
|
+
def method_missing(method, *args, &block)
|
425
|
+
value.public_send(method, *args, &block)
|
130
426
|
end
|
131
427
|
|
132
|
-
def
|
133
|
-
|
428
|
+
def raise_type_error(other)
|
429
|
+
raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
|
134
430
|
end
|
135
431
|
end
|
136
432
|
end
|