activesupport 4.2.11.1 → 6.1.7.3

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -391
  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 +50 -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 +61 -55
  12. data/lib/active_support/cache/mem_cache_store.rb +115 -100
  13. data/lib/active_support/cache/memory_store.rb +81 -58
  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 +90 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +386 -225
  19. data/lib/active_support/callbacks.rb +661 -594
  20. data/lib/active_support/concern.rb +80 -7
  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 +16 -17
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +41 -1
  26. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  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 +5 -3
  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 +52 -48
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -25
  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 +27 -24
  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 +167 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -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 +8 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +186 -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/conversions.rb +62 -41
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +13 -10
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +20 -43
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +10 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +19 -14
  85. data/lib/active_support/core_ext/module/delegation.rb +164 -51
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -22
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  90. data/lib/active_support/core_ext/module.rb +13 -11
  91. data/lib/active_support/core_ext/name_error.rb +51 -4
  92. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
  94. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  95. data/lib/active_support/core_ext/numeric.rb +5 -3
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +27 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  101. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  102. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  103. data/lib/active_support/core_ext/object/json.rb +63 -21
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +81 -23
  107. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  108. data/lib/active_support/core_ext/object.rb +14 -13
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  111. data/lib/active_support/core_ext/range/each.rb +18 -17
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +45 -0
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  119. data/lib/active_support/core_ext/string/conversions.rb +8 -4
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +48 -6
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +102 -26
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  126. data/lib/active_support/core_ext/string/output_safety.rb +125 -40
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +6 -5
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +137 -53
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +22 -13
  137. data/lib/active_support/core_ext/time/zones.rb +41 -7
  138. data/lib/active_support/core_ext/time.rb +7 -6
  139. data/lib/active_support/core_ext/uri.rb +11 -8
  140. data/lib/active_support/core_ext.rb +3 -1
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +210 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +241 -175
  147. data/lib/active_support/deprecation/behaviors.rb +58 -12
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  153. data/lib/active_support/deprecation/reporting.rb +81 -18
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +22 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  158. data/lib/active_support/duration/iso8601_serializer.rb +59 -0
  159. data/lib/active_support/duration.rb +364 -39
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +170 -0
  164. data/lib/active_support/execution_wrapper.rb +132 -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/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +7 -5
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +171 -48
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +47 -16
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +58 -14
  175. data/lib/active_support/inflector/methods.rb +186 -169
  176. data/lib/active_support/inflector/transliterate.rb +83 -33
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +32 -30
  179. data/lib/active_support/json/encoding.rb +22 -61
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +11 -43
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +9 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  186. data/lib/active_support/log_subscriber.rb +52 -19
  187. data/lib/active_support/logger.rb +10 -24
  188. data/lib/active_support/logger_silence.rb +14 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -10
  190. data/lib/active_support/message_encryptor.rb +167 -57
  191. data/lib/active_support/message_verifier.rb +151 -18
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +35 -80
  196. data/lib/active_support/multibyte/unicode.rb +22 -330
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +125 -23
  199. data/lib/active_support/notifications/instrumenter.rb +98 -16
  200. data/lib/active_support/notifications.rb +82 -14
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +15 -5
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +123 -71
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +7 -5
  213. data/lib/active_support/ordered_options.rb +35 -7
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +10 -4
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +10 -11
  218. data/lib/active_support/railtie.rb +66 -10
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +112 -57
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +13 -4
  224. data/lib/active_support/subscriber.rb +80 -31
  225. data/lib/active_support/tagged_logging.rb +54 -17
  226. data/lib/active_support/test_case.rb +107 -44
  227. data/lib/active_support/testing/assertions.rb +158 -20
  228. data/lib/active_support/testing/autorun.rb +4 -2
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +13 -10
  232. data/lib/active_support/testing/file_fixtures.rb +38 -0
  233. data/lib/active_support/testing/isolation.rb +35 -26
  234. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +43 -0
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +121 -20
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +215 -51
  244. data/lib/active_support/values/time_zone.rb +223 -71
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +116 -115
  247. data/lib/active_support/xml_mini/libxml.rb +16 -13
  248. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  249. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  250. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +38 -46
  253. data/lib/active_support.rb +25 -11
  254. metadata +100 -43
  255. data/lib/active_support/concurrency/latch.rb +0 -27
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  258. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  259. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  260. data/lib/active_support/core_ext/hash/compact.rb +0 -24
  261. data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
  262. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  263. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  264. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  265. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  266. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  267. data/lib/active_support/core_ext/object/itself.rb +0 -15
  268. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  269. data/lib/active_support/core_ext/struct.rb +0 -6
  270. data/lib/active_support/core_ext/thread.rb +0 -86
  271. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  272. 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,242 @@ 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.fetch(:seconds, 0)
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.fetch(:seconds, 0)
55
+ new_parts = other.parts.transform_values(&:-@)
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.transform_values { |other_value| value * other_value }
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
+ unless value.is_a?(::Numeric)
185
+ raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
186
+ end
187
+
188
+ parts = {}
189
+ remainder_sign = value <=> 0
190
+ remainder = value.round(9).abs
191
+
192
+ PARTS.each do |part|
193
+ unless part == :seconds
194
+ part_in_seconds = PARTS_IN_SECONDS[part]
195
+ parts[part] = remainder.div(part_in_seconds) * remainder_sign
196
+ remainder %= part_in_seconds
197
+ end
198
+ end unless value == 0
199
+
200
+ parts[:seconds] = remainder * remainder_sign
201
+
202
+ new(value, parts)
203
+ end
204
+
205
+ private
206
+ def calculate_total_seconds(parts)
207
+ parts.inject(0) do |total, (part, value)|
208
+ total + value * PARTS_IN_SECONDS[part]
209
+ end
210
+ end
211
+ end
212
+
12
213
  def initialize(value, parts) #:nodoc:
