activesupport 5.1.7 → 7.0.4.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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +259 -585
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +50 -43
  12. data/lib/active_support/cache/mem_cache_store.rb +194 -67
  13. data/lib/active_support/cache/memory_store.rb +70 -34
  14. data/lib/active_support/cache/null_store.rb +18 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +474 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +73 -50
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +556 -220
  19. data/lib/active_support/callbacks.rb +264 -159
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +81 -8
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  23. data/lib/active_support/concurrency/share_lock.rb +4 -3
  24. data/lib/active_support/configurable.rb +17 -16
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +18 -8
  27. data/lib/active_support/core_ext/array/conversions.rb +20 -17
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +8 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +4 -2
  33. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  34. data/lib/active_support/core_ext/array.rb +4 -1
  35. data/lib/active_support/core_ext/benchmark.rb +4 -2
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +3 -1
  37. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  38. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  40. data/lib/active_support/core_ext/class/subclasses.rb +10 -24
  41. data/lib/active_support/core_ext/class.rb +2 -0
  42. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  43. data/lib/active_support/core_ext/date/blank.rb +3 -1
  44. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  45. data/lib/active_support/core_ext/date/conversions.rb +24 -22
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +2 -0
  48. data/lib/active_support/core_ext/date.rb +3 -0
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +65 -41
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  54. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  56. data/lib/active_support/core_ext/date_time/conversions.rb +15 -14
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +3 -0
  59. data/lib/active_support/core_ext/digest/uuid.rb +42 -14
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +244 -72
  62. data/lib/active_support/core_ext/file/atomic.rb +6 -2
  63. data/lib/active_support/core_ext/file.rb +2 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +7 -6
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +4 -2
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +4 -31
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  72. data/lib/active_support/core_ext/hash.rb +3 -2
  73. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +7 -14
  76. data/lib/active_support/core_ext/integer.rb +2 -0
  77. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +6 -4
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +3 -1
  80. data/lib/active_support/core_ext/kernel.rb +2 -1
  81. data/lib/active_support/core_ext/load_error.rb +3 -8
  82. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  83. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  84. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -56
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +36 -27
  87. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  88. data/lib/active_support/core_ext/module/delegation.rb +97 -58
  89. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  90. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  93. data/lib/active_support/core_ext/module.rb +3 -1
  94. data/lib/active_support/core_ext/name_error.rb +30 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +134 -129
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  99. data/lib/active_support/core_ext/numeric.rb +3 -1
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +15 -5
  102. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  103. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  104. data/lib/active_support/core_ext/object/duplicable.rb +16 -110
  105. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  106. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  107. data/lib/active_support/core_ext/object/json.rb +51 -26
  108. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  109. data/lib/active_support/core_ext/object/to_query.rb +4 -2
  110. data/lib/active_support/core_ext/object/try.rb +26 -14
  111. data/lib/active_support/core_ext/object/with_options.rb +24 -3
  112. data/lib/active_support/core_ext/object.rb +2 -0
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +6 -3
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +3 -1
  121. data/lib/active_support/core_ext/range.rb +4 -1
  122. data/lib/active_support/core_ext/regexp.rb +10 -5
  123. data/lib/active_support/core_ext/securerandom.rb +25 -3
  124. data/lib/active_support/core_ext/string/access.rb +7 -16
  125. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  126. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  127. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  128. data/lib/active_support/core_ext/string/filters.rb +44 -1
  129. data/lib/active_support/core_ext/string/indent.rb +2 -0
  130. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  133. data/lib/active_support/core_ext/string/output_safety.rb +135 -27
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +5 -1
  136. data/lib/active_support/core_ext/string/zones.rb +2 -0
  137. data/lib/active_support/core_ext/string.rb +2 -0
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  141. data/lib/active_support/core_ext/time/calculations.rb +81 -24
  142. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  143. data/lib/active_support/core_ext/time/conversions.rb +17 -12
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +12 -25
  146. data/lib/active_support/core_ext/time.rb +3 -0
  147. data/lib/active_support/core_ext/uri.rb +4 -23
  148. data/lib/active_support/core_ext.rb +4 -1
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +2 -0
  152. data/lib/active_support/dependencies/interlock.rb +12 -18
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +59 -715
  155. data/lib/active_support/deprecation/behaviors.rb +48 -13
  156. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  159. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -8
  161. data/lib/active_support/deprecation/reporting.rb +54 -9
  162. data/lib/active_support/deprecation.rb +10 -3
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +9 -9
  166. data/lib/active_support/duration/iso8601_serializer.rb +29 -15
  167. data/lib/active_support/duration.rb +158 -72
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +129 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +87 -122
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +46 -21
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +2 -0
  178. data/lib/active_support/file_update_checker.rb +2 -1
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +7 -5
  181. data/lib/active_support/gzip.rb +2 -0
  182. data/lib/active_support/hash_with_indifferent_access.rb +126 -42
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +5 -1
  185. data/lib/active_support/i18n_railtie.rb +19 -14
  186. data/lib/active_support/inflections.rb +2 -0
  187. data/lib/active_support/inflector/inflections.rb +41 -14
  188. data/lib/active_support/inflector/methods.rb +73 -87
  189. data/lib/active_support/inflector/transliterate.rb +56 -18
  190. data/lib/active_support/inflector.rb +2 -0
  191. data/lib/active_support/isolated_execution_state.rb +72 -0
  192. data/lib/active_support/json/decoding.rb +27 -26
  193. data/lib/active_support/json/encoding.rb +16 -6
  194. data/lib/active_support/json.rb +2 -0
  195. data/lib/active_support/key_generator.rb +25 -38
  196. data/lib/active_support/lazy_load_hooks.rb +35 -6
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +8 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +4 -2
  200. data/lib/active_support/log_subscriber.rb +54 -13
  201. data/lib/active_support/logger.rb +4 -17
  202. data/lib/active_support/logger_silence.rb +13 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +48 -10
  204. data/lib/active_support/message_encryptor.rb +111 -37
  205. data/lib/active_support/message_verifier.rb +124 -21
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +19 -76
  210. data/lib/active_support/multibyte/unicode.rb +9 -331
  211. data/lib/active_support/multibyte.rb +3 -1
  212. data/lib/active_support/notifications/fanout.rb +165 -37
  213. data/lib/active_support/notifications/instrumenter.rb +92 -11
  214. data/lib/active_support/notifications.rb +96 -30
  215. data/lib/active_support/number_helper/number_converter.rb +8 -9
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -12
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +6 -3
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +7 -4
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +6 -3
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  223. data/lib/active_support/number_helper/rounding_helper.rb +16 -34
  224. data/lib/active_support/number_helper.rb +38 -12
  225. data/lib/active_support/option_merger.rb +19 -6
  226. data/lib/active_support/ordered_hash.rb +4 -2
  227. data/lib/active_support/ordered_options.rb +18 -6
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +8 -1
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +3 -10
  232. data/lib/active_support/railtie.rb +112 -11
  233. data/lib/active_support/reloader.rb +12 -11
  234. data/lib/active_support/rescuable.rb +19 -18
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +26 -15
  238. data/lib/active_support/string_inquirer.rb +4 -3
  239. data/lib/active_support/subscriber.rb +81 -42
  240. data/lib/active_support/tagged_logging.rb +45 -9
  241. data/lib/active_support/test_case.rb +86 -2
  242. data/lib/active_support/testing/assertions.rb +89 -21
  243. data/lib/active_support/testing/autorun.rb +2 -0
  244. data/lib/active_support/testing/constant_lookup.rb +2 -0
  245. data/lib/active_support/testing/declarative.rb +2 -0
  246. data/lib/active_support/testing/deprecation.rb +54 -2
  247. data/lib/active_support/testing/file_fixtures.rb +4 -0
  248. data/lib/active_support/testing/isolation.rb +6 -4
  249. data/lib/active_support/testing/method_call_assertions.rb +34 -5
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  255. data/lib/active_support/testing/stream.rb +6 -7
  256. data/lib/active_support/testing/tagged_logging.rb +3 -1
  257. data/lib/active_support/testing/time_helpers.rb +91 -15
  258. data/lib/active_support/time.rb +2 -0
  259. data/lib/active_support/time_with_zone.rb +168 -56
  260. data/lib/active_support/values/time_zone.rb +85 -37
  261. data/lib/active_support/version.rb +3 -1
  262. data/lib/active_support/xml_mini/jdom.rb +6 -5
  263. data/lib/active_support/xml_mini/libxml.rb +9 -7
  264. data/lib/active_support/xml_mini/libxmlsax.rb +7 -5
  265. data/lib/active_support/xml_mini/nokogiri.rb +8 -6
  266. data/lib/active_support/xml_mini/nokogirisax.rb +6 -4
  267. data/lib/active_support/xml_mini/rexml.rb +13 -4
  268. data/lib/active_support/xml_mini.rb +10 -15
  269. data/lib/active_support.rb +30 -9
  270. metadata +76 -35
  271. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  272. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  273. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  274. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  275. data/lib/active_support/core_ext/marshal.rb +0 -22
  276. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  277. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  278. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  279. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/array/conversions"
