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,57 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module EnumerableCoreExt # :nodoc:
5
+ module Constants
6
+ private
7
+ def const_missing(name)
8
+ if name == :SoleItemExpectedError
9
+ ::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError
10
+ else
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
1
18
  module Enumerable
2
- # Enumerable#sum was added in Ruby 2.4 but it only works with Numeric elements
19
+ # Error generated by +sole+ when called on an enumerable that doesn't have
20
+ # exactly one item.
21
+ class SoleItemExpectedError < StandardError; end
22
+
23
+ # HACK: For performance reasons, Enumerable shouldn't have any constants of its own.
24
+ # So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt.
25
+ ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
26
+ singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
27
+
28
+ # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
3
29
  # when we omit an identity.
30
+
31
+ # :stopdoc:
32
+
33
+ # We can't use Refinements here because Refinements with Module which will be prepended
34
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
35
+ alias :_original_sum_with_required_identity :sum
36
+ private :_original_sum_with_required_identity
37
+
38
+ # :startdoc:
39
+
40
+ # Calculates the minimum from the extracted elements.
4
41
  #
5
- # We tried shimming it to attempt the fast native method, rescue TypeError,
6
- # and fall back to the compatible implementation, but that's much slower than
7
- # just calling the compat method in the first place.
8
- if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
9
- # We can't use Refinements here because Refinements with Module which will be prepended
10
- # doesn't work well https://bugs.ruby-lang.org/issues/13446
11
- alias :_original_sum_with_required_identity :sum
12
- private :_original_sum_with_required_identity
13
- # Calculates a sum from the elements.
14
- #
15
- # payments.sum { |p| p.price * p.tax_rate }
16
- # payments.sum(&:price)
17
- #
18
- # The latter is a shortcut for:
19
- #
20
- # payments.inject(0) { |sum, p| sum + p.price }
21
- #
22
- # It can also calculate the sum without the use of a block.
23
- #
24
- # [5, 15, 10].sum # => 30
25
- # ['foo', 'bar'].sum # => "foobar"
26
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
27
- #
28
- # The default sum of an empty list is zero. You can override this default:
29
- #
30
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
31
- def sum(identity = nil, &block)
32
- if identity
33
- _original_sum_with_required_identity(identity, &block)
34
- elsif block_given?
35
- map(&block).sum(identity)
36
- else
37
- inject(:+) || 0
38
- end
39
- end
40
- else
41
- def sum(identity = nil, &block)
42
- if block_given?
43
- map(&block).sum(identity)
44
- else
45
- sum = identity ? inject(identity, :+) : inject(:+)
46
- sum || identity || 0
47
- end
42
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
43
+ # payments.minimum(:price) # => 5
44
+ def minimum(key)
45
+ map(&key).min
46
+ end
47
+
48
+ # Calculates the maximum from the extracted elements.
49
+ #
50
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
51
+ # payments.maximum(:price) # => 15
52
+ def maximum(key)
53
+ map(&key).max
54
+ end
55
+
56
+ # Calculates a sum from the elements.
57
+ #
58
+ # payments.sum { |p| p.price * p.tax_rate }
59
+ # payments.sum(&:price)
60
+ #
61
+ # The latter is a shortcut for:
62
+ #
63
+ # payments.inject(0) { |sum, p| sum + p.price }
64
+ #
65
+ # It can also calculate the sum without the use of a block.
66
+ #
67
+ # [5, 15, 10].sum # => 30
68
+ # ['foo', 'bar'].sum('') # => "foobar"
69
+ # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
70
+ #
71
+ # The default sum of an empty list is zero. You can override this default:
72
+ #
73
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
74
+ def sum(identity = nil, &block)
75
+ if identity
76
+ _original_sum_with_required_identity(identity, &block)
77
+ elsif block_given?
78
+ map(&block).sum
79
+ # we check `first(1) == []` to check if we have an
80
+ # empty Enumerable; checking `empty?` would return
81
+ # true for `[nil]`, which we want to deprecate to
82
+ # keep consistent with Ruby
83
+ elsif first.is_a?(Numeric) || first(1) == []
84
+ identity ||= 0
85
+ _original_sum_with_required_identity(identity, &block)
86
+ else
87
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
88
+ Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
89
+ Sum of non-numeric elements requires an initial argument.
90
+ MSG
91
+ inject(:+) || 0
48
92
  end
