activesupport 5.1.7 → 6.1.7

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 (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +434 -490
  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 +6 -2
  8. data/lib/active_support/backtrace_cleaner.rb +31 -3
  9. data/lib/active_support/benchmarkable.rb +3 -1
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +37 -36
  12. data/lib/active_support/cache/mem_cache_store.rb +72 -56
  13. data/lib/active_support/cache/memory_store.rb +61 -33
  14. data/lib/active_support/cache/null_store.rb +10 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +67 -21
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +310 -126
  19. data/lib/active_support/callbacks.rb +106 -100
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  22. data/lib/active_support/concurrency/share_lock.rb +2 -1
  23. data/lib/active_support/configurable.rb +12 -14
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +7 -5
  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 +2 -0
  30. data/lib/active_support/core_ext/array/inquiry.rb +2 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +3 -1
  33. data/lib/active_support/core_ext/benchmark.rb +4 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
  35. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -40
  39. data/lib/active_support/core_ext/class.rb +2 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  41. data/lib/active_support/core_ext/date/blank.rb +2 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +8 -5
  43. data/lib/active_support/core_ext/date/conversions.rb +12 -10
  44. data/lib/active_support/core_ext/date/zones.rb +2 -0
  45. data/lib/active_support/core_ext/date.rb +2 -0
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +61 -37
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +2 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  53. data/lib/active_support/core_ext/date_time/conversions.rb +2 -1
  54. data/lib/active_support/core_ext/date_time.rb +2 -0
  55. data/lib/active_support/core_ext/digest/uuid.rb +4 -1
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +174 -71
  58. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  59. data/lib/active_support/core_ext/file.rb +2 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +7 -5
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
  65. data/lib/active_support/core_ext/hash/keys.rb +3 -30
  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 +3 -2
  69. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +7 -14
  72. data/lib/active_support/core_ext/integer.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +2 -1
  77. data/lib/active_support/core_ext/load_error.rb +3 -8
  78. data/lib/active_support/core_ext/marshal.rb +4 -0
  79. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +44 -56
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +18 -18
  84. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  85. data/lib/active_support/core_ext/module/delegation.rb +103 -58
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +3 -1
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +131 -129
  94. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  95. data/lib/active_support/core_ext/numeric.rb +2 -1
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +13 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  99. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  100. data/lib/active_support/core_ext/object/duplicable.rb +9 -114
  101. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +22 -2
  104. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  105. data/lib/active_support/core_ext/object/to_query.rb +2 -0
  106. data/lib/active_support/core_ext/object/try.rb +19 -7
  107. data/lib/active_support/core_ext/object/with_options.rb +4 -2
  108. data/lib/active_support/core_ext/object.rb +2 -0
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  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 +4 -1
  115. data/lib/active_support/core_ext/regexp.rb +10 -5
  116. data/lib/active_support/core_ext/securerandom.rb +25 -3
  117. data/lib/active_support/core_ext/string/access.rb +7 -16
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +3 -0
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +44 -1
  122. data/lib/active_support/core_ext/string/indent.rb +2 -0
  123. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  124. data/lib/active_support/core_ext/string/inquiry.rb +3 -0
  125. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  126. data/lib/active_support/core_ext/string/output_safety.rb +104 -20
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +2 -0
  130. data/lib/active_support/core_ext/string.rb +2 -0
  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 +2 -0
  134. data/lib/active_support/core_ext/time/calculations.rb +76 -18
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +4 -0
  137. data/lib/active_support/core_ext/time/zones.rb +6 -4
  138. data/lib/active_support/core_ext/time.rb +2 -0
  139. data/lib/active_support/core_ext/uri.rb +11 -6
  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 +2 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +134 -60
  147. data/lib/active_support/deprecation/behaviors.rb +43 -11
  148. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  151. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +32 -6
  153. data/lib/active_support/deprecation/reporting.rb +54 -9
  154. data/lib/active_support/deprecation.rb +9 -2
  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 +6 -6
  158. data/lib/active_support/duration/iso8601_serializer.rb +20 -14
  159. data/lib/active_support/duration.rb +102 -45
  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 +84 -117
  164. data/lib/active_support/execution_wrapper.rb +19 -13
  165. data/lib/active_support/executor.rb +2 -0
  166. data/lib/active_support/file_update_checker.rb +2 -1
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +3 -1
  169. data/lib/active_support/gzip.rb +2 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +123 -41
  171. data/lib/active_support/i18n.rb +4 -1
  172. data/lib/active_support/i18n_railtie.rb +19 -14
  173. data/lib/active_support/inflections.rb +2 -0
  174. data/lib/active_support/inflector/inflections.rb +19 -8
  175. data/lib/active_support/inflector/methods.rb +87 -77
  176. data/lib/active_support/inflector/transliterate.rb +56 -18
  177. data/lib/active_support/inflector.rb +2 -0
  178. data/lib/active_support/json/decoding.rb +27 -26
  179. data/lib/active_support/json/encoding.rb +13 -3
  180. data/lib/active_support/json.rb +2 -0
  181. data/lib/active_support/key_generator.rb +3 -33
  182. data/lib/active_support/lazy_load_hooks.rb +7 -2
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +2 -0
  186. data/lib/active_support/log_subscriber.rb +42 -11
  187. data/lib/active_support/logger.rb +4 -17
  188. data/lib/active_support/logger_silence.rb +13 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +54 -7
  190. data/lib/active_support/message_encryptor.rb +100 -32
  191. data/lib/active_support/message_verifier.rb +85 -14
  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 +12 -68
  196. data/lib/active_support/multibyte/unicode.rb +17 -327
  197. data/lib/active_support/multibyte.rb +2 -0
  198. data/lib/active_support/notifications/fanout.rb +118 -16
  199. data/lib/active_support/notifications/instrumenter.rb +73 -9
  200. data/lib/active_support/notifications.rb +74 -8
  201. data/lib/active_support/number_helper/number_converter.rb +7 -6
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -2
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -3
  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 +5 -2
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  209. data/lib/active_support/number_helper/rounding_helper.rb +16 -30
  210. data/lib/active_support/number_helper.rb +40 -12
  211. data/lib/active_support/option_merger.rb +24 -3
  212. data/lib/active_support/ordered_hash.rb +3 -1
  213. data/lib/active_support/ordered_options.rb +17 -5
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +4 -1
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +3 -10
  218. data/lib/active_support/railtie.rb +60 -9
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +7 -6
  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 +6 -3
  224. data/lib/active_support/subscriber.rb +74 -24
  225. data/lib/active_support/tagged_logging.rb +44 -8
  226. data/lib/active_support/test_case.rb +94 -2
  227. data/lib/active_support/testing/assertions.rb +58 -20
  228. data/lib/active_support/testing/autorun.rb +2 -0
  229. data/lib/active_support/testing/constant_lookup.rb +2 -0
  230. data/lib/active_support/testing/declarative.rb +2 -0
  231. data/lib/active_support/testing/deprecation.rb +2 -1
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +4 -2
  234. data/lib/active_support/testing/method_call_assertions.rb +30 -1
  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 +12 -7
  239. data/lib/active_support/testing/stream.rb +3 -2
  240. data/lib/active_support/testing/tagged_logging.rb +2 -0
  241. data/lib/active_support/testing/time_helpers.rb +78 -13
  242. data/lib/active_support/time.rb +2 -0
  243. data/lib/active_support/time_with_zone.rb +113 -41
  244. data/lib/active_support/values/time_zone.rb +54 -25
  245. data/lib/active_support/version.rb +2 -0
  246. data/lib/active_support/xml_mini/jdom.rb +5 -4
  247. data/lib/active_support/xml_mini/libxml.rb +4 -2
  248. data/lib/active_support/xml_mini/libxmlsax.rb +6 -4
  249. data/lib/active_support/xml_mini/nokogiri.rb +4 -2
  250. data/lib/active_support/xml_mini/nokogirisax.rb +5 -3
  251. data/lib/active_support/xml_mini/rexml.rb +12 -3
  252. data/lib/active_support/xml_mini.rb +5 -11
  253. data/lib/active_support.rb +18 -13
  254. metadata +71 -32
  255. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  256. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  257. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  258. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  259. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  260. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  261. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  262. 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
@@ -38,7 +39,7 @@ module ActiveSupport
38
39
 
39
40
  def +(other)
40
41
  if Duration === other
41
- seconds = value + other.parts[:seconds]
42
+ seconds = value + other.parts.fetch(:seconds, 0)
42
43
  new_parts = other.parts.merge(seconds: seconds)
43
44
  new_value = value + other.value
44
45
 
@@ -50,8 +51,8 @@ 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
 
@@ -63,7 +64,7 @@ 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
70
  Duration.new(new_value, new_parts)
@@ -131,7 +132,7 @@ module ActiveSupport
131
132
  class << self
132
133
  # Creates a new Duration from string formatted according to ISO 8601 Duration.
133
134
  #
134
- # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
135
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
135
136
  # This method allows negative parts to be present in pattern.
136
137
  # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
137
138
  def parse(iso8601duration)
@@ -146,31 +147,31 @@ module ActiveSupport
146
147
  end
147
148
 
148
149
  def seconds(value) #:nodoc:
149
- new(value, [[:seconds, value]])
150
+ new(value, seconds: value)
150
151
  end
151
152
 
152
153
  def minutes(value) #:nodoc:
153
- new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
154
+ new(value * SECONDS_PER_MINUTE, minutes: value)
154
155
  end
155
156
 
156
157
  def hours(value) #:nodoc:
157
- new(value * SECONDS_PER_HOUR, [[:hours, value]])
158
+ new(value * SECONDS_PER_HOUR, hours: value)
158
159
  end
159
160
 
160
161
  def days(value) #:nodoc:
161
- new(value * SECONDS_PER_DAY, [[:days, value]])
162
+ new(value * SECONDS_PER_DAY, days: value)
162
163
  end
163
164
 
164
165
  def weeks(value) #:nodoc:
165
- new(value * SECONDS_PER_WEEK, [[:weeks, value]])
166
+ new(value * SECONDS_PER_WEEK, weeks: value)
166
167
  end
167
168
 
168
169
  def months(value) #:nodoc:
169
- new(value * SECONDS_PER_MONTH, [[:months, value]])
170
+ new(value * SECONDS_PER_MONTH, months: value)
170
171
  end
171
172
 
172
173
  def years(value) #:nodoc:
173
- new(value * SECONDS_PER_YEAR, [[:years, value]])
174
+ new(value * SECONDS_PER_YEAR, years: value)
174
175
  end
175
176
 
176
177
  # Creates a new Duration from a seconds value that is converted
@@ -180,25 +181,28 @@ module ActiveSupport
180
181
  # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
181
182
  #
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
+
183
188
  parts = {}
184
- remainder = value.to_f
189
+ remainder_sign = value <=> 0
190
+ remainder = value.round(9).abs
185
191
 
186
192
  PARTS.each do |part|
187
193
  unless part == :seconds
188
194
  part_in_seconds = PARTS_IN_SECONDS[part]
189
- parts[part] = remainder.div(part_in_seconds)
190
- remainder = (remainder % part_in_seconds).round(9)
195
+ parts[part] = remainder.div(part_in_seconds) * remainder_sign
196
+ remainder %= part_in_seconds
191
197
  end
192
- end
198
+ end unless value == 0
193
199
 
194
- parts[:seconds] = remainder
195
- parts.reject! { |k, v| v.zero? }
200
+ parts[:seconds] = remainder * remainder_sign
196
201
 
197
202
  new(value, parts)
198
203
  end
199
204
 
200
205
  private
201
-
202
206
  def calculate_total_seconds(parts)
203
207
  parts.inject(0) do |total, (part, value)|
204
208
  total + value * PARTS_IN_SECONDS[part]
@@ -207,13 +211,16 @@ module ActiveSupport
207
211
  end
208
212
 
209
213
  def initialize(value, parts) #:nodoc:
210
- @value, @parts = value, parts.to_h
211
- @parts.default = 0
214
+ @value, @parts = value, parts
215
+ @parts.reject! { |k, v| v.zero? } unless value == 0
212
216
  end
213
217
 
214
218
  def coerce(other) #:nodoc:
215
- if Scalar === other
219
+ case other
220
+ when Scalar
216
221
  [other, self]
222
+ when Duration
223
+ [Scalar.new(other.value), self]
217
224
  else
218
225
  [Scalar.new(other), self]
219
226
  end
@@ -233,13 +240,12 @@ module ActiveSupport
233
240
  # are treated as seconds.
234
241
  def +(other)
235
242
  if Duration === other
236
- parts = @parts.dup
237
- other.parts.each do |(key, value)|
238
- parts[key] += value
243
+ parts = @parts.merge(other.parts) do |_key, value, other_value|
244
+ value + other_value
239
245
  end
240
246
  Duration.new(value + other.value, parts)
241
247
  else
242
- seconds = @parts[:seconds] + other
248
+ seconds = @parts.fetch(:seconds, 0) + other
243
249
  Duration.new(value + other, @parts.merge(seconds: seconds))
244
250
  end
245
251
  end
@@ -253,9 +259,9 @@ module ActiveSupport
253
259
  # Multiplies this Duration by a Numeric and returns a new Duration.
254
260
  def *(other)
255
261
  if Scalar === other || Duration === other
256
- Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
262
+ Duration.new(value * other.value, parts.transform_values { |number| number * other.value })
257
263
  elsif Numeric === other
258
- Duration.new(value * other, parts.map { |type, number| [type, number * other] })
264
+ Duration.new(value * other, parts.transform_values { |number| number * other })
259
265
  else
260
266
  raise_type_error(other)
261
267
  end
@@ -264,11 +270,11 @@ module ActiveSupport
264
270
  # Divides this Duration by a Numeric and returns a new Duration.
265
271
  def /(other)
266
272
  if Scalar === other
267
- Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
273
+ Duration.new(value / other.value, parts.transform_values { |number| number / other.value })
268
274
  elsif Duration === other
269
275
  value / other.value
270
276
  elsif Numeric === other
271
- Duration.new(value / other, parts.map { |type, number| [type, number / other] })
277
+ Duration.new(value / other, parts.transform_values { |number| number / other })
272
278
  else
273
279
  raise_type_error(other)
274
280
  end
@@ -287,7 +293,11 @@ module ActiveSupport
287
293
  end
288
294
 
289
295
  def -@ #:nodoc:
290
- Duration.new(-value, parts.map { |type, number| [type, -number] })
296
+ Duration.new(-value, parts.transform_values(&:-@))
297
+ end
298
+
299
+ def +@ #:nodoc:
300
+ self
291
301
  end
292
302
 
293
303
  def is_a?(klass) #:nodoc:
@@ -334,12 +344,55 @@ module ActiveSupport
334
344
  # 1.year.to_i # => 31556952
335
345
  #
336
346
  # 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
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
339
349
  # date and time arithmetic.
340
350
  def to_i
341
351
  @value.to_i
342
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
343
396
 
344
397
  # Returns +true+ if +other+ is also a Duration instance, which has the
345
398
  # same parts as this one.
@@ -368,8 +421,9 @@ module ActiveSupport
368
421
  alias :before :ago
369
422
 
370
423
  def inspect #:nodoc:
424
+ return "#{value} seconds" if parts.empty?
425
+
371
426
  parts.
372
- reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
373
427
  sort_by { |unit, _ | PARTS.index(unit) }.
374
428
  map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
375
429
  to_sentence(locale: ::I18n.default_locale)
@@ -379,10 +433,6 @@ module ActiveSupport
379
433
  to_i
380
434
  end
381
435
 
382
- def respond_to_missing?(method, include_private = false) #:nodoc:
383
- @value.respond_to?(method, include_private)
384
- end
385
-
386
436
  def init_with(coder) #:nodoc:
387
437
  initialize(coder["value"], coder["parts"])
388
438
  end
@@ -398,10 +448,15 @@ module ActiveSupport
398
448
  end
399
449
 
400
450
  private
401
-
402
451
  def sum(sign, time = ::Time.current)
403
- parts.inject(time) do |t, (type, number)|
404
- if t.acts_like?(:time) || t.acts_like?(:date)
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)|
405
460
  if type == :seconds
406
461
  t.since(sign * number)
407
462
  elsif type == :minutes
@@ -411,14 +466,16 @@ module ActiveSupport
411
466
  else
412
467
  t.advance(type => sign * number)
413
468
  end
414
- else
415
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
416
469
  end
417
470
  end
418
471
  end
419
472
 
473
+ def respond_to_missing?(method, _)
474
+ value.respond_to?(method)
475
+ end
476
+
420
477
  def method_missing(method, *args, &block)
421
- value.send(method, *args, &block)
478
+ value.public_send(method, *args, &block)
422
479
  end
423
480
 
424
481
  def raise_type_error(other)
@@ -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