2
4
  require "active_support/core_ext/module/delegation"
3
5
  require "active_support/core_ext/object/acts_like"
4
6
  require "active_support/core_ext/string/filters"
5
- require "active_support/deprecation"
6
7
 
7
8
  module ActiveSupport
8
9
  # Provides accurate date and time measurements using Date#advance and
@@ -10,7 +11,7 @@ module ActiveSupport
10
11
  #
11
12
  # 1.month.ago # equivalent to Time.now.advance(months: -1)
12
13
  class Duration
13
- class Scalar < Numeric #:nodoc:
14
+ class Scalar < Numeric # :nodoc:
14
15
  attr_reader :value
15
16
  delegate :to_i, :to_f, :to_s, to: :value
16
17
 
@@ -38,11 +39,11 @@ module ActiveSupport
38
39
 
39
40
  def +(other)
40
41
  if Duration === other
41
- seconds = value + other.parts[:seconds]
42
- new_parts = other.parts.merge(seconds: seconds)
42
+ seconds = value + other._parts.fetch(:seconds, 0)
43
+ new_parts = other._parts.merge(seconds: seconds)
43
44
  new_value = value + other.value
44
45
 
45
- Duration.new(new_value, new_parts)
46
+ Duration.new(new_value, new_parts, other.variable?)
46
47
  else
