activesupport 5.2.7.1 → 6.1.4.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -434
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +34 -34
  10. data/lib/active_support/cache/mem_cache_store.rb +39 -24
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +72 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +148 -78
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +70 -3
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/enumerable.rb +171 -75
  37. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +2 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  41. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  42. data/lib/active_support/core_ext/hash.rb +1 -2
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/marshal.rb +2 -0
  47. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  48. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  49. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  50. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  51. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  52. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  53. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  54. data/lib/active_support/core_ext/module.rb +0 -1
  55. data/lib/active_support/core_ext/name_error.rb +29 -2
  56. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  57. data/lib/active_support/core_ext/numeric.rb +0 -1
  58. data/lib/active_support/core_ext/object/blank.rb +1 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  61. data/lib/active_support/core_ext/object/json.rb +14 -2
  62. data/lib/active_support/core_ext/object/try.rb +17 -7
  63. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  64. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  65. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  66. data/lib/active_support/core_ext/range/each.rb +0 -1
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  68. data/lib/active_support/core_ext/regexp.rb +8 -5
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +5 -16
  71. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  72. data/lib/active_support/core_ext/string/filters.rb +42 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  74. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  75. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  76. data/lib/active_support/core_ext/string/output_safety.rb +70 -41
  77. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  78. data/lib/active_support/core_ext/string/strip.rb +3 -1
  79. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  80. data/lib/active_support/core_ext/symbol.rb +3 -0
  81. data/lib/active_support/core_ext/time/calculations.rb +51 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  83. data/lib/active_support/core_ext/uri.rb +6 -1
  84. data/lib/active_support/core_ext.rb +1 -1
  85. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  86. data/lib/active_support/current_attributes.rb +16 -2
  87. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/deprecation/behaviors.rb +16 -3
  90. data/lib/active_support/deprecation/disallowed.rb +56 -0
  91. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  92. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  94. data/lib/active_support/deprecation/reporting.rb +50 -7
  95. data/lib/active_support/deprecation.rb +6 -1
  96. data/lib/active_support/descendants_tracker.rb +59 -9
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  98. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  99. data/lib/active_support/duration.rb +78 -30
  100. data/lib/active_support/encrypted_configuration.rb +0 -4
  101. data/lib/active_support/encrypted_file.rb +22 -4
  102. data/lib/active_support/environment_inquirer.rb +20 -0
  103. data/lib/active_support/evented_file_update_checker.rb +82 -117
  104. data/lib/active_support/execution_wrapper.rb +2 -1
  105. data/lib/active_support/file_update_checker.rb +0 -1
  106. data/lib/active_support/fork_tracker.rb +64 -0
  107. data/lib/active_support/gem_version.rb +4 -4
  108. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  109. data/lib/active_support/i18n.rb +1 -0
  110. data/lib/active_support/i18n_railtie.rb +15 -8
  111. data/lib/active_support/inflector/inflections.rb +2 -7
  112. data/lib/active_support/inflector/methods.rb +49 -58
  113. data/lib/active_support/inflector/transliterate.rb +47 -18
  114. data/lib/active_support/json/decoding.rb +25 -26
  115. data/lib/active_support/json/encoding.rb +11 -3
  116. data/lib/active_support/key_generator.rb +1 -33
  117. data/lib/active_support/lazy_load_hooks.rb +5 -2
  118. data/lib/active_support/locale/en.rb +33 -0
  119. data/lib/active_support/locale/en.yml +7 -3
  120. data/lib/active_support/log_subscriber.rb +39 -9
  121. data/lib/active_support/logger.rb +2 -17
  122. data/lib/active_support/logger_silence.rb +11 -19
  123. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  124. data/lib/active_support/message_encryptor.rb +8 -13
  125. data/lib/active_support/message_verifier.rb +10 -10
  126. data/lib/active_support/messages/metadata.rb +11 -2
  127. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  128. data/lib/active_support/messages/rotator.rb +10 -9
  129. data/lib/active_support/multibyte/chars.rb +10 -68
  130. data/lib/active_support/multibyte/unicode.rb +15 -327
  131. data/lib/active_support/notifications/fanout.rb +116 -16
  132. data/lib/active_support/notifications/instrumenter.rb +71 -9
  133. data/lib/active_support/notifications.rb +72 -8
  134. data/lib/active_support/number_helper/number_converter.rb +5 -6
  135. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  136. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  137. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  138. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  142. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  143. data/lib/active_support/number_helper.rb +38 -12
  144. data/lib/active_support/option_merger.rb +22 -3
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +13 -3
  147. data/lib/active_support/parameter_filter.rb +133 -0
  148. data/lib/active_support/per_thread_registry.rb +1 -1
  149. data/lib/active_support/rails.rb +1 -10
  150. data/lib/active_support/railtie.rb +23 -1
  151. data/lib/active_support/reloader.rb +4 -5
  152. data/lib/active_support/rescuable.rb +4 -4
  153. data/lib/active_support/secure_compare_rotator.rb +51 -0
  154. data/lib/active_support/security_utils.rb +19 -12
  155. data/lib/active_support/string_inquirer.rb +4 -3
  156. data/lib/active_support/subscriber.rb +72 -28
  157. data/lib/active_support/tagged_logging.rb +42 -8
  158. data/lib/active_support/test_case.rb +91 -0
  159. data/lib/active_support/testing/assertions.rb +30 -9
  160. data/lib/active_support/testing/deprecation.rb +0 -1
  161. data/lib/active_support/testing/file_fixtures.rb +2 -0
  162. data/lib/active_support/testing/isolation.rb +2 -2
  163. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  164. data/lib/active_support/testing/parallelization/server.rb +78 -0
  165. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  166. data/lib/active_support/testing/parallelization.rb +51 -0
  167. data/lib/active_support/testing/stream.rb +1 -2
  168. data/lib/active_support/testing/time_helpers.rb +47 -12
  169. data/lib/active_support/time_with_zone.rb +81 -47
  170. data/lib/active_support/values/time_zone.rb +32 -17
  171. data/lib/active_support/xml_mini/jdom.rb +2 -3
  172. data/lib/active_support/xml_mini/libxml.rb +2 -2
  173. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  174. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  176. data/lib/active_support/xml_mini/rexml.rb +10 -3
  177. data/lib/active_support/xml_mini.rb +2 -10
  178. data/lib/active_support.rb +14 -1
  179. metadata +55 -29
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,64 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enumerable
4
+ INDEX_WITH_DEFAULT = Object.new
5
+ private_constant :INDEX_WITH_DEFAULT
6
+
4
7
  # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