13
214
  @value, @parts = value, parts
215
+ @parts.reject! { |k, v| v.zero? } unless value == 0
216
+ end
217
+
218
+ def coerce(other) #:nodoc:
219
+ case other
220
+ when Scalar
221
+ [other, self]
222
+ when Duration
223
+ [Scalar.new(other.value), self]
224
+ else
225
+ [Scalar.new(other), self]
226
+ end
227
+ end
228
+
229
+ # Compares one Duration with another or a Numeric to this Duration.
230
+ # Numeric values are treated as seconds.
231
+ def <=>(other)
232
+ if Duration === other
233
+ value <=> other.value
234
+ elsif Numeric === other
235
+ value <=> other
236
+ end
14
237
  end
15
238
 
16
239
  # Adds another Duration or a Numeric to this Duration. Numeric values
17
240
  # are treated as seconds.
18
241
  def +(other)
19
242
  if Duration === other
20
- Duration.new(value + other.value, @parts + other.parts)
243
+ parts = @parts.merge(other.parts) do |_key, value, other_value|
244
+ value + other_value
245
+ end
246
+ Duration.new(value + other.value, parts)
21
247
  else
22
- Duration.new(value + other, @parts + [[:seconds, other]])
248
+ seconds = @parts.fetch(:seconds, 0) + other
249
+ Duration.new(value + other, @parts.merge(seconds: seconds))
23
250
  end
24
251
  end
25
252
 
@@ -29,8 +256,48 @@ module ActiveSupport
29
256
  self + (-other)
30
257
  end
31
258
 
259
+ # Multiplies this Duration by a Numeric and returns a new Duration.
260
+ def *(other)
261
+ if Scalar === other || Duration === other
262
+ Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
263
+ elsif Numeric === other
264
+ Duration.new(value * other, parts.transform_values { |number| number * other })
265
+ else
266
+ raise_type_error(other)
267
+ end
268
+ end
269
+
270
+ # Divides this Duration by a Numeric and returns a new Duration.
271
+ def /(other)
272
+ if Scalar === other
273
+ Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
274
+ elsif Duration === other
275
+ value / other.value
276
+ elsif Numeric === other
277
+ Duration.new(value / other, parts.transform_values { |number| number / other })
278
+ else
279
+ raise_type_error(other)
280
+ end
281
+ end
282
+
283
+ # Returns the modulo of this Duration by another Duration or Numeric.
284
+ # Numeric values are treated as seconds.
285
+ def %(other)
286
+ if Duration === other || Scalar === other
287
+ Duration.build(value % other.value)
288
+ elsif Numeric === other
289
+ Duration.build(value % other)
290
+ else
291
+ raise_type_error(other)
292
+ end
293
+ end
294
+
32
295
  def -@ #:nodoc:
33
- Duration.new(-value, parts.map { |type,number| [type, -number] })
296
+ Duration.new(-value, parts.transform_values(&:-@))
297
+ end
298
+
299
+ def +@ #:nodoc:
300
+ self
34
301
  end
35
302
 
36
303
  def is_a?(klass) #:nodoc:
@@ -52,6 +319,10 @@ module ActiveSupport
52
319
  end
53
320
  end
54
321
 