47
48
  calculate(:+, other)
48
49
  end
@@ -50,12 +51,12 @@ module ActiveSupport
50
51
 
51
52
  def -(other)
52
53
  if Duration === other
53
- seconds = value - other.parts[:seconds]
54
- new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
54
+ seconds = value - other._parts.fetch(:seconds, 0)
55
+ new_parts = other._parts.transform_values(&:-@)
55
56
  new_parts = new_parts.merge(seconds: seconds)
56
57
  new_value = value - other.value
57
58
 
58
- Duration.new(new_value, new_parts)
59
+ Duration.new(new_value, new_parts, other.variable?)
59
60
  else
60
61
  calculate(:-, other)
61
62
  end
@@ -63,10 +64,10 @@ module ActiveSupport
63
64
 
64
65
  def *(other)
65
66
  if Duration === other
66
- new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
67
+ new_parts = other._parts.transform_values { |other_value| value * other_value }
67
68
  new_value = value * other.value
68
69
 
69
- Duration.new(new_value, new_parts)
70
+ Duration.new(new_value, new_parts, other.variable?)
70
71
  else
71
72
  calculate(:*, other)
72
73
  end
@@ -88,6 +89,10 @@ module ActiveSupport
88
89
  end
89
90
  end
90
91
 
92
+ def variable? # :nodoc:
93
+ false
94
+ end
95
+
91
96
  private