5
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.
6
20
  #
7
- # We tried shimming it to attempt the fast native method, rescue TypeError,
8
- # and fall back to the compatible implementation, but that's much slower than
9
- # just calling the compat method in the first place.
10
- if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
11
- # :stopdoc:
12
-
13
- # We can't use Refinements here because Refinements with Module which will be prepended
14
- # doesn't work well https://bugs.ruby-lang.org/issues/13446
15
- alias :_original_sum_with_required_identity :sum
16
- private :_original_sum_with_required_identity
17
-
18
- # :startdoc:
19
-
20
- # Calculates a sum from the elements.
21
- #
22
- # payments.sum { |p| p.price * p.tax_rate }
23
- # payments.sum(&:price)
24
- #
25
- # The latter is a shortcut for:
26
- #
27
- # payments.inject(0) { |sum, p| sum + p.price }
28
- #
29
- # It can also calculate the sum without the use of a block.
30
- #
31
- # [5, 15, 10].sum # => 30
32
- # ['foo', 'bar'].sum # => "foobar"
33
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
34
- #
35
- # The default sum of an empty list is zero. You can override this default:
36
- #
37
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
38
- def sum(identity = nil, &block)
39
- if identity
40
- _original_sum_with_required_identity(identity, &block)
41
- elsif block_given?
42
- map(&block).sum(identity)
43
- else
44
- inject(:+) || 0
45
- end
46
- end
47
- else
48
- def sum(identity = nil, &block)
49
- if block_given?
50
- map(&block).sum(identity)
51
- else
52
- sum = identity ? inject(identity, :+) : inject(:+)
53
- sum || identity || 0
54
- end
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
55
44
  end
56
45
  end
57
46
 
58
- # Convert an enumerable to a hash.
47
+ # Convert an enumerable to a hash, using the block result as the key and the
48
+ # element as the value.
59
49
  #
60
50
  # people.index_by(&:login)
61
51
  # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
52
+ #
62
53
  # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
63
54
  # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
64
55
  def index_by
@@ -71,6 +62,33 @@ module Enumerable
71
62
  end
72
63
  end
73
64
 
