activesupport 4.2.11.1 → 6.0.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (263) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -411
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +34 -6
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +58 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +95 -91
  13. data/lib/active_support/cache/memory_store.rb +39 -36
  14. data/lib/active_support/cache/null_store.rb +11 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +75 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +331 -217
  19. data/lib/active_support/callbacks.rb +650 -592
  20. data/lib/active_support/concern.rb +35 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +226 -0
  23. data/lib/active_support/configurable.rb +13 -14
  24. data/lib/active_support/core_ext/array/access.rb +41 -1
  25. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  29. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +4 -6
  31. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  32. data/lib/active_support/core_ext/array.rb +9 -6
  33. data/lib/active_support/core_ext/benchmark.rb +3 -1
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +45 -31
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  39. data/lib/active_support/core_ext/class.rb +4 -3
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +14 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  43. data/lib/active_support/core_ext/date/conversions.rb +25 -23
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +6 -4
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +154 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -3
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  53. data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +114 -22
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/compact.rb +4 -23
  61. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  64. data/lib/active_support/core_ext/hash/except.rb +12 -9
  65. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  66. data/lib/active_support/core_ext/hash/keys.rb +19 -42
  67. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  68. data/lib/active_support/core_ext/hash/slice.rb +5 -27
  69. data/lib/active_support/core_ext/hash/transform_values.rb +4 -22
  70. data/lib/active_support/core_ext/hash.rb +10 -9
  71. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  72. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  73. data/lib/active_support/core_ext/integer/time.rb +11 -18
  74. data/lib/active_support/core_ext/integer.rb +5 -3
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/kernel.rb +5 -5
  79. data/lib/active_support/core_ext/load_error.rb +3 -22
  80. data/lib/active_support/core_ext/marshal.rb +8 -8
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -46
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +133 -30
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +44 -19
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -7
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -11
  94. data/lib/active_support/core_ext/name_error.rb +22 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +129 -136
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  99. data/lib/active_support/core_ext/numeric.rb +5 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +27 -3
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +51 -20
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  110. data/lib/active_support/core_ext/object/try.rb +81 -23
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/object.rb +14 -13
  113. data/lib/active_support/core_ext/range/compare_range.rb +76 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  115. data/lib/active_support/core_ext/range/each.rb +18 -17
  116. data/lib/active_support/core_ext/range/include_range.rb +7 -21
  117. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  118. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  119. data/lib/active_support/core_ext/range.rb +7 -4
  120. data/lib/active_support/core_ext/regexp.rb +2 -0
  121. data/lib/active_support/core_ext/securerandom.rb +45 -0
  122. data/lib/active_support/core_ext/string/access.rb +16 -6
  123. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  124. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  125. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  126. data/lib/active_support/core_ext/string/filters.rb +48 -6
  127. data/lib/active_support/core_ext/string/indent.rb +6 -4
  128. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  129. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  130. data/lib/active_support/core_ext/string/multibyte.rb +16 -7
  131. data/lib/active_support/core_ext/string/output_safety.rb +93 -40
  132. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  133. data/lib/active_support/core_ext/string/strip.rb +6 -5
  134. data/lib/active_support/core_ext/string/zones.rb +4 -2
  135. data/lib/active_support/core_ext/string.rb +15 -13
  136. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  137. data/lib/active_support/core_ext/time/calculations.rb +115 -52
  138. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  139. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  140. data/lib/active_support/core_ext/time/zones.rb +41 -7
  141. data/lib/active_support/core_ext/time.rb +7 -6
  142. data/lib/active_support/core_ext/uri.rb +6 -7
  143. data/lib/active_support/core_ext.rb +3 -1
  144. data/lib/active_support/current_attributes.rb +203 -0
  145. data/lib/active_support/dependencies/autoload.rb +2 -0
  146. data/lib/active_support/dependencies/interlock.rb +57 -0
  147. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  148. data/lib/active_support/dependencies.rb +208 -166
  149. data/lib/active_support/deprecation/behaviors.rb +44 -11
  150. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  151. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  152. data/lib/active_support/deprecation/method_wrappers.rb +61 -21
  153. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  154. data/lib/active_support/deprecation/reporting.rb +32 -12
  155. data/lib/active_support/deprecation.rb +12 -9
  156. data/lib/active_support/descendants_tracker.rb +57 -9
  157. data/lib/active_support/digest.rb +20 -0
  158. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  159. data/lib/active_support/duration/iso8601_serializer.rb +53 -0
  160. data/lib/active_support/duration.rb +315 -40
  161. data/lib/active_support/encrypted_configuration.rb +45 -0
  162. data/lib/active_support/encrypted_file.rb +100 -0
  163. data/lib/active_support/evented_file_update_checker.rb +234 -0
  164. data/lib/active_support/execution_wrapper.rb +129 -0
  165. data/lib/active_support/executor.rb +8 -0
  166. data/lib/active_support/file_update_checker.rb +62 -37
  167. data/lib/active_support/gem_version.rb +6 -4
  168. data/lib/active_support/gzip.rb +7 -5
  169. data/lib/active_support/hash_with_indifferent_access.rb +129 -30
  170. data/lib/active_support/i18n.rb +9 -6
  171. data/lib/active_support/i18n_railtie.rb +50 -14
  172. data/lib/active_support/inflections.rb +13 -11
  173. data/lib/active_support/inflector/inflections.rb +58 -13
  174. data/lib/active_support/inflector/methods.rb +159 -145
  175. data/lib/active_support/inflector/transliterate.rb +84 -34
  176. data/lib/active_support/inflector.rb +7 -5
  177. data/lib/active_support/json/decoding.rb +32 -30
  178. data/lib/active_support/json/encoding.rb +17 -60
  179. data/lib/active_support/json.rb +4 -2
  180. data/lib/active_support/key_generator.rb +11 -43
  181. data/lib/active_support/lazy_load_hooks.rb +53 -20
  182. data/lib/active_support/locale/en.rb +33 -0
  183. data/lib/active_support/locale/en.yml +2 -0
  184. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  185. data/lib/active_support/log_subscriber.rb +44 -19
  186. data/lib/active_support/logger.rb +9 -23
  187. data/lib/active_support/logger_silence.rb +32 -14
  188. data/lib/active_support/logger_thread_safe_level.rb +32 -8
  189. data/lib/active_support/message_encryptor.rb +166 -53
  190. data/lib/active_support/message_verifier.rb +149 -16
  191. data/lib/active_support/messages/metadata.rb +72 -0
  192. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  193. data/lib/active_support/messages/rotator.rb +56 -0
  194. data/lib/active_support/multibyte/chars.rb +56 -63
  195. data/lib/active_support/multibyte/unicode.rb +56 -290
  196. data/lib/active_support/multibyte.rb +4 -2
  197. data/lib/active_support/notifications/fanout.rb +109 -22
  198. data/lib/active_support/notifications/instrumenter.rb +107 -16
  199. data/lib/active_support/notifications.rb +51 -10
  200. data/lib/active_support/number_helper/number_converter.rb +16 -15
  201. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -15
  202. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  203. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -10
  204. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -9
  205. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  206. data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
  207. data/lib/active_support/number_helper/number_to_rounded_converter.rb +25 -57
  208. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  209. data/lib/active_support/number_helper.rb +105 -68
  210. data/lib/active_support/option_merger.rb +24 -4
  211. data/lib/active_support/ordered_hash.rb +7 -5
  212. data/lib/active_support/ordered_options.rb +27 -5
  213. data/lib/active_support/parameter_filter.rb +128 -0
  214. data/lib/active_support/per_thread_registry.rb +9 -4
  215. data/lib/active_support/proxy_object.rb +2 -0
  216. data/lib/active_support/rails.rb +10 -8
  217. data/lib/active_support/railtie.rb +43 -9
  218. data/lib/active_support/reloader.rb +130 -0
  219. data/lib/active_support/rescuable.rb +108 -53
  220. data/lib/active_support/security_utils.rb +15 -11
  221. data/lib/active_support/string_inquirer.rb +11 -4
  222. data/lib/active_support/subscriber.rb +74 -30
  223. data/lib/active_support/tagged_logging.rb +25 -13
  224. data/lib/active_support/test_case.rb +107 -44
  225. data/lib/active_support/testing/assertions.rb +151 -20
  226. data/lib/active_support/testing/autorun.rb +4 -2
  227. data/lib/active_support/testing/constant_lookup.rb +2 -1
  228. data/lib/active_support/testing/declarative.rb +3 -1
  229. data/lib/active_support/testing/deprecation.rb +13 -10
  230. data/lib/active_support/testing/file_fixtures.rb +38 -0
  231. data/lib/active_support/testing/isolation.rb +35 -26
  232. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  233. data/lib/active_support/testing/parallelization.rb +134 -0
  234. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  235. data/lib/active_support/testing/stream.rb +43 -0
  236. data/lib/active_support/testing/tagged_logging.rb +3 -1
  237. data/lib/active_support/testing/time_helpers.rb +84 -20
  238. data/lib/active_support/time.rb +14 -12
  239. data/lib/active_support/time_with_zone.rb +179 -39
  240. data/lib/active_support/values/time_zone.rb +203 -63
  241. data/lib/active_support/version.rb +3 -1
  242. data/lib/active_support/xml_mini/jdom.rb +116 -115
  243. data/lib/active_support/xml_mini/libxml.rb +16 -13
  244. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  245. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  246. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  247. data/lib/active_support/xml_mini/rexml.rb +11 -9
  248. data/lib/active_support/xml_mini.rb +38 -46
  249. data/lib/active_support.rb +13 -11
  250. metadata +84 -26
  251. data/lib/active_support/concurrency/latch.rb +0 -27
  252. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  253. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  254. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  255. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  256. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  257. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  258. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  259. data/lib/active_support/core_ext/object/itself.rb +0 -15
  260. data/lib/active_support/core_ext/struct.rb +0 -6
  261. data/lib/active_support/core_ext/thread.rb +0 -86
  262. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  263. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,9 @@