92
97
  def calculate(op, other)
93
98
  if Scalar === other
@@ -122,8 +127,9 @@ module ActiveSupport
122
127
  }.freeze
123
128
 
124
129
  PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
130
+ VARIABLE_PARTS = [:years, :months, :weeks, :days].freeze
125
131
 
126
- attr_accessor :value, :parts
132
+ attr_reader :value
127
133
 
128
134
  autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
129
135
  autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
@@ -131,7 +137,7 @@ module ActiveSupport
131
137
  class << self
132
138
  # Creates a new Duration from string formatted according to ISO 8601 Duration.
133
139
  #
134
- # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
140
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
135
141
  # This method allows negative parts to be present in pattern.
136
142
  # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
137
143
  def parse(iso8601duration)
@@ -139,38 +145,38 @@ module ActiveSupport
139
145
  new(calculate_total_seconds(parts), parts)
140
146
  end
141
147
 
142
- def ===(other) #:nodoc:
148
+ def ===(other) # :nodoc:
143
149
  other.is_a?(Duration)
144
150
  rescue ::NoMethodError
145
151
  false
146
152
  end
147
153
 
148
- def seconds(value) #:nodoc:
149
- new(value, [[:seconds, value]])
154
+ def seconds(value) # :nodoc:
155
+ new(value, { seconds: value }, false)
150
156
  end
151
157
 
152
- def minutes(value) #:nodoc:
153
- new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
158
+ def minutes(value) # :nodoc:
159
+ new(value * SECONDS_PER_MINUTE, { minutes: value }, false)
154
160
  end
155
161
 
156
- def hours(value) #:nodoc:
157
- new(value * SECONDS_PER_HOUR, [[:hours, value]])
162
+ def hours(value) # :nodoc:
163
+ new(value * SECONDS_PER_HOUR, { hours: value }, false)
158
164
  end
159
165
 
160
- def days(value) #:nodoc:
161
- new(value * SECONDS_PER_DAY, [[:days, value]])
166
+ def days(value) # :nodoc:
167
+ new(value * SECONDS_PER_DAY, { days: value }, true)
162
168
  end
163
169
 
164
- def weeks(value) #:nodoc:
165
- new(value * SECONDS_PER_WEEK, [[:weeks, value]])
170
+ def weeks(value) # :nodoc:
171
+ new(value * SECONDS_PER_WEEK, { weeks: value }, true)
166
172
  end
167
173
 
168
- def months(value) #:nodoc:
169
- new(value * SECONDS_PER_MONTH, [[:months, value]])
174
+ def months(value) # :nodoc:
175
+ new(value * SECONDS_PER_MONTH, { months: value }, true)
170
176
  end
171
177
 
172
- def years(value) #:nodoc:
173
- new(value * SECONDS_PER_YEAR, [[:years, value]])
178
+ def years(value) # :nodoc:
179
+ new(value * SECONDS_PER_YEAR, { years: value }, true)
174
180
  end
175
181
 
176
182
  # Creates a new Duration from a seconds value that is converted
@@ -180,25 +186,33 @@ module ActiveSupport
180
186
  # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
181
187
  #
182
188
  def build(value)
189
+ unless value.is_a?(::Numeric)
190
+ raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
191
+ end
192
+
183
193
  parts = {}
184
- remainder = value.to_f
194
+ remainder_sign = value <=> 0
195
+ remainder = value.round(9).abs
196
+ variable = false
185
197
 
186
198
  PARTS.each do |part|
187
199
  unless part == :seconds
188
200
  part_in_seconds = PARTS_IN_SECONDS[part]
189
- parts[part] = remainder.div(part_in_seconds)
190
- remainder = (remainder % part_in_seconds).round(9)
201
+ parts[part] = remainder.div(part_in_seconds) * remainder_sign
202
+ remainder %= part_in_seconds
203
+
204
+ unless parts[part].zero?
205
+ variable ||= VARIABLE_PARTS.include?(part)
206
+ end
191
207
  end