65
+ # Convert an enumerable to a hash, using the element as the key and the block
66
+ # result as the value.
67
+ #
68
+ # post = Post.new(title: "hey there", body: "what's up?")
69
+ #
70
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
71
+ # # => { title: "hey there", body: "what's up?" }
72
+ #
73
+ # If an argument is passed instead of a block, it will be used as the value
74
+ # for all elements:
75
+ #
76
+ # %i( created_at updated_at ).index_with(Time.now)
77
+ # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
78
+ def index_with(default = INDEX_WITH_DEFAULT)
79
+ if block_given?
80
+ result = {}
81
+ each { |elem| result[elem] = yield(elem) }
82
+ result
83
+ elsif default != INDEX_WITH_DEFAULT
84
+ result = {}
85
+ each { |elem| result[elem] = default }
86
+ result
87
+ else
88
+ to_enum(:index_with) { size if respond_to?(:size) }
89
+ end
90
+ end
91
+
74
92
  # Returns +true+ if the enumerable has more than 1 element. Functionally
75
93
  # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
76
94
  # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
@@ -87,24 +105,44 @@ module Enumerable
87
105
  end
88
106
  end
89
107
 
108
+ # Returns a new array that includes the passed elements.
109
+ #
110
+ # [ 1, 2, 3 ].including(4, 5)
111
+ # # => [ 1, 2, 3, 4, 5 ]
112
+ #
113
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
114
+ # # => ["David", "Rafael", "Aaron", "Todd"]
115
+ def including(*elements)
116
+ to_a.including(*elements)
117
+ end
118
+
90
119
  # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
91
120
  # collection does not include the object.
92
121
  def exclude?(object)
93
122
  !include?(object)
94
123
  end
95
124
 
96
- # Returns a copy of the enumerable without the specified elements.
125
+ # Returns a copy of the enumerable excluding the specified elements.
97
126
  #
98
- # ["David", "Rafael", "Aaron", "Todd"].without "Aaron", "Todd"
127
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
99
128
  # # => ["David", "Rafael"]
100
129
  #
101
- # {foo: 1, bar: 2, baz: 3}.without :bar
130
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
131
+ # # => ["David", "Rafael"]
132
+ #
133
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
102
134
  # # => {foo: 1, baz: 3}
103
- def without(*elements)
135
+ def excluding(*elements)
136
+ elements.flatten!(1)
104
137
  reject { |element| elements.include?(element) }
105
138
  end
106
139
 
107
- # Convert an enumerable to an array based on the given key.
140
+ # Alias for #excluding.
141
+ def without(*elements)
142
+ excluding(*elements)
143
+ end
144
+
145
+ # Extract the given key from each element in the enumerable.
108
146
  #
109
147
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
110
148
  # # => ["David", "Rafael", "Aaron"]
@@ -115,9 +153,62 @@ module Enumerable
115
153
  if keys.many?
116
154
  map { |element| keys.map { |key| element[key] } }
117
155
  else
118
- map { |element| element[keys.first] }
156
+ key = keys.first
157
+ map { |element| element[key] }
158
+ end
159
+ end
160
+
161
+ # Extract the given key from the first element in the enumerable.
162
+ #
163
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
164
+ # # => "David"
165
+ #
166
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
167
+ # # => [1, "David"]
168
+ def pick(*keys)
169
+ return if none?
170
+
171
+ if keys.many?
172
+ keys.map { |key| first[key] }
173
+ else
174
+ first[keys.first]
119
175
  end
120
176
  end
177
+
178
+ # Returns a new +Array+ without the blank items.
179
+ # Uses Object#blank? for determining if an item is blank.
180
+ #
181
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
182
+ # # => [1, 2, true]
183
+ #
184
+ # Set.new([nil, "", 1, 2])
185
+ # # => [2, 1] (or [1, 2])
186
+ #
187
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
188
+ #
189
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
190
+ # #=> { b: 1, f: true }
191
+ def compact_blank
192
+ reject(&:blank?)
193
+ end
194
+ end
195
+
196
+ class Hash
197
+ # Hash#reject has its own definition, so this needs one too.
198
+ def compact_blank #:nodoc:
199
+ reject { |_k, v| v.blank? }
200
+ end
201
+
202
+ # Removes all blank values from the +Hash+ in place and returns self.
203
+ # Uses Object#blank? for determining if a value is blank.
204
+ #
205
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
206
+ # h.compact_blank!
207
+ # # => { b: 1, f: true }
208
+ def compact_blank!
209
+ # use delete_if rather than reject! because it always returns self even if nothing changed
210
+ delete_if { |_k, v| v.blank? }
211
+ end
121
212
  end