322
+ # Returns the amount of seconds a duration covers as a string.
323
+ # For more information check to_i method.
324
+ #
325
+ # 1.day.to_s # => "86400"
55
326
  def to_s
56
327
  @value.to_s
57
328
  end
@@ -63,22 +334,65 @@ module ActiveSupport
63
334
  # 1.day.to_i # => 86400
64
335
  #
65
336
  # 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:
337
+ # duration of some periods, e.g. months are always 1/12 of year
338
+ # and years are 365.2425 days:
68
339
  #
69
- # # equivalent to 30.days.to_i
70
- # 1.month.to_i # => 2592000
340
+ # # equivalent to (1.year / 12).to_i
341
+ # 1.month.to_i # => 2629746
71
342
  #
72
- # # equivalent to 365.25.days.to_i
73
- # 1.year.to_i # => 31557600
343
+ # # equivalent to 365.2425.days.to_i
344
+ # 1.year.to_i # => 31556952
74
345
  #
75
346
  # 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
347
+ # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
348
+ # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
78
349
  # date and time arithmetic.
79
350
  def to_i
80
351
  @value.to_i
81
352
  end
353
+ alias :in_seconds :to_i
354
+
355
+ # Returns the amount of minutes a duration covers as a float
356
+ #
357
+ # 1.day.in_minutes # => 1440.0
358
+ def in_minutes
359
+ in_seconds / SECONDS_PER_MINUTE.to_f
360
+ end
361
+
362
+ # Returns the amount of hours a duration covers as a float
363
+ #
364
+ # 1.day.in_hours # => 24.0
365
+ def in_hours
366
+ in_seconds / SECONDS_PER_HOUR.to_f
367
+ end
368
+
369
+ # Returns the amount of days a duration covers as a float
370
+ #
371
+ # 12.hours.in_days # => 0.5
372
+ def in_days
373
+ in_seconds / SECONDS_PER_DAY.to_f
374
+ end
375
+
376
+ # Returns the amount of weeks a duration covers as a float
377
+ #
378
+ # 2.months.in_weeks # => 8.696
379
+ def in_weeks
380
+ in_seconds / SECONDS_PER_WEEK.to_f
381
+ end
382
+
383
+ # Returns the amount of months a duration covers as a float
384
+ #
385
+ # 9.weeks.in_months # => 2.07
386
+ def in_months
387
+ in_seconds / SECONDS_PER_MONTH.to_f
388
+ end
389
+
390
+ # Returns the amount of years a duration covers as a float
391
+ #
392
+ # 30.days.in_years # => 0.082
393
+ def in_years
394
+ in_seconds / SECONDS_PER_YEAR.to_f
395
+ end
82
396
 
83
397
  # Returns +true+ if +other+ is also a Duration instance, which has the
84
398
  # same parts as this one.
@@ -90,18 +404,13 @@ module ActiveSupport
90
404
  @value.hash
91
405
  end
92
406
 
93
- def self.===(other) #:nodoc:
94
- other.is_a?(Duration)
95
- rescue ::NoMethodError
96
- false
97
- end
98
-
99
407
  # Calculates a new Time or Date that is as far in the future
100
408
  # as this Duration represents.
101
409
  def since(time = ::Time.current)
102
410
  sum(1, time)
103
411
  end
104
412
  alias :from_now :since
413
+ alias :after :since
105
414
 
106
415
  # Calculates a new Time or Date that is as far in the past
107
416
  # as this Duration represents.
@@ -109,12 +418,14 @@ module ActiveSupport
109
418
  sum(-1, time)
110
419
  end
111
420
  alias :until :ago
421
+ alias :before :ago
112
422
 
113
423
  def inspect #:nodoc:
424
+ return "#{value} seconds" if parts.empty?
425
+
114
426
  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}"}.
427
+ sort_by { |unit, _ | PARTS.index(unit) }.
428
+ map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
118
429
  to_sentence(locale: ::I18n.default_locale)
119
430
  end
120
431
 
@@ -122,39 +433,53 @@ module ActiveSupport
122
433
  to_i
123
434
  end
124
435
 
125
- def respond_to_missing?(method, include_private=false) #:nodoc
126
- @value.respond_to?(method, include_private)
436
+ def init_with(coder) #:nodoc:
437
+ initialize(coder["value"], coder["parts"])
127
438
  end
128
439
 
129
- delegate :<=>, to: :value
440
+ def encode_with(coder) #:nodoc:
441
+ coder.map = { "value" => @value, "parts" => @parts }
442
+ end
130
443
 