1
- require 'active_support/core_ext/array/conversions'
2
- require 'active_support/core_ext/object/acts_like'
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"
3
7
 
4
8
  module ActiveSupport
5
9
  # Provides accurate date and time measurements using Date#advance and
@@ -7,19 +11,239 @@ module ActiveSupport
7
11
  #
8
12
  # 1.month.ago # equivalent to Time.now.advance(months: -1)
9
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
+
10
127
  attr_accessor :value, :parts
11
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.round(9)
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 %= part_in_seconds
192
+ end
193
+ end unless value == 0
194
+
195
+ parts[:seconds] = remainder
196
+
197
+ new(value, parts)
198
+ end
199
+
200
+ private
201
+ def calculate_total_seconds(parts)
202
+ parts.inject(0) do |total, (part, value)|
203
+ total + value * PARTS_IN_SECONDS[part]
204
+ end
205
+ end
206
+ end
207
+
12
208
  def initialize(value, parts) #:nodoc:
13
- @value, @parts = value, parts
209
+ @value, @parts = value, parts.to_h
210
+ @parts.default = 0
211
+ @parts.reject! { |k, v| v.zero? } unless value == 0
212
+ end
213
+
214
+ def coerce(other) #:nodoc:
215
+ case other
216
+ when Scalar
217
+ [other, self]
218
+ when Duration
219
+ [Scalar.new(other.value), self]
220
+ else
221
+ [Scalar.new(other), self]
222
+ end
223
+ end
224
+
225
+ # Compares one Duration with another or a Numeric to this Duration.
226
+ # Numeric values are treated as seconds.
227
+ def <=>(other)
228
+ if Duration === other
229
+ value <=> other.value
230
+ elsif Numeric === other
231
+ value <=> other
232
+ end
14
233
  end