122
213
 
123
214
  class Range #:nodoc:
@@ -138,27 +229,32 @@ class Range #:nodoc:
138
229
  end
139
230
  end
140
231
 
141
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
142
- #
143
- # We tried shimming it to attempt the fast native method, rescue TypeError,
144
- # and fall back to the compatible implementation, but that's much slower than
145
- # just calling the compat method in the first place.
146
- if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
147
- # Using Refinements here in order not to expose our internal method
148
- using Module.new {
149
- refine Array do
150
- alias :orig_sum :sum
151
- end
152
- }
232
+ # Using Refinements here in order not to expose our internal method
233
+ using Module.new {
234
+ refine Array do
235
+ alias :orig_sum :sum
236
+ end
237
+ }
153
238
 
154
- class Array
155
- def sum(init = nil, &block) #:nodoc:
156
- if init.is_a?(Numeric) || first.is_a?(Numeric)
157
- init ||= 0
158
- orig_sum(init, &block)
159
- else
160
- super
161
- end
239
+ class Array #:nodoc:
240
+ # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
241
+ def sum(init = nil, &block)
242
+ if init.is_a?(Numeric) || first.is_a?(Numeric)
243
+ init ||= 0
244
+ orig_sum(init, &block)
245
+ else
246
+ super
162
247
  end
163
248
  end
249
+
250
+ # Removes all blank elements from the +Array+ in place and returns self.
251
+ # Uses Object#blank? for determining if an item is blank.
252
+ #
253
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
254
+ # a.compact_blank!
255
+ # # => [1, 2, true]
256
+ def compact_blank!
257
+ # use delete_if rather than reject! because it always returns self even if nothing changed
258
+ delete_if(&:blank?)
259
+ end
164
260
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/xml_mini"
4
- require "active_support/time"
5
4
  require "active_support/core_ext/object/blank"
6
5
  require "active_support/core_ext/object/to_param"
7
6
  require "active_support/core_ext/object/to_query"
7
+ require "active_support/core_ext/object/try"
8
8
  require "active_support/core_ext/array/wrap"
9
9
  require "active_support/core_ext/hash/reverse_merge"
10
10
  require "active_support/core_ext/string/inflections"
@@ -73,7 +73,7 @@ class Hash
73
73
  # configure your own builder with the <tt>:builder</tt> option. The method also accepts
74
74
  # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
75
75
  def to_xml(options = {})
76
- require "active_support/builder" unless defined?(Builder)
76
+ require "active_support/builder" unless defined?(Builder::XmlMarkup)
77
77
 
78
78
  options = options.dup
79
79
  options[:indent] ||= 2
@@ -208,7 +208,7 @@ module ActiveSupport
208
208
  elsif become_empty_string?(value)
209
209
  ""
210
210
  elsif become_hash?(value)
211
- xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
211
+ xml_value = value.transform_values { |v| deep_to_h(v) }
212
212
 
213
213
  # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
214
214
  # how multipart uploaded files from HTML appear
@@ -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
@@ -10,8 +10,8 @@ class Hash
10
10
  # This is useful for limiting a set of parameters to everything but a few known toggles:
11
11
  # @person.update(params[:person].except(:admin))
12
12
  def except(*keys)
13
- dup.except!(*keys)
14
- end
13
+ slice(*self.keys - keys)
14
+ end unless method_defined?(:except)
15
15
 
16
16
  # Removes the given keys from hash and returns it.
17
17
  # hash = { a: true, b: false, c: nil }
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Returns a new hash with all keys converted using the +block+ operation.
5
- #
6
- # hash = { name: 'Rob', age: '28' }
7
- #
8
- # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
9
- #
10
- # If you do not provide a +block+, it will return an Enumerator
11
- # for chaining with other methods:
12
- #
13
- # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
14
- def transform_keys
15
- return enum_for(:transform_keys) { size } unless block_given?
16
- result = {}
17
- each_key do |key|
18
- result[yield(key)] = self[key]
19
- end
20
- result
21
- end unless method_defined? :transform_keys
22
-
23
- # Destructively converts all keys using the +block+ operations.
24
- # Same as +transform_keys+ but modifies +self+.
25
- def transform_keys!
26
- return enum_for(:transform_keys!) { size } unless block_given?
27
- keys.each do |key|
28
- self[yield(key)] = delete(key)
29
- end
30
- self
31
- end unless method_defined? :transform_keys!
32
-
33
4
  # Returns a new hash with all keys converted to strings.