192
- end
208
+ end unless value == 0
193
209
 
194
- parts[:seconds] = remainder
195
- parts.reject! { |k, v| v.zero? }
210
+ parts[:seconds] = remainder * remainder_sign
196
211
 
197
- new(value, parts)
212
+ new(value, parts, variable)
198
213
  end
199
214
 
200
215
  private
201
-
202
216
  def calculate_total_seconds(parts)
203
217
  parts.inject(0) do |total, (part, value)|
204
218
  total + value * PARTS_IN_SECONDS[part]
@@ -206,14 +220,28 @@ module ActiveSupport
206
220
  end
207
221
  end
208
222
 
209
- def initialize(value, parts) #:nodoc:
210
- @value, @parts = value, parts.to_h
211
- @parts.default = 0
223
+ def initialize(value, parts, variable = nil) # :nodoc:
224
+ @value, @parts = value, parts
225
+ @parts.reject! { |k, v| v.zero? } unless value == 0
226
+ @parts.freeze
227
+ @variable = variable
228
+
229
+ if @variable.nil?
230
+ @variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) }
231
+ end
212
232
  end
213
233
 
214
- def coerce(other) #:nodoc:
215
- if Scalar === other
234
+ # Returns a copy of the parts hash that defines the duration
235
+ def parts
236
+ @parts.dup
237
+ end
238
+
239
+ def coerce(other) # :nodoc:
240
+ case other
241
+ when Scalar
216
242
  [other, self]
243
+ when Duration
244
+ [Scalar.new(other.value), self]
217
245
  else
218
246
  [Scalar.new(other), self]
219
247
  end
@@ -233,14 +261,13 @@ module ActiveSupport
233
261
  # are treated as seconds.
234
262
  def +(other)
235
263
  if Duration === other
236
- parts = @parts.dup
237
- other.parts.each do |(key, value)|
238
- parts[key] += value
264
+ parts = @parts.merge(other._parts) do |_key, value, other_value|
265
+ value + other_value
239
266
  end
240
- Duration.new(value + other.value, parts)
267
+ Duration.new(value + other.value, parts, @variable || other.variable?)
241
268
  else
242
- seconds = @parts[:seconds] + other
243
- Duration.new(value + other, @parts.merge(seconds: seconds))
269
+ seconds = @parts.fetch(:seconds, 0) + other
270
+ Duration.new(value + other, @parts.merge(seconds: seconds), @variable)
244
271
  end
245
272
  end
246
273
 
@@ -253,9 +280,9 @@ module ActiveSupport
253
280
  # Multiplies this Duration by a Numeric and returns a new Duration.
254
281
  def *(other)
255
282
  if Scalar === other || Duration === other
256
- Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
283
+ Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?)
257
284
  elsif Numeric === other
258
- Duration.new(value * other, parts.map { |type, number| [type, number * other] })
285
+ Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable)
259
286
  else
260
287
  raise_type_error(other)
261
288
  end
@@ -264,11 +291,11 @@ module ActiveSupport
264
291
  # Divides this Duration by a Numeric and returns a new Duration.
265
292
  def /(other)
266
293
  if Scalar === other
267
- Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
294
+ Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable)
268
295
  elsif Duration === other
269
296
  value / other.value
270
297
  elsif Numeric === other
271
- Duration.new(value / other, parts.map { |type, number| [type, number / other] })
298
+ Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable)
272
299
  else
273
300
  raise_type_error(other)
274
301
  end
@@ -286,11 +313,15 @@ module ActiveSupport
286
313
  end
287
314
  end
288
315
 
289
- def -@ #:nodoc:
290
- Duration.new(-value, parts.map { |type, number| [type, -number] })
316
+ def -@ # :nodoc:
317
+ Duration.new(-value, @parts.transform_values(&:-@), @variable)
318
+ end
319
+
320
+ def +@ # :nodoc:
321
+ self
291
322
  end
292
323
 
293
- def is_a?(klass) #:nodoc:
324
+ def is_a?(klass) # :nodoc:
294
325
  Duration == klass || value.is_a?(klass)