15
234
 
16
235
  # Adds another Duration or a Numeric to this Duration. Numeric values
17
236
  # are treated as seconds.
18
237
  def +(other)
19
238
  if Duration === other
20
- Duration.new(value + other.value, @parts + other.parts)
239
+ parts = @parts.dup
240
+ other.parts.each do |(key, value)|
241
+ parts[key] += value
242
+ end
243
+ Duration.new(value + other.value, parts)
21
244
  else
22
- Duration.new(value + other, @parts + [[:seconds, other]])
245
+ seconds = @parts[:seconds] + other
246
+ Duration.new(value + other, @parts.merge(seconds: seconds))
23
247
  end
24
248
  end
25
249
 
@@ -29,8 +253,44 @@ module ActiveSupport
29
253
  self + (-other)
30
254
  end
31
255
 
256
+ # Multiplies this Duration by a Numeric and returns a new Duration.
257
+ def *(other)
258
+ if Scalar === other || Duration === other
259
+ Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
260
+ elsif Numeric === other
261
+ Duration.new(value * other, parts.map { |type, number| [type, number * other] })
262
+ else
263
+ raise_type_error(other)
264
+ end
265
+ end
266
+
267
+ # Divides this Duration by a Numeric and returns a new Duration.
268
+ def /(other)
269
+ if Scalar === other
270
+ Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
271
+ elsif Duration === other
272
+ value / other.value
273
+ elsif Numeric === other
274
+ Duration.new(value / other, parts.map { |type, number| [type, number / other] })
275
+ else
276
+ raise_type_error(other)
277
+ end
278
+ end
279
+
280
+ # Returns the modulo of this Duration by another Duration or Numeric.
281
+ # Numeric values are treated as seconds.
282
+ def %(other)
283
+ if Duration === other || Scalar === other
284
+ Duration.build(value % other.value)
285
+ elsif Numeric === other
286
+ Duration.build(value % other)
287
+ else
288
+ raise_type_error(other)
289
+ end
290
+ end
291
+
32
292
  def -@ #:nodoc:
33
- Duration.new(-value, parts.map { |type,number| [type, -number] })
293
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
34
294
  end
35
295
 
36
296
  def is_a?(klass) #:nodoc:
@@ -52,6 +312,10 @@ module ActiveSupport
52
312
  end
53
313
  end
54
314
 
315
+ # Returns the amount of seconds a duration covers as a string.
316
+ # For more information check to_i method.
317
+ #
318
+ # 1.day.to_s # => "86400"
55
319
  def to_s
56
320
  @value.to_s
57
321
  end
@@ -63,18 +327,18 @@ module ActiveSupport
63
327
  # 1.day.to_i # => 86400
64
328
  #
65
329
  # Note that this conversion makes some assumptions about the
66
- # duration of some periods, e.g. months are always 30 days
67
- # and years are 365.25 days:
330
+ # duration of some periods, e.g. months are always 1/12 of year
331
+ # and years are 365.2425 days:
68
332
  #
69
- # # equivalent to 30.days.to_i
70
- # 1.month.to_i # => 2592000
333
+ # # equivalent to (1.year / 12).to_i
334
+ # 1.month.to_i # => 2629746
71
335
  #
72
- # # equivalent to 365.25.days.to_i
73
- # 1.year.to_i # => 31557600
336
+ # # equivalent to 365.2425.days.to_i
337
+ # 1.year.to_i # => 31556952
74
338
  #
75
339
  # In such cases, Ruby's core
76
- # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
77
- # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
340
+ # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
341
+ # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
78
342
  # date and time arithmetic.
79
343
  def to_i
80
344
  @value.to_i
@@ -90,18 +354,13 @@ module ActiveSupport
90
354
  @value.hash
91
355
  end
92
356
 
93
- def self.===(other) #:nodoc:
94
- other.is_a?(Duration)
95
- rescue ::NoMethodError
96
- false
97
- end
98
-
99
357
  # Calculates a new Time or Date that is as far in the future
100
358
  # as this Duration represents.
101
359
  def since(time = ::Time.current)
102
360
  sum(1, time)
103
361
  end
104
362
  alias :from_now :since
363
+ alias :after :since
105
364
 
106
365
  # Calculates a new Time or Date that is as far in the past
107
366
  # as this Duration represents.
@@ -109,12 +368,14 @@ module ActiveSupport
109
368
  sum(-1, time)
110
369
  end
111
370
  alias :until :ago
371
+ alias :before :ago
112
372
 
113
373
  def inspect #:nodoc:
374
+ return "#{value} seconds" if parts.empty?
375
+
114
376
  parts.
115
- reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
116
- sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
117
- map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
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,39 +383,53 @@ module ActiveSupport
122
383
  to_i
123
384
  end
124
385
 
125
- def respond_to_missing?(method, include_private=false) #:nodoc
126
- @value.respond_to?(method, include_private)
386
+ def init_with(coder) #:nodoc:
387
+ initialize(coder["value"], coder["parts"])
127
388
  end
128
389
 
129
- delegate :<=>, to: :value
390
+ def encode_with(coder) #:nodoc:
391
+ coder.map = { "value" => @value, "parts" => @parts }
392
+ end
393
+
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
- protected
400
+ private
401
+ def sum(sign, time = ::Time.current)
402
+ unless time.acts_like?(:time) || time.acts_like?(:date)
403
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
404
+ end
132
405
 