34
5
  #
35
6
  # hash = { name: 'Rob', age: '28' }
@@ -141,7 +112,7 @@ class Hash
141
112
  end
142
113
 
143
114
  private
144
- # support methods for deep transforming nested hashes and arrays
115
+ # Support methods for deep transforming nested hashes and arrays.
145
116
  def _deep_transform_keys_in_object(object, &block)
146
117
  case object
147
118
  when Hash
@@ -1,34 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Slices a hash to include only the given keys. Returns a hash containing
5
- # the given keys.
6
- #
7
- # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
8
- # # => {:a=>1, :b=>2}
9
- #
10
- # This is useful for limiting an options hash to valid keys before
11
- # passing to a method:
12
- #
13
- # def search(criteria = {})
14
- # criteria.assert_valid_keys(:mass, :velocity, :time)
15
- # end
16
- #
17
- # search(options.slice(:mass, :velocity, :time))
18
- #
19
- # If you have an array of keys you want to limit to, you should splat them:
20
- #
21
- # valid_keys = [:mass, :velocity, :time]
22
- # search(options.slice(*valid_keys))
23
- def slice(*keys)
24
- keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
25
- end unless method_defined?(:slice)
26
-
27
4
  # Replaces the hash with only the given keys.
28
5
  # Returns a hash containing the removed key/value pairs.
29
6
  #
30
- # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
31
- # # => {:c=>3, :d=>4}
7
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
8
+ # hash.slice!(:a, :b) # => {:c=>3, :d=>4}
9
+ # hash # => {:a=>1, :b=>2}
32
10
  def slice!(*keys)
33
11
  omit = slice(*self.keys - keys)
34
12
  hash = slice(*keys)
@@ -40,8 +18,9 @@ class Hash
40
18
 
41
19
  # Removes and returns the key/value pairs matching the given keys.
42
20
  #
43
- # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
44
- # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
21
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
22
+ # hash.extract!(:a, :b) # => {:a=>1, :b=>2}
23
+ # hash # => {:c=>3, :d=>4}
45
24
  def extract!(*keys)
46
25
  keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
47
26
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/compact"
4
3
  require "active_support/core_ext/hash/conversions"
5
4
  require "active_support/core_ext/hash/deep_merge"
5
+ require "active_support/core_ext/hash/deep_transform_values"
6
6
  require "active_support/core_ext/hash/except"
7
7
  require "active_support/core_ext/hash/indifferent_access"
8
8
  require "active_support/core_ext/hash/keys"
9
9
  require "active_support/core_ext/hash/reverse_merge"
10
10
  require "active_support/core_ext/hash/slice"
11
- require "active_support/core_ext/hash/transform_values"
@@ -7,6 +7,6 @@ class Integer
7
7
  # 6.multiple_of?(5) # => false
8
8
  # 10.multiple_of?(2) # => true
9
9
  def multiple_of?(number)
10
- number != 0 ? self % number == 0 : zero?
10
+ number == 0 ? self == 0 : self % number == 0
11
11
  end
12
12
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/kernel/agnostics"
4
3
  require "active_support/core_ext/kernel/concern"
5
4
  require "active_support/core_ext/kernel/reporting"
6
5
  require "active_support/core_ext/kernel/singleton_class"
@@ -4,6 +4,6 @@ class LoadError
4
4
  # Returns true if the given path name (except perhaps for the ".rb"
5
5
  # extension) is the missing file which caused the exception to be raised.
6
6
  def is_missing?(location)
7
- location.sub(/\.rb$/, "".freeze) == path.sub(/\.rb$/, "".freeze)
7
+ location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
8
8
  end
9
9
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/inflections"
4
+
3
5
  module ActiveSupport
4
6
  module MarshalWithAutoloading # :nodoc:
5
7
  def load(source, proc = nil)
@@ -28,9 +28,9 @@ class Module
28
28
  end
29
29
 
30
30
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, "")
31
+ internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
32
32
  # use native attr_* methods as they are faster on some Ruby implementations
33
- send("attr_#{type}", internal_name)
33
+ public_send("attr_#{type}", internal_name)
34
34
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
35
35
  alias_method attr_name, internal_name
36
36
  remove_method internal_name