49
93
  end
50
94
 
51
- # Convert an enumerable to a hash.
95
+ # Convert an enumerable to a hash, using the block result as the key and the
96
+ # element as the value.
52
97
  #
53
98
  # people.index_by(&:login)
54
99
  # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
100
+ #
55
101
  # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
56
102
  # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
57
103
  def index_by
@@ -64,6 +110,33 @@ module Enumerable
64
110
  end
65
111
  end
66
112
 
113
+ # Convert an enumerable to a hash, using the element as the key and the block
114
+ # result as the value.
115
+ #
116
+ # post = Post.new(title: "hey there", body: "what's up?")
117
+ #
118
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
119
+ # # => { title: "hey there", body: "what's up?" }
120
+ #
121
+ # If an argument is passed instead of a block, it will be used as the value
122
+ # for all elements:
123
+ #
124
+ # %i( created_at updated_at ).index_with(Time.now)
125
+ # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
126
+ def index_with(default = (no_default = true))
127
+ if block_given?
128
+ result = {}
129
+ each { |elem| result[elem] = yield(elem) }
130
+ result
131
+ elsif no_default
132
+ to_enum(:index_with) { size if respond_to?(:size) }
133
+ else
134
+ result = {}
135
+ each { |elem| result[elem] = default }
136
+ result
137
+ end
138
+ end
139
+
67
140
  # Returns +true+ if the enumerable has more than 1 element. Functionally
68
141
  # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
69
142
  # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
@@ -80,24 +153,40 @@ module Enumerable
80
153
  end
81
154
  end
82
155
 
156
+ # Returns a new array that includes the passed elements.
157
+ #
158
+ # [ 1, 2, 3 ].including(4, 5)
159
+ # # => [ 1, 2, 3, 4, 5 ]
160
+ #
161
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
162
+ # # => ["David", "Rafael", "Aaron", "Todd"]
163
+ def including(*elements)
164
+ to_a.including(*elements)
165
+ end
166
+
83
167
  # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
84
168
  # collection does not include the object.
85
169
  def exclude?(object)
86
170
  !include?(object)
87
171
  end
88
172
 
89
- # Returns a copy of the enumerable without the specified elements.
173
+ # Returns a copy of the enumerable excluding the specified elements.
174
+ #
175
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
176
+ # # => ["David", "Rafael"]
90
177
  #
91
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
178
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
92
179
  # # => ["David", "Rafael"]
93
180
  #
94
- # {foo: 1, bar: 2, baz: 3}.without :bar
181
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
95
182
  # # => {foo: 1, baz: 3}
96
- def without(*elements)
183
+ def excluding(*elements)
184
+ elements.flatten!(1)
97
185
  reject { |element| elements.include?(element) }
98
186
  end
187
+ alias :without :excluding
99
188
 
100
- # Convert an enumerable to an array based on the given key.
189
+ # Extract the given key from each element in the enumerable.
101
190
  #
102
191
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
103
192
  # # => ["David", "Rafael", "Aaron"]
@@ -108,12 +197,91 @@ module Enumerable
108
197
  if keys.many?
109
198
  map { |element| keys.map { |key| element[key] } }
110
199
  else
