activesupport 6.0.0

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.
Files changed (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -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,433 @@
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
+
8
+ module ActiveSupport
9
+ # Provides accurate date and time measurements using Date#advance and
10
+ # Time#advance, respectively. It mainly supports the methods on Numeric.
11
+ #
12
+ # 1.month.ago # equivalent to Time.now.advance(months: -1)
13
+ class Duration
14
+ class Scalar < Numeric #:nodoc:
15
+ attr_reader :value
16
+ delegate :to_i, :to_f, :to_s, to: :value
17
+
18
+ def initialize(value)
19
+ @value = value
20
+ end
21
+
22
+ def coerce(other)
23
+ [Scalar.new(other), self]
24
+ end
25
+
26
+ def -@
27
+ Scalar.new(-value)
28
+ end
29
+
30
+ def <=>(other)
31
+ if Scalar === other || Duration === other
32
+ value <=> other.value
33
+ elsif Numeric === other
34
+ value <=> other
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def +(other)
41
+ if Duration === other
42
+ seconds = value + other.parts[:seconds]
43
+ new_parts = other.parts.merge(seconds: seconds)
44
+ new_value = value + other.value
45
+
46
+ Duration.new(new_value, new_parts)
47
+ else
48
+ calculate(:+, other)
49
+ end
50
+ end
51
+
52
+ def -(other)
53
+ if Duration === other
54
+ seconds = value - other.parts[:seconds]
55
+ new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
56
+ new_parts = new_parts.merge(seconds: seconds)
57
+ new_value = value - other.value
58
+
59
+ Duration.new(new_value, new_parts)
60
+ else
61
+ calculate(:-, other)
62
+ end
63
+ end
64
+
65
+ def *(other)
66
+ if Duration === other
67
+ new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
68
+ new_value = value * other.value
69
+
70
+ Duration.new(new_value, new_parts)
71
+ else
72
+ calculate(:*, other)
73
+ end
74
+ end
75
+
76
+ def /(other)
77
+ if Duration === other
78
+ value / other.value
79
+ else
80
+ calculate(:/, other)
81
+ end
82
+ end
83
+
84
+ def %(other)
85
+ if Duration === other
86
+ Duration.build(value % other.value)
87
+ else
88
+ calculate(:%, other)
89
+ end
90
+ end
91
+
92
+ private
93
+ def calculate(op, other)
94
+ if Scalar === other
95
+ Scalar.new(value.public_send(op, other.value))
96
+ elsif Numeric === other
97
+ Scalar.new(value.public_send(op, other))
98
+ else
99
+ raise_type_error(other)
100
+ end
101
+ end
102
+
103
+ def raise_type_error(other)
104
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
105
+ end
106
+ end
107
+
108
+ SECONDS_PER_MINUTE = 60
109
+ SECONDS_PER_HOUR = 3600
110
+ SECONDS_PER_DAY = 86400
111
+ SECONDS_PER_WEEK = 604800
112
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
113
+ SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
114
+
115
+ PARTS_IN_SECONDS = {
116
+ seconds: 1,
117
+ minutes: SECONDS_PER_MINUTE,
118
+ hours: SECONDS_PER_HOUR,
119
+ days: SECONDS_PER_DAY,
120
+ weeks: SECONDS_PER_WEEK,
121
+ months: SECONDS_PER_MONTH,
122
+ years: SECONDS_PER_YEAR
123
+ }.freeze
124
+
125
+ PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
126
+
127
+ attr_accessor :value, :parts
128
+
129
+ autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
130
+ autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
131
+
132
+ class << self
133
+ # Creates a new Duration from string formatted according to ISO 8601 Duration.
134
+ #
135
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
136
+ # This method allows negative parts to be present in pattern.
137
+ # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
138
+ def parse(iso8601duration)
139
+ parts = ISO8601Parser.new(iso8601duration).parse!
140
+ new(calculate_total_seconds(parts), parts)
141
+ end
142
+
143
+ def ===(other) #:nodoc:
144
+ other.is_a?(Duration)
145
+ rescue ::NoMethodError
146
+ false
147
+ end
148
+
149
+ def seconds(value) #:nodoc:
150
+ new(value, [[:seconds, value]])
151
+ end
152
+
153
+ def minutes(value) #:nodoc:
154
+ new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
155
+ end
156
+
157
+ def hours(value) #:nodoc:
158
+ new(value * SECONDS_PER_HOUR, [[:hours, value]])
159
+ end
160
+
161
+ def days(value) #:nodoc:
162
+ new(value * SECONDS_PER_DAY, [[:days, value]])
163
+ end
164
+
165
+ def weeks(value) #:nodoc:
166
+ new(value * SECONDS_PER_WEEK, [[:weeks, value]])
167
+ end
168
+
169
+ def months(value) #:nodoc:
170
+ new(value * SECONDS_PER_MONTH, [[:months, value]])
171
+ end
172
+
173
+ def years(value) #:nodoc:
174
+ new(value * SECONDS_PER_YEAR, [[:years, value]])
175
+ end
176
+
177
+ # Creates a new Duration from a seconds value that is converted
178
+ # to the individual parts:
179
+ #
180
+ # ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
181
+ # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
182
+ #
183
+ def build(value)
184
+ parts = {}
185
+ remainder = value.to_f
186
+
187
+ PARTS.each do |part|
188
+ unless part == :seconds
189
+ part_in_seconds = PARTS_IN_SECONDS[part]
190
+ parts[part] = remainder.div(part_in_seconds)
191
+ remainder = (remainder % part_in_seconds).round(9)
192
+ end
193
+ end
194
+
195
+ parts[:seconds] = remainder
196
+
197
+ new(value, parts)
198
+ end
199
+
200
+ private
201
+
202
+ def calculate_total_seconds(parts)
203
+ parts.inject(0) do |total, (part, value)|
204
+ total + value * PARTS_IN_SECONDS[part]
205
+ end
206
+ end
207
+ end
208
+
209
+ def initialize(value, parts) #:nodoc:
210
+ @value, @parts = value, parts.to_h
211
+ @parts.default = 0
212
+ @parts.reject! { |k, v| v.zero? }
213
+ end
214
+
215
+ def coerce(other) #:nodoc:
216
+ case other
217
+ when Scalar
218
+ [other, self]
219
+ when Duration
220
+ [Scalar.new(other.value), self]
221
+ else
222
+ [Scalar.new(other), self]
223
+ end
224
+ end
225
+
226
+ # Compares one Duration with another or a Numeric to this Duration.
227
+ # Numeric values are treated as seconds.
228
+ def <=>(other)
229
+ if Duration === other
230
+ value <=> other.value
231
+ elsif Numeric === other
232
+ value <=> other
233
+ end
234
+ end
235
+
236
+ # Adds another Duration or a Numeric to this Duration. Numeric values
237
+ # are treated as seconds.
238
+ def +(other)
239
+ if Duration === other
240
+ parts = @parts.dup
241
+ other.parts.each do |(key, value)|
242
+ parts[key] += value
243
+ end
244
+ Duration.new(value + other.value, parts)
245
+ else
246
+ seconds = @parts[:seconds] + other
247
+ Duration.new(value + other, @parts.merge(seconds: seconds))
248
+ end
249
+ end
250
+
251
+ # Subtracts another Duration or a Numeric from this Duration. Numeric
252
+ # values are treated as seconds.
253
+ def -(other)
254
+ self + (-other)
255
+ end
256
+
257
+ # Multiplies this Duration by a Numeric and returns a new Duration.
258
+ def *(other)
259
+ if Scalar === other || Duration === other
260
+ Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
261
+ elsif Numeric === other
262
+ Duration.new(value * other, parts.map { |type, number| [type, number * other] })
263
+ else
264
+ raise_type_error(other)
265
+ end
266
+ end
267
+
268
+ # Divides this Duration by a Numeric and returns a new Duration.
269
+ def /(other)
270
+ if Scalar === other
271
+ Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
272
+ elsif Duration === other
273
+ value / other.value
274
+ elsif Numeric === other
275
+ Duration.new(value / other, parts.map { |type, number| [type, number / other] })
276
+ else
277
+ raise_type_error(other)
278
+ end
279
+ end
280
+
281
+ # Returns the modulo of this Duration by another Duration or Numeric.
282
+ # Numeric values are treated as seconds.
283
+ def %(other)
284
+ if Duration === other || Scalar === other
285
+ Duration.build(value % other.value)
286
+ elsif Numeric === other
287
+ Duration.build(value % other)
288
+ else
289
+ raise_type_error(other)
290
+ end
291
+ end
292
+
293
+ def -@ #:nodoc:
294
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
295
+ end
296
+
297
+ def is_a?(klass) #:nodoc:
298
+ Duration == klass || value.is_a?(klass)
299
+ end
300
+ alias :kind_of? :is_a?
301
+
302
+ def instance_of?(klass) # :nodoc:
303
+ Duration == klass || value.instance_of?(klass)
304
+ end
305
+
306
+ # Returns +true+ if +other+ is also a Duration instance with the
307
+ # same +value+, or if <tt>other == value</tt>.
308
+ def ==(other)
309
+ if Duration === other
310
+ other.value == value
311
+ else
312
+ other == value
313
+ end
314
+ end
315
+
316
+ # Returns the amount of seconds a duration covers as a string.
317
+ # For more information check to_i method.
318
+ #
319
+ # 1.day.to_s # => "86400"
320
+ def to_s
321
+ @value.to_s
322
+ end
323
+
324
+ # Returns the number of seconds that this Duration represents.
325
+ #
326
+ # 1.minute.to_i # => 60
327
+ # 1.hour.to_i # => 3600
328
+ # 1.day.to_i # => 86400
329
+ #
330
+ # Note that this conversion makes some assumptions about the
331
+ # duration of some periods, e.g. months are always 1/12 of year
332
+ # and years are 365.2425 days:
333
+ #
334
+ # # equivalent to (1.year / 12).to_i
335
+ # 1.month.to_i # => 2629746
336
+ #
337
+ # # equivalent to 365.2425.days.to_i
338
+ # 1.year.to_i # => 31556952
339
+ #
340
+ # In such cases, Ruby's core
341
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
342
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
343
+ # date and time arithmetic.
344
+ def to_i
345
+ @value.to_i
346
+ end
347
+
348
+ # Returns +true+ if +other+ is also a Duration instance, which has the
349
+ # same parts as this one.
350
+ def eql?(other)
351
+ Duration === other && other.value.eql?(value)
352
+ end
353
+
354
+ def hash
355
+ @value.hash
356
+ end
357
+
358
+ # Calculates a new Time or Date that is as far in the future
359
+ # as this Duration represents.
360
+ def since(time = ::Time.current)
361
+ sum(1, time)
362
+ end
363
+ alias :from_now :since
364
+ alias :after :since
365
+
366
+ # Calculates a new Time or Date that is as far in the past
367
+ # as this Duration represents.
368
+ def ago(time = ::Time.current)
369
+ sum(-1, time)
370
+ end
371
+ alias :until :ago
372
+ alias :before :ago
373
+
374
+ def inspect #:nodoc:
375
+ return "0 seconds" if parts.empty?
376
+
377
+ parts.
378
+ sort_by { |unit, _ | PARTS.index(unit) }.
379
+ map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
380
+ to_sentence(locale: ::I18n.default_locale)
381
+ end
382
+
383
+ def as_json(options = nil) #:nodoc:
384
+ to_i
385
+ end
386
+
387
+ def init_with(coder) #:nodoc:
388
+ initialize(coder["value"], coder["parts"])
389
+ end
390
+
391
+ def encode_with(coder) #:nodoc:
392
+ coder.map = { "value" => @value, "parts" => @parts }
393
+ end
394
+
395
+ # Build ISO 8601 Duration string for this duration.
396
+ # The +precision+ parameter can be used to limit seconds' precision of duration.
397
+ def iso8601(precision: nil)
398
+ ISO8601Serializer.new(self, precision: precision).serialize
399
+ end
400
+
401
+ private
402
+
403
+ def sum(sign, time = ::Time.current)
404
+ parts.inject(time) do |t, (type, number)|
405
+ if t.acts_like?(:time) || t.acts_like?(:date)
406
+ if type == :seconds
407
+ t.since(sign * number)
408
+ elsif type == :minutes
409
+ t.since(sign * number * 60)
410
+ elsif type == :hours
411
+ t.since(sign * number * 3600)
412
+ else
413
+ t.advance(type => sign * number)
414
+ end
415
+ else
416
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
417
+ end
418
+ end
419
+ end
420
+
421
+ def respond_to_missing?(method, _)
422
+ value.respond_to?(method)
423
+ end
424
+
425
+ def method_missing(method, *args, &block)
426
+ value.public_send(method, *args, &block)
427
+ end
428
+
429
+ def raise_type_error(other)
430
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
431
+ end
432
+ end
433
+ end