131
- protected
444
+ # Build ISO 8601 Duration string for this duration.
445
+ # The +precision+ parameter can be used to limit seconds' precision of duration.
446
+ def iso8601(precision: nil)
447
+ ISO8601Serializer.new(self, precision: precision).serialize
448
+ end
132
449
 
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)
450
+ private
451
+ def sum(sign, time = ::Time.current)
452
+ unless time.acts_like?(:time) || time.acts_like?(:date)
453
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
454
+ end
455
+
456
+ if parts.empty?
457
+ time.since(sign * value)
458
+ else
459
+ parts.inject(time) do |t, (type, number)|
136
460
  if type == :seconds
137
461
  t.since(sign * number)
462
+ elsif type == :minutes
463
+ t.since(sign * number * 60)
464
+ elsif type == :hours
465
+ t.since(sign * number * 3600)
138
466
  else
139
467
  t.advance(type => sign * number)
140
468
  end
141
- else
142
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
143
469
  end
144
470
  end
145
471
  end
146
472
 
147
- private
473
+ def respond_to_missing?(method, _)
474
+ value.respond_to?(method)
475
+ end
148
476
 
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
477
+ def method_missing(method, *args, &block)
478
+ value.public_send(method, *args, &block)
154
479
  end
155
480
 
156
- def method_missing(method, *args, &block) #:nodoc:
157
- value.send(method, *args, &block)
481
+ def raise_type_error(other)
482
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
158
483
  end
159
484
  end
160
485
  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,117 @@
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
+ class InvalidKeyLengthError < RuntimeError
24
+ def initialize
25
+ super "Encryption key must be exactly #{EncryptedFile.expected_key_length} characters."
26
+ end
27
+ end
28
+
29
+ CIPHER = "aes-128-gcm"
30
+
31
+ def self.generate_key
32
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
33
+ end
34
+
35
+ def self.expected_key_length # :nodoc:
36
+ @expected_key_length ||= generate_key.length
37
+ end
38
+
39
+
40
+ attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
41
+
42
+ def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
43
+ @content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
44
+ @key_path = Pathname.new(key_path)
45
+ @env_key, @raise_if_missing_key = env_key, raise_if_missing_key
46
+ end
47
+
48
+ def key
49
+ read_env_key || read_key_file || handle_missing_key
50
+ end
51
+
52
+ def read
53
+ if !key.nil? && content_path.exist?
54
+ decrypt content_path.binread
55
+ else
56
+ raise MissingContentError, content_path
57
+ end
58
+ end
59
+
60
+ def write(contents)
61
+ IO.binwrite "#{content_path}.tmp", encrypt(contents)
62
+ FileUtils.mv "#{content_path}.tmp", content_path
63
+ end
64
+
65
+ def change(&block)
66
+ writing read, &block
67
+ end
68
+
69
+
70
+ private
71
+ def writing(contents)
72
+ tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
73
+ tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
74
+ tmp_path.binwrite contents
75
+
76
+ yield tmp_path
77
+
78
+ updated_contents = tmp_path.binread
79
+
80
+ write(updated_contents) if updated_contents != contents
81
+ ensure
82
+ FileUtils.rm(tmp_path) if tmp_path&.exist?
83
+ end
84
+
85
+
86
+ def encrypt(contents)
87
+ check_key_length
88
+ encryptor.encrypt_and_sign contents
89
+ end
90
+
91
+ def decrypt(contents)
92
+ encryptor.decrypt_and_verify contents
93
+ end
94
+
95
+ def encryptor
96
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
97
+ end
98
+
99
+
100
+ def read_env_key
101
+ ENV[env_key]
102
+ end
103
+
104
+ def read_key_file
105
+ return @key_file_contents if defined?(@key_file_contents)
106
+ @key_file_contents = (key_path.binread.strip if key_path.exist?)
107
+ end
108
+
109
+ def handle_missing_key
110
+ raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
111
+ end
112
+
113
+ def check_key_length
114
+ raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/string_inquirer"
4
+
5
+ module ActiveSupport
6
+ class EnvironmentInquirer < StringInquirer #:nodoc:
7
+ DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
8
+ def initialize(env)
9
+ super(env)
10
+
11
+ DEFAULT_ENVIRONMENTS.each do |default|
12
+ instance_variable_set :"@#{default}", env == default
13
+ end
14
+ end
15
+
16
+ DEFAULT_ENVIRONMENTS.each do |env|
17
+ class_eval "def #{env}?; @#{env}; end"
18
+ end
19
+ end
20
+ end