295
326
  end
296
327
  alias :kind_of? :is_a?
@@ -334,12 +365,55 @@ module ActiveSupport
334
365
  # 1.year.to_i # => 31556952
335
366
  #
336
367
  # In such cases, Ruby's core
337
- # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
338
- # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
368
+ # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
369
+ # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
339
370
  # date and time arithmetic.
340
371
  def to_i
341
372
  @value.to_i
342
373
  end
374
+ alias :in_seconds :to_i
375
+
376
+ # Returns the amount of minutes a duration covers as a float
377
+ #
378
+ # 1.day.in_minutes # => 1440.0
379
+ def in_minutes
380
+ in_seconds / SECONDS_PER_MINUTE.to_f
381
+ end
382
+
383
+ # Returns the amount of hours a duration covers as a float
384
+ #
385
+ # 1.day.in_hours # => 24.0
386
+ def in_hours
387
+ in_seconds / SECONDS_PER_HOUR.to_f
388
+ end
389
+
390
+ # Returns the amount of days a duration covers as a float
391
+ #
392
+ # 12.hours.in_days # => 0.5
393
+ def in_days
394
+ in_seconds / SECONDS_PER_DAY.to_f
395
+ end
396
+
397
+ # Returns the amount of weeks a duration covers as a float
398
+ #
399
+ # 2.months.in_weeks # => 8.696
400
+ def in_weeks
401
+ in_seconds / SECONDS_PER_WEEK.to_f
402
+ end
403
+
404
+ # Returns the amount of months a duration covers as a float
405
+ #
406
+ # 9.weeks.in_months # => 2.07
407
+ def in_months
408
+ in_seconds / SECONDS_PER_MONTH.to_f
409
+ end
410
+
411
+ # Returns the amount of years a duration covers as a float
412
+ #
413
+ # 30.days.in_years # => 0.082
414
+ def in_years
415
+ in_seconds / SECONDS_PER_YEAR.to_f
416
+ end
343
417
 
344
418
  # Returns +true+ if +other+ is also a Duration instance, which has the
345
419
  # same parts as this one.
@@ -367,27 +441,24 @@ module ActiveSupport
367
441
  alias :until :ago
368
442
  alias :before :ago
369
443
 
370
- def inspect #:nodoc:
371
- parts.
372
- reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
444
+ def inspect # :nodoc:
445
+ return "#{value} seconds" if @parts.empty?
446
+
447
+ @parts.
373
448
  sort_by { |unit, _ | PARTS.index(unit) }.
374
449
  map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
375
- to_sentence(locale: ::I18n.default_locale)
450
+ to_sentence(locale: false)
376
451
  end
377
452
 
378
- def as_json(options = nil) #:nodoc:
453
+ def as_json(options = nil) # :nodoc:
379
454
  to_i
380
455
  end
381
456
 
382
- def respond_to_missing?(method, include_private = false) #:nodoc:
383
- @value.respond_to?(method, include_private)
384
- end
385
-
386
- def init_with(coder) #:nodoc:
457
+ def init_with(coder) # :nodoc:
387
458
  initialize(coder["value"], coder["parts"])
388
459
  end
389
460
 
390
- def encode_with(coder) #:nodoc:
461
+ def encode_with(coder) # :nodoc:
391
462
  coder.map = { "value" => @value, "parts" => @parts }
392
463
  end
393
464
 
@@ -397,11 +468,24 @@ module ActiveSupport
397
468
  ISO8601Serializer.new(self, precision: precision).serialize
398
469
  end
399
470
 
400
- private
471
+ def variable? # :nodoc:
472
+ @variable
473
+ end
401
474
 
475
+ def _parts # :nodoc:
476
+ @parts
477
+ end
478
+
479
+ private
402
480
  def sum(sign, time = ::Time.current)
