activesupport 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Enumerable
4
+ INDEX_WITH_DEFAULT = Object.new
5
+ private_constant :INDEX_WITH_DEFAULT
6
+
7
+ # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
8
+ # when we omit an identity.
9
+
10
+ # :stopdoc:
11
+
12
+ # We can't use Refinements here because Refinements with Module which will be prepended
13
+ # doesn't work well https://bugs.ruby-lang.org/issues/13446
14
+ alias :_original_sum_with_required_identity :sum
15
+ private :_original_sum_with_required_identity
16
+
17
+ # :startdoc:
18
+
19
+ # Calculates a sum from the elements.
20
+ #
21
+ # payments.sum { |p| p.price * p.tax_rate }
22
+ # payments.sum(&:price)
23
+ #
24
+ # The latter is a shortcut for:
25
+ #
26
+ # payments.inject(0) { |sum, p| sum + p.price }
27
+ #
28
+ # It can also calculate the sum without the use of a block.
29
+ #
30
+ # [5, 15, 10].sum # => 30
31
+ # ['foo', 'bar'].sum # => "foobar"
32
+ # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
33
+ #
34
+ # The default sum of an empty list is zero. You can override this default:
35
+ #
36
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
37
+ def sum(identity = nil, &block)
38
+ if identity
39
+ _original_sum_with_required_identity(identity, &block)
40
+ elsif block_given?
41
+ map(&block).sum(identity)
42
+ else
43
+ inject(:+) || 0
44
+ end
45
+ end
46
+
47
+ # Convert an enumerable to a hash keying it by the block return value.
48
+ #
49
+ # people.index_by(&:login)
50
+ # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
51
+ #
52
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
53
+ # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
54
+ def index_by
55
+ if block_given?
56
+ result = {}
57
+ each { |elem| result[yield(elem)] = elem }
58
+ result
59
+ else
60
+ to_enum(:index_by) { size if respond_to?(:size) }
61
+ end
62
+ end
63
+
64
+ # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
65
+ #
66
+ # post = Post.new(title: "hey there", body: "what's up?")
67
+ #
68
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
69
+ # # => { title: "hey there", body: "what's up?" }
70
+ def index_with(default = INDEX_WITH_DEFAULT)
71
+ if block_given?
72
+ result = {}
73
+ each { |elem| result[elem] = yield(elem) }
74
+ result
75
+ elsif default != INDEX_WITH_DEFAULT
76
+ result = {}
77
+ each { |elem| result[elem] = default }
78
+ result
79
+ else
80
+ to_enum(:index_with) { size if respond_to?(:size) }
81
+ end
82
+ end
83
+
84
+ # Returns +true+ if the enumerable has more than 1 element. Functionally
85
+ # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
86
+ # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
87
+ # if more than one person is over 26.
88
+ def many?
89
+ cnt = 0
90
+ if block_given?
91
+ any? do |element|
92
+ cnt += 1 if yield element
93
+ cnt > 1
94
+ end
95
+ else
96
+ any? { (cnt += 1) > 1 }
97
+ end
98
+ end
99
+
100
+ # Returns a new array that includes the passed elements.
101
+ #
102
+ # [ 1, 2, 3 ].including(4, 5)
103
+ # # => [ 1, 2, 3, 4, 5 ]
104
+ #
105
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
106
+ # # => ["David", "Rafael", "Aaron", "Todd"]
107
+ def including(*elements)
108
+ to_a.including(*elements)
109
+ end
110
+
111
+ # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
112
+ # collection does not include the object.
113
+ def exclude?(object)
114
+ !include?(object)
115
+ end
116
+
117
+ # Returns a copy of the enumerable excluding the specified elements.
118
+ #
119
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
120
+ # # => ["David", "Rafael"]
121
+ #
122
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
123
+ # # => ["David", "Rafael"]
124
+ #
125
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
126
+ # # => {foo: 1, baz: 3}
127
+ def excluding(*elements)
128
+ elements.flatten!(1)
129
+ reject { |element| elements.include?(element) }
130
+ end
131
+
132
+ # Alias for #excluding.
133
+ def without(*elements)
134
+ excluding(*elements)
135
+ end
136
+
137
+ # Convert an enumerable to an array based on the given key.
138
+ #
139
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
140
+ # # => ["David", "Rafael", "Aaron"]
141
+ #
142
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
143
+ # # => [[1, "David"], [2, "Rafael"]]
144
+ def pluck(*keys)
145
+ if keys.many?
146
+ map { |element| keys.map { |key| element[key] } }
147
+ else
148
+ map { |element| element[keys.first] }
149
+ end
150
+ end
151
+ end
152
+
153
+ class Range #:nodoc:
154
+ # Optimize range sum to use arithmetic progression if a block is not given and
155
+ # we have a range of numeric values.
156
+ def sum(identity = nil)
157
+ if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
158
+ super
159
+ else
160
+ actual_last = exclude_end? ? (last - 1) : last
161
+ if actual_last >= first
162
+ sum = identity || 0
163
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
164
+ else
165
+ identity || 0
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ # Using Refinements here in order not to expose our internal method
172
+ using Module.new {
173
+ refine Array do
174
+ alias :orig_sum :sum
175
+ end
176
+ }
177
+
178
+ class Array #:nodoc:
179
+ # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
180
+ def sum(init = nil, &block)
181
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
182
+ init ||= 0
183
+ orig_sum(init, &block)
184
+ else
185
+ super
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/file/atomic"
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ class File
6
+ # Write to a file atomically. Useful for situations where you don't
7
+ # want other processes or threads to see half-written files.
8
+ #
9
+ # File.atomic_write('important.file') do |file|
10
+ # file.write('hello')
11
+ # end
12
+ #
13
+ # This method needs to create a temporary file. By default it will create it
14
+ # in the same directory as the destination file. If you don't like this
15
+ # behavior you can provide a different directory but it must be on the
16
+ # same physical filesystem as the file you're trying to write.
17
+ #
18
+ # File.atomic_write('/data/something.important', '/data/tmp') do |file|
19
+ # file.write('hello')
20
+ # end
21
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
22
+ require "tempfile" unless defined?(Tempfile)
23
+
24
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
25
+ temp_file.binmode
26
+ return_val = yield temp_file
27
+ temp_file.close
28
+
29
+ old_stat = if exist?(file_name)
30
+ # Get original file permissions
31
+ stat(file_name)
32
+ else
33
+ # If not possible, probe which are the default permissions in the
34
+ # destination directory.
35
+ probe_stat_in(dirname(file_name))
36
+ end
37
+
38
+ if old_stat
39
+ # Set correct permissions on new file
40
+ begin
41
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
42
+ # This operation will affect filesystem ACL's
43
+ chmod(old_stat.mode, temp_file.path)
44
+ rescue Errno::EPERM, Errno::EACCES
45
+ # Changing file ownership failed, moving on.
46
+ end
47
+ end
48
+
49
+ # Overwrite original file with temp file
50
+ rename(temp_file.path, file_name)
51
+ return_val
52
+ end
53
+ end
54
+
55
+ # Private utility method.
56
+ def self.probe_stat_in(dir) #:nodoc:
57
+ basename = [
58
+ ".permissions_check",
59
+ Thread.current.object_id,
60
+ Process.pid,
61
+ rand(1000000)
62
+ ].join(".")
63
+
64
+ file_name = join(dir, basename)
65
+ FileUtils.touch(file_name)
66
+ stat(file_name)
67
+ ensure
68
+ FileUtils.rm_f(file_name) if file_name
69
+ end
70
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/conversions"
4
+ require "active_support/core_ext/hash/deep_merge"
5
+ require "active_support/core_ext/hash/deep_transform_values"
6
+ require "active_support/core_ext/hash/except"
7
+ require "active_support/core_ext/hash/indifferent_access"
8
+ require "active_support/core_ext/hash/keys"
9
+ require "active_support/core_ext/hash/reverse_merge"
10
+ require "active_support/core_ext/hash/slice"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/deprecation"
4
+
5
+ ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
@@ -0,0 +1,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/xml_mini"
4
+ require "active_support/time"
5
+ require "active_support/core_ext/object/blank"
6
+ require "active_support/core_ext/object/to_param"
7
+ require "active_support/core_ext/object/to_query"
8
+ require "active_support/core_ext/array/wrap"
9
+ require "active_support/core_ext/hash/reverse_merge"
10
+ require "active_support/core_ext/string/inflections"
11
+
12
+ class Hash
13
+ # Returns a string containing an XML representation of its receiver:
14
+ #
15
+ # { foo: 1, bar: 2 }.to_xml
16
+ # # =>
17
+ # # <?xml version="1.0" encoding="UTF-8"?>
18
+ # # <hash>
19
+ # # <foo type="integer">1</foo>
20
+ # # <bar type="integer">2</bar>
21
+ # # </hash>
22
+ #
23
+ # To do so, the method loops over the pairs and builds nodes that depend on
24
+ # the _values_. Given a pair +key+, +value+:
25
+ #
26
+ # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
27
+ #
28
+ # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
29
+ # and +key+ singularized as <tt>:children</tt>.
30
+ #
31
+ # * If +value+ is a callable object it must expect one or two arguments. Depending
32
+ # on the arity, the callable is invoked with the +options+ hash as first argument
33
+ # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
34
+ # callable can add nodes by using <tt>options[:builder]</tt>.
35
+ #
36
+ # {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
37
+ # # => "<b>foo</b>"
38
+ #
39
+ # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
40
+ #
41
+ # class Foo
42
+ # def to_xml(options)
43
+ # options[:builder].bar 'fooing!'
44
+ # end
45
+ # end
46
+ #
47
+ # { foo: Foo.new }.to_xml(skip_instruct: true)
48
+ # # =>
49
+ # # <hash>
50
+ # # <bar>fooing!</bar>
51
+ # # </hash>
52
+ #
53
+ # * Otherwise, a node with +key+ as tag is created with a string representation of
54
+ # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
55
+ # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
56
+ # added as well according to the following mapping:
57
+ #
58
+ # XML_TYPE_NAMES = {
59
+ # "Symbol" => "symbol",
60
+ # "Integer" => "integer",
61
+ # "BigDecimal" => "decimal",
62
+ # "Float" => "float",
63
+ # "TrueClass" => "boolean",
64
+ # "FalseClass" => "boolean",
65
+ # "Date" => "date",
66
+ # "DateTime" => "dateTime",
67
+ # "Time" => "dateTime"
68
+ # }
69
+ #
70
+ # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
71
+ #
72
+ # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
73
+ # configure your own builder with the <tt>:builder</tt> option. The method also accepts
74
+ # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
75
+ def to_xml(options = {})
76
+ require "active_support/builder" unless defined?(Builder)
77
+
78
+ options = options.dup
79
+ options[:indent] ||= 2
80
+ options[:root] ||= "hash"
81
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
82
+
83
+ builder = options[:builder]
84
+ builder.instruct! unless options.delete(:skip_instruct)
85
+
86
+ root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
87
+
88
+ builder.tag!(root) do
89
+ each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
90
+ yield builder if block_given?
91
+ end
92
+ end
93
+
94
+ class << self
95
+ # Returns a Hash containing a collection of pairs when the key is the node name and the value is
96
+ # its content
97
+ #
98
+ # xml = <<-XML
99
+ # <?xml version="1.0" encoding="UTF-8"?>
100
+ # <hash>
101
+ # <foo type="integer">1</foo>
102
+ # <bar type="integer">2</bar>
103
+ # </hash>
104
+ # XML
105
+ #
106
+ # hash = Hash.from_xml(xml)
107
+ # # => {"hash"=>{"foo"=>1, "bar"=>2}}
108
+ #
109
+ # +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or
110
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
111
+ # parse this XML.
112
+ #
113
+ # Custom +disallowed_types+ can also be passed in the form of an
114
+ # array.
115
+ #
116
+ # xml = <<-XML
117
+ # <?xml version="1.0" encoding="UTF-8"?>
118
+ # <hash>
119
+ # <foo type="integer">1</foo>
120
+ # <bar type="string">"David"</bar>
121
+ # </hash>
122
+ # XML
123
+ #
124
+ # hash = Hash.from_xml(xml, ['integer'])
125
+ # # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
126
+ #
127
+ # Note that passing custom disallowed types will override the default types,
128
+ # which are Symbol and YAML.
129
+ def from_xml(xml, disallowed_types = nil)
130
+ ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
131
+ end
132
+
133
+ # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
134
+ def from_trusted_xml(xml)
135
+ from_xml xml, []
136
+ end
137
+ end
138
+ end
139
+
140
+ module ActiveSupport
141
+ class XMLConverter # :nodoc:
142
+ # Raised if the XML contains attributes with type="yaml" or
143
+ # type="symbol". Read Hash#from_xml for more details.
144
+ class DisallowedType < StandardError
145
+ def initialize(type)
146
+ super "Disallowed type attribute: #{type.inspect}"
147
+ end
148
+ end
149
+
150
+ DISALLOWED_TYPES = %w(symbol yaml)
151
+
152
+ def initialize(xml, disallowed_types = nil)
153
+ @xml = normalize_keys(XmlMini.parse(xml))
154
+ @disallowed_types = disallowed_types || DISALLOWED_TYPES
155
+ end
156
+
157
+ def to_h
158
+ deep_to_h(@xml)
159
+ end
160
+
161
+ private
162
+ def normalize_keys(params)
163
+ case params
164
+ when Hash
165
+ Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
166
+ when Array
167
+ params.map { |v| normalize_keys(v) }
168
+ else
169
+ params
170
+ end
171
+ end
172
+
173
+ def deep_to_h(value)
174
+ case value
175
+ when Hash
176
+ process_hash(value)
177
+ when Array
178
+ process_array(value)
179
+ when String
180
+ value
181
+ else
182
+ raise "can't typecast #{value.class.name} - #{value.inspect}"
183
+ end
184
+ end
185
+
186
+ def process_hash(value)
187
+ if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"])
188
+ raise DisallowedType, value["type"]
189
+ end
190
+
191
+ if become_array?(value)
192
+ _, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
193
+ if entries.nil? || value["__content__"].try(:empty?)
194
+ []
195
+ else
196
+ case entries
197
+ when Array
198
+ entries.collect { |v| deep_to_h(v) }
199
+ when Hash
200
+ [deep_to_h(entries)]
201
+ else
202
+ raise "can't typecast #{entries.inspect}"
203
+ end
204
+ end
205
+ elsif become_content?(value)
206
+ process_content(value)
207
+
208
+ elsif become_empty_string?(value)
209
+ ""
210
+ elsif become_hash?(value)
211
+ xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
212
+
213
+ # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
214
+ # how multipart uploaded files from HTML appear
215
+ xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
216
+ end
217
+ end
218
+
219
+ def become_content?(value)
220
+ value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
221
+ end
222
+
223
+ def become_array?(value)
224
+ value["type"] == "array"
225
+ end
226
+
227
+ def become_empty_string?(value)
228
+ # { "string" => true }
229
+ # No tests fail when the second term is removed.
230
+ value["type"] == "string" && value["nil"] != "true"
231
+ end
232
+
233
+ def become_hash?(value)
234
+ !nothing?(value) && !garbage?(value)
235
+ end
236
+
237
+ def nothing?(value)
238
+ # blank or nil parsed values are represented by nil
239
+ value.blank? || value["nil"] == "true"
240
+ end
241
+
242
+ def garbage?(value)
243
+ # If the type is the only element which makes it then
244
+ # this still makes the value nil, except if type is
245
+ # an XML node(where type['value'] is a Hash)
246
+ value["type"] && !value["type"].is_a?(::Hash) && value.size == 1
247
+ end
248
+
249
+ def process_content(value)
250
+ content = value["__content__"]
251
+ if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
252
+ parser.arity == 1 ? parser.call(content) : parser.call(content, value)
253
+ else
254
+ content
255
+ end
256
+ end
257
+
258
+ def process_array(value)
259
+ value.map! { |i| deep_to_h(i) }
260
+ value.length > 1 ? value : value.first
261
+ end
262
+ end
263
+ end