111
- map { |element| element[keys.first] }
200
+ key = keys.first
201
+ map { |element| element[key] }
202
+ end
203
+ end
204
+
205
+ # Extract the given key from the first element in the enumerable.
206
+ #
207
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
208
+ # # => "David"
209
+ #
210
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
211
+ # # => [1, "David"]
212
+ def pick(*keys)
213
+ return if none?
214
+
215
+ if keys.many?
216
+ keys.map { |key| first[key] }
217
+ else
218
+ first[keys.first]
219
+ end
220
+ end
221
+
222
+ # Returns a new +Array+ without the blank items.
223
+ # Uses Object#blank? for determining if an item is blank.
224
+ #
225
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
226
+ # # => [1, 2, true]
227
+ #
228
+ # Set.new([nil, "", 1, 2])
229
+ # # => [2, 1] (or [1, 2])
230
+ #
231
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
232
+ #
233
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
234
+ # # => { b: 1, f: true }
235
+ def compact_blank
236
+ reject(&:blank?)
237
+ end
238
+
239
+ # Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
240
+ # objects in the original enumerable.
241
+ #
242
+ # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
243
+ # # => [ Person.find(1), Person.find(5), Person.find(3) ]
244
+ #
245
+ # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
246
+ # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
247
+ def in_order_of(key, series)
248
+ index_by(&key).values_at(*series).compact
249
+ end
250
+
251
+ # Returns the sole item in the enumerable. If there are no items, or more
252
+ # than one item, raises +Enumerable::SoleItemExpectedError+.
253
+ #
254
+ # ["x"].sole # => "x"
255
+ # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
256
+ # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
257
+ def sole
258
+ case count
259
+ when 1 then return first # rubocop:disable Style/RedundantReturn
260
+ when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
261
+ when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
112
262
  end
113
263
  end
114
264
  end
115
265
 
116
- class Range #:nodoc:
266
+ class Hash
267
+ # Hash#reject has its own definition, so this needs one too.
268
+ def compact_blank # :nodoc:
269
+ reject { |_k, v| v.blank? }
270
+ end
271
+
272
+ # Removes all blank values from the +Hash+ in place and returns self.
273
+ # Uses Object#blank? for determining if a value is blank.
274
+ #
275
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
276
+ # h.compact_blank!
277
+ # # => { b: 1, f: true }
278
+ def compact_blank!
279
+ # use delete_if rather than reject! because it always returns self even if nothing changed
280
+ delete_if { |_k, v| v.blank? }
281
+ end
282
+ end
283
+
284
+ class Range # :nodoc:
117
285
  # Optimize range sum to use arithmetic progression if a block is not given and
118
286
  # we have a range of numeric values.
119
287
  def sum(identity = nil)
@@ -131,27 +299,31 @@ class Range #:nodoc:
131
299
  end
132
300
  end
133
301
 
134
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
135
- #
136
- # We tried shimming it to attempt the fast native method, rescue TypeError,
137
- # and fall back to the compatible implementation, but that's much slower than
138
- # just calling the compat method in the first place.
139
- if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
140
- # Using Refinements here in order not to expose our internal method
141
- using Module.new {
142
- refine Array do
143
- alias :orig_sum :sum
144
- end
145
- }
302
+ # Using Refinements here in order not to expose our internal method
303
+ using Module.new {
304
+ refine Array do
305
+ alias :orig_sum :sum
306
+ end
307
+ }
146
308
 
147
- class Array
148
- def sum(init = nil, &block) #:nodoc:
149
- if init.is_a?(Numeric) || first.is_a?(Numeric)
150
- init ||= 0
151
- orig_sum(init, &block)
152
- else
153
- super
154
- end
309
+ class Array # :nodoc:
310
+ def sum(init = nil, &block)
311
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
312
+ init ||= 0
313
+ orig_sum(init, &block)
314
+ else
315
+ super
155
316
  end
156
317
  end
318
+
319
+ # Removes all blank elements from the +Array+ in place and returns self.
320
+ # Uses Object#blank? for determining if an item is blank.
321
+ #
322
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
323
+ # a.compact_blank!
324
+ # # => [1, 2, true]
325
+ def compact_blank!
326
+ # use delete_if rather than reject! because it always returns self even if nothing changed
327
+ delete_if(&:blank?)
328
+ end
157
329
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "fileutils"
2
4
 
3
5
  class File
@@ -27,7 +29,7 @@ class File
27
29
  old_stat = if exist?(file_name)
28
30
  # Get original file permissions
29
31
  stat(file_name)
30
- elsif temp_dir != dirname(file_name)
32
+ else
31
33
  # If not possible, probe which are the default permissions in the
32
34
  # destination directory.
33
35
  probe_stat_in(dirname(file_name))
@@ -51,7 +53,7 @@ class File
51
53
  end
52
54
 
53
55
  # Private utility method.