403
- parts.inject(time) do |t, (type, number)|
404
- if t.acts_like?(:time) || t.acts_like?(:date)
481
+ unless time.acts_like?(:time) || time.acts_like?(:date)
482
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
483
+ end
484
+
485
+ if @parts.empty?
486
+ time.since(sign * value)
487
+ else
488
+ @parts.inject(time) do |t, (type, number)|
405
489
  if type == :seconds
406
490
  t.since(sign * number)
407
491
  elsif type == :minutes
@@ -411,14 +495,16 @@ module ActiveSupport
411
495
  else
412
496
  t.advance(type => sign * number)
413
497
  end
414
- else
415
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
416
498
  end
417
499
  end
418
500
  end
419
501
 
502
+ def respond_to_missing?(method, _)
503
+ value.respond_to?(method)
504
+ end
505
+
420
506
  def method_missing(method, *args, &block)
421
- value.send(method, *args, &block)
507
+ value.public_send(method, *args, &block)
422
508
  end
423
509
 
424
510
  def raise_type_error(other)
@@ -0,0 +1,56 @@
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 deep_transform(hash)
38
+ return hash unless hash.is_a?(Hash)
39
+
40
+ h = ActiveSupport::InheritableOptions.new
41
+ hash.each do |k, v|
42
+ h[k] = deep_transform(v)
43
+ end
44
+ h
45
+ end
46
+
47
+ def options
48
+ @options ||= ActiveSupport::InheritableOptions.new(deep_transform(config))
49
+ end
50
+
51
+ def deserialize(config)
52
+ doc = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(config) : YAML.load(config)
53
+ doc.presence || {}
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,129 @@
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
+ # Returns the encryption key, first trying the environment variable
49
+ # specified by +env_key+, then trying the key file specified by +key_path+.
50
+ # If +raise_if_missing_key+ is true, raises MissingKeyError if the
51
+ # environment variable is not set and the key file does not exist.
52
+ def key
53
+ read_env_key || read_key_file || handle_missing_key
54
+ end
55
+
56
+ # Reads the file and returns the decrypted content.
57
+ #
58
+ # Raises:
59
+ # - MissingKeyError if the key is missing and +raise_if_missing_key+ is true.
60
+ # - MissingContentError if the encrypted file does not exist or otherwise
61
+ # if the key is missing.
62
+ # - ActiveSupport::MessageEncryptor::InvalidMessage if the content cannot be
63
+ # decrypted or verified.
64
+ def read
65
+ if !key.nil? && content_path.exist?
66
+ decrypt content_path.binread
67
+ else
68
+ raise MissingContentError, content_path
69
+ end
70
+ end
71
+
72
+ def write(contents)
73
+ IO.binwrite "#{content_path}.tmp", encrypt(contents)
74
+ FileUtils.mv "#{content_path}.tmp", content_path
75
+ end
76
+
77
+ def change(&block)
78
+ writing read, &block
79
+ end
80
+
81
+
82
+ private
83
+ def writing(contents)
84
+ tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
85
+ tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
86
+ tmp_path.binwrite contents
87
+
88
+ yield tmp_path
89
+
90
+ updated_contents = tmp_path.binread
91
+
92
+ write(updated_contents) if updated_contents != contents
93
+ ensure
94
+ FileUtils.rm(tmp_path) if tmp_path&.exist?
95
+ end
96
+
97
+
98
+ def encrypt(contents)
99
+ check_key_length
100
+ encryptor.encrypt_and_sign contents
101
+ end
102
+
103
+ def decrypt(contents)
104
+ encryptor.decrypt_and_verify contents
105
+ end
106
+
107
+ def encryptor
108
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
109
+ end
110
+
111
+
112
+ def read_env_key
113
+ ENV[env_key].presence
114
+ end
115
+
116
+ def read_key_file
117
+ return @key_file_contents if defined?(@key_file_contents)
118
+ @key_file_contents = (key_path.binread.strip if key_path.exist?)
119
+ end
120
+
121
+ def handle_missing_key
122
+ raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
123
+ end
124
+
125
+ def check_key_length
126
+ raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
127
+ end
128
+ end
129
+ 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