133
- def sum(sign, time = ::Time.current) #:nodoc:
134
- parts.inject(time) do |t,(type,number)|
135
- if t.acts_like?(:time) || t.acts_like?(:date)
406
+ if parts.empty?
407
+ time.since(sign * value)
408
+ else
409
+ parts.inject(time) do |t, (type, number)|
136
410
  if type == :seconds
137
411
  t.since(sign * number)
412
+ elsif type == :minutes
413
+ t.since(sign * number * 60)
414
+ elsif type == :hours
415
+ t.since(sign * number * 3600)
138
416
  else
139
417
  t.advance(type => sign * number)
140
418
  end
141
- else
142
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
143
419
  end
144
420
  end
145
421
  end
146
422
 
147
- private
423
+ def respond_to_missing?(method, _)
424
+ value.respond_to?(method)
425
+ end
148
426
 
149
- # We define it as a workaround to Ruby 2.0.0-p353 bug.
150
- # For more information, check rails/rails#13055.
151
- # Remove it when we drop support for 2.0.0-p353.
152
- def ===(other) #:nodoc:
153
- value === other
427
+ def method_missing(method, *args, &block)
428
+ value.public_send(method, *args, &block)
154
429
  end
155
430
 
156
- def method_missing(method, *args, &block) #:nodoc:
157
- value.send(method, *args, &block)
431
+ def raise_type_error(other)
432
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
158
433
  end
159
434
  end
160
435
  end
@@ -0,0 +1,45 @@
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 deserialize(config)
42
+ YAML.load(config).presence || {}
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "tmpdir"
5
+ require "active_support/message_encryptor"
6
+
7
+ module ActiveSupport
8
+ class EncryptedFile
9
+ class MissingContentError < RuntimeError
10
+ def initialize(content_path)
11
+ super "Missing encrypted content file in #{content_path}."
12
+ end
13
+ end
14
+
15
+ class MissingKeyError < RuntimeError
16
+ def initialize(key_path:, env_key:)
17
+ super \
18
+ "Missing encryption key to decrypt file with. " +
19
+ "Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
20
+ end
21
+ end
22
+
23
+ CIPHER = "aes-128-gcm"
24
+
25
+ def self.generate_key
26
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
27
+ end
28
+
29
+
30
+ attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
31
+
32
+ def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
33
+ @content_path, @key_path = Pathname.new(content_path), Pathname.new(key_path)
34
+ @env_key, @raise_if_missing_key = env_key, raise_if_missing_key
35
+ end
36
+
37
+ def key
38
+ read_env_key || read_key_file || handle_missing_key
39
+ end
40
+
41
+ def read
42
+ if !key.nil? && content_path.exist?
43
+ decrypt content_path.binread
44
+ else
45
+ raise MissingContentError, content_path
46
+ end
47
+ end
48
+
49
+ def write(contents)
50
+ IO.binwrite "#{content_path}.tmp", encrypt(contents)
51
+ FileUtils.mv "#{content_path}.tmp", content_path
52
+ end
53
+
54
+ def change(&block)
55
+ writing read, &block
56
+ end
57
+
58
+
59
+ private
60
+ def writing(contents)
61
+ tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
62
+ tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
63
+ tmp_path.binwrite contents
64
+
65
+ yield tmp_path
66
+
67
+ updated_contents = tmp_path.binread
68
+
69
+ write(updated_contents) if updated_contents != contents
70
+ ensure
71
+ FileUtils.rm(tmp_path) if tmp_path&.exist?
72
+ end
73
+
74
+
75
+ def encrypt(contents)
76
+ encryptor.encrypt_and_sign contents
77
+ end
78
+
79
+ def decrypt(contents)
80
+ encryptor.decrypt_and_verify contents
81
+ end
82
+
83
+ def encryptor
84
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
85
+ end
86
+
87
+
88
+ def read_env_key
89
+ ENV[env_key]
90
+ end
91
+
92
+ def read_key_file
93
+ key_path.binread.strip if key_path.exist?
94
+ end
95
+
96
+ def handle_missing_key
97
+ raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
98
+ end
99
+ end
100
+ end