54
- def self.probe_stat_in(dir) #:nodoc:
56
+ def self.probe_stat_in(dir) # :nodoc:
55
57
  basename = [
56
58
  ".permissions_check",
57
59
  Thread.current.object_id,
@@ -62,6 +64,8 @@ class File
62
64
  file_name = join(dir, basename)
63
65
  FileUtils.touch(file_name)
64
66
  stat(file_name)
67
+ rescue Errno::ENOENT
68
+ file_name = nil
65
69
  ensure
66
70
  FileUtils.rm_f(file_name) if file_name
67
71
  end
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/file/atomic"
@@ -1,8 +1,9 @@
1
- require "active_support/xml_mini"
2
- require "active_support/time"
1
+ # frozen_string_literal: true
2
+
3
3
  require "active_support/core_ext/object/blank"
4
4
  require "active_support/core_ext/object/to_param"
5
5
  require "active_support/core_ext/object/to_query"
6
+ require "active_support/core_ext/object/try"
6
7
  require "active_support/core_ext/array/wrap"
7
8
  require "active_support/core_ext/hash/reverse_merge"
8
9
  require "active_support/core_ext/string/inflections"
@@ -71,7 +72,7 @@ class Hash
71
72
  # configure your own builder with the <tt>:builder</tt> option. The method also accepts
72
73
  # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
73
74
  def to_xml(options = {})
74
- require "active_support/builder" unless defined?(Builder)
75
+ require "active_support/builder" unless defined?(Builder::XmlMarkup)
75
76
 
76
77
  options = options.dup
77
78
  options[:indent] ||= 2
@@ -163,7 +164,7 @@ module ActiveSupport
163
164
  Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
164
165
  when Array
165
166
  params.map { |v| normalize_keys(v) }
166
- else
167
+ else
167
168
  params
168
169
  end
169
170
  end
@@ -176,7 +177,7 @@ module ActiveSupport
176
177
  process_array(value)
177
178
  when String
178
179
  value
179
- else
180
+ else
180
181
  raise "can't typecast #{value.class.name} - #{value.inspect}"
181
182
  end
182
183
  end
@@ -206,7 +207,7 @@ module ActiveSupport
206
207
  elsif become_empty_string?(value)
207
208
  ""
208
209
  elsif become_hash?(value)
209
- xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
210
+ xml_value = value.transform_values { |v| deep_to_h(v) }
210
211
 
211
212
  # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
212
213
  # how multipart uploaded files from HTML appear
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Hash
2
4
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
5
  #
@@ -19,20 +21,14 @@ class Hash
19
21
 
20
22
  # Same as +deep_merge+, but modifies +self+.
21
23
  def deep_merge!(other_hash, &block)
22
- other_hash.each_pair do |current_key, other_value|
23
- this_value = self[current_key]
24
-
25
- self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
26
- this_value.deep_merge(other_value, &block)
24
+ merge!(other_hash) do |key, this_val, other_val|
25
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
26
+ this_val.deep_merge(other_val, &block)
27
+ elsif block_given?
28
+ block.call(key, this_val, other_val)
27
29
  else
28
- if block_given? && key?(current_key)
29
- block.call(current_key, this_value, other_value)
30
- else
31
- other_value
32
- end
30
+ other_val
33
31
  end
34
32
  end
35
-
36
- self
37
33
  end
38
34
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Returns a new hash with all values converted by the block operation.
5
+ # This includes the values from the root hash and from all
6
+ # nested hashes and arrays.
7
+ #
8
+ # hash = { person: { name: 'Rob', age: '28' } }
9
+ #
10
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
11
+ # # => {person: {name: "ROB", age: "28"}}
12
+ def deep_transform_values(&block)
13
+ _deep_transform_values_in_object(self, &block)
14
+ end
15
+
16
+ # Destructively converts all values by using the block operation.
17
+ # This includes the values from the root hash and from all
18
+ # nested hashes and arrays.
19
+ def deep_transform_values!(&block)
20
+ _deep_transform_values_in_object!(self, &block)
21
+ end
22
+
23
+ private
24
+ # Support methods for deep transforming nested hashes and arrays.
25
+ def _deep_transform_values_in_object(object, &block)
26
+ case object
27
+ when Hash
28
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
29
+ when Array
30
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
31
+ else
32
+ yield(object)
33
+ end
34
+ end
35
+
36
+ def _deep_transform_values_in_object!(object, &block)
37
+ case object
38
+ when Hash
39
+ object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
40
+ when Array
41
+ object.map! { |e| _deep_transform_values_in_object!(e, &block) }
42
+ else
43
+ yield(object)
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Hash
2
4
  # Returns a hash that includes everything except given keys.
3
5
  # hash = { a: true, b: false, c: nil }
@@ -8,8 +10,8 @@ class Hash
8
10
  # This is useful for limiting a set of parameters to everything but a few known toggles:
9
11
  # @person.update(params[:person].except(:admin))
10
12
  def except(*keys)
11
- dup.except!(*keys)
12
- end
13
+ slice(*self.keys - keys)
14
+ end unless method_defined?(:except)
13
15
 
14
16
  # Removes the given keys from hash and returns it.
15
17
  # hash = { a: true, b: false, c: nil }
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/hash_with_indifferent_access"
2
4
 
3
5
  class Hash
4
- # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
6
+ # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
5
7
  #
6
8
  # { a: 1 }.with_indifferent_access['a'] # => 1
7
9
  def with_indifferent_access
@@ -11,8 +13,8 @@ class Hash
11
13
  # Called when object is nested under an object that receives
12
14
  # #with_indifferent_access. This method will be called on the current object
13
15
  # by the enclosing object and is aliased to #with_indifferent_access by
14
- # default. Subclasses of Hash may overwrite this method to return +self+ if
15
- # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
16
+ # default. Subclasses of Hash may override this method to return +self+ if
17
+ # converting to an ActiveSupport::HashWithIndifferentAccess would not be
16
18
  # desirable.
17
19
  #
18
20
  # b = { b: 1 }
@@ -1,33 +1,6 @@
1
- class Hash
2
- # Returns a new hash with all keys converted using the +block+ operation.
3
- #
4
- # hash = { name: 'Rob', age: '28' }
5
- #
6
- # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
7
- #
8
- # If you do not provide a +block+, it will return an Enumerator
9
- # for chaining with other methods:
10
- #
11
- # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
12
- def transform_keys
13
- return enum_for(:transform_keys) { size } unless block_given?
14
- result = {}
15
- each_key do |key|
16
- result[yield(key)] = self[key]
17
- end
18
- result
19
- end unless method_defined? :transform_keys
20
-
21
- # Destructively converts all keys using the +block+ operations.
22
- # Same as +transform_keys+ but modifies +self+.
23
- def transform_keys!
24
- return enum_for(:transform_keys!) { size } unless block_given?
25
- keys.each do |key|
26
- self[yield(key)] = delete(key)
27
- end
28
- self
29
- end unless method_defined? :transform_keys!
1
+ # frozen_string_literal: true
30
2
 
3
+ class Hash
31
4
  # Returns a new hash with all keys converted to strings.
32
5
  #
33
6
  # hash = { name: 'Rob', age: '28' }
@@ -139,11 +112,11 @@ class Hash
139
112
  end
140
113
 
141
114
  private
142
- # support methods for deep transforming nested hashes and arrays
115
+ # Support methods for deep transforming nested hashes and arrays.
143
116
  def _deep_transform_keys_in_object(object, &block)
144
117
  case object
145
118
  when Hash
146
- object.each_with_object({}) do |(key, value), result|
119
+ object.each_with_object(self.class.new) do |(key, value), result|
147
120
  result[yield(key)] = _deep_transform_keys_in_object(value, &block)
148
121
  end
149
122
  when Array
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Hash
2
4
  # Merges the caller into +other_hash+. For example,
3
5
  #
@@ -12,11 +14,12 @@ class Hash
12
14
  def reverse_merge(other_hash)
13
15
  other_hash.merge(self)
14
16
  end
17
+ alias_method :with_defaults, :reverse_merge
15
18
 
16
19
  # Destructive +reverse_merge+.
17
20
  def reverse_merge!(other_hash)
18
- # right wins if there is no left
19
- merge!(other_hash) { |key, left, right| left }
21
+ replace(reverse_merge(other_hash))
20
22
  end
21
23
  alias_method :reverse_update, :reverse_merge!
24
+ alias_method :with_defaults!, :reverse_merge!
22
25
  end