activesupport 7.0.0 → 7.2.2.1

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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +156 -255
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +3 -1
  7. data/lib/active_support/backtrace_cleaner.rb +41 -9
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +49 -17
  14. data/lib/active_support/cache/mem_cache_store.rb +111 -129
  15. data/lib/active_support/cache/memory_store.rb +81 -26
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +175 -154
  18. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +31 -13
  20. data/lib/active_support/cache.rb +457 -377
  21. data/lib/active_support/callbacks.rb +123 -139
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +12 -2
  27. data/lib/active_support/core_ext/array/conversions.rb +7 -9
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +0 -1
  30. data/lib/active_support/core_ext/class/subclasses.rb +4 -15
  31. data/lib/active_support/core_ext/date/blank.rb +4 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +20 -5
  33. data/lib/active_support/core_ext/date/conversions.rb +15 -16
  34. data/lib/active_support/core_ext/date.rb +0 -1
  35. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  36. data/lib/active_support/core_ext/date_and_time/compatibility.rb +29 -2
  37. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  38. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +15 -15
  40. data/lib/active_support/core_ext/date_time.rb +0 -1
  41. data/lib/active_support/core_ext/digest/uuid.rb +7 -10
  42. data/lib/active_support/core_ext/enumerable.rb +51 -101
  43. data/lib/active_support/core_ext/erb/util.rb +201 -0
  44. data/lib/active_support/core_ext/file/atomic.rb +2 -0
  45. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  46. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +7 -7
  50. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  51. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  52. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  53. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  54. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +38 -20
  55. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  56. data/lib/active_support/core_ext/module/delegation.rb +20 -119
  57. data/lib/active_support/core_ext/module/deprecation.rb +12 -12
  58. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  59. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  60. data/lib/active_support/core_ext/numeric/conversions.rb +77 -75
  61. data/lib/active_support/core_ext/numeric.rb +0 -1
  62. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  63. data/lib/active_support/core_ext/object/blank.rb +45 -1
  64. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  65. data/lib/active_support/core_ext/object/duplicable.rb +25 -16
  66. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  67. data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
  68. data/lib/active_support/core_ext/object/json.rb +17 -7
  69. data/lib/active_support/core_ext/object/to_query.rb +0 -2
  70. data/lib/active_support/core_ext/object/with.rb +46 -0
  71. data/lib/active_support/core_ext/object/with_options.rb +9 -9
  72. data/lib/active_support/core_ext/object.rb +1 -0
  73. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  74. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  75. data/lib/active_support/core_ext/pathname.rb +1 -0
  76. data/lib/active_support/core_ext/range/conversions.rb +32 -11
  77. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  78. data/lib/active_support/core_ext/range.rb +1 -2
  79. data/lib/active_support/core_ext/securerandom.rb +2 -6
  80. data/lib/active_support/core_ext/string/conversions.rb +3 -3
  81. data/lib/active_support/core_ext/string/filters.rb +21 -15
  82. data/lib/active_support/core_ext/string/indent.rb +1 -1
  83. data/lib/active_support/core_ext/string/inflections.rb +16 -9
  84. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  85. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  86. data/lib/active_support/core_ext/string/output_safety.rb +39 -150
  87. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  88. data/lib/active_support/core_ext/time/calculations.rb +42 -32
  89. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  90. data/lib/active_support/core_ext/time/conversions.rb +13 -15
  91. data/lib/active_support/core_ext/time/zones.rb +8 -9
  92. data/lib/active_support/core_ext/time.rb +0 -1
  93. data/lib/active_support/core_ext.rb +0 -1
  94. data/lib/active_support/current_attributes.rb +53 -46
  95. data/lib/active_support/deep_mergeable.rb +53 -0
  96. data/lib/active_support/delegation.rb +202 -0
  97. data/lib/active_support/dependencies/autoload.rb +9 -16
  98. data/lib/active_support/deprecation/behaviors.rb +65 -42
  99. data/lib/active_support/deprecation/constant_accessor.rb +47 -25
  100. data/lib/active_support/deprecation/deprecators.rb +104 -0
  101. data/lib/active_support/deprecation/disallowed.rb +6 -8
  102. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  103. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
  104. data/lib/active_support/deprecation/reporting.rb +49 -27
  105. data/lib/active_support/deprecation.rb +39 -9
  106. data/lib/active_support/deprecator.rb +7 -0
  107. data/lib/active_support/descendants_tracker.rb +66 -175
  108. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  109. data/lib/active_support/duration/iso8601_serializer.rb +1 -4
  110. data/lib/active_support/duration.rb +13 -7
  111. data/lib/active_support/encrypted_configuration.rb +63 -10
  112. data/lib/active_support/encrypted_file.rb +29 -13
  113. data/lib/active_support/environment_inquirer.rb +22 -2
  114. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  115. data/lib/active_support/error_reporter.rb +160 -36
  116. data/lib/active_support/evented_file_update_checker.rb +19 -7
  117. data/lib/active_support/execution_wrapper.rb +23 -28
  118. data/lib/active_support/file_update_checker.rb +5 -3
  119. data/lib/active_support/fork_tracker.rb +4 -32
  120. data/lib/active_support/gem_version.rb +4 -4
  121. data/lib/active_support/gzip.rb +2 -0
  122. data/lib/active_support/hash_with_indifferent_access.rb +41 -25
  123. data/lib/active_support/html_safe_translation.rb +19 -6
  124. data/lib/active_support/i18n.rb +1 -1
  125. data/lib/active_support/i18n_railtie.rb +20 -13
  126. data/lib/active_support/inflector/inflections.rb +2 -0
  127. data/lib/active_support/inflector/methods.rb +28 -18
  128. data/lib/active_support/inflector/transliterate.rb +4 -2
  129. data/lib/active_support/isolated_execution_state.rb +39 -19
  130. data/lib/active_support/json/decoding.rb +2 -1
  131. data/lib/active_support/json/encoding.rb +25 -43
  132. data/lib/active_support/key_generator.rb +13 -5
  133. data/lib/active_support/lazy_load_hooks.rb +33 -7
  134. data/lib/active_support/locale/en.yml +2 -0
  135. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  136. data/lib/active_support/log_subscriber.rb +76 -36
  137. data/lib/active_support/logger.rb +22 -60
  138. data/lib/active_support/logger_thread_safe_level.rb +10 -32
  139. data/lib/active_support/message_encryptor.rb +200 -55
  140. data/lib/active_support/message_encryptors.rb +141 -0
  141. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  142. data/lib/active_support/message_pack/extensions.rb +305 -0
  143. data/lib/active_support/message_pack/serializer.rb +63 -0
  144. data/lib/active_support/message_pack.rb +50 -0
  145. data/lib/active_support/message_verifier.rb +220 -89
  146. data/lib/active_support/message_verifiers.rb +135 -0
  147. data/lib/active_support/messages/codec.rb +65 -0
  148. data/lib/active_support/messages/metadata.rb +111 -45
  149. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  150. data/lib/active_support/messages/rotator.rb +34 -32
  151. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  152. data/lib/active_support/multibyte/chars.rb +4 -2
  153. data/lib/active_support/multibyte/unicode.rb +9 -37
  154. data/lib/active_support/notifications/fanout.rb +248 -87
  155. data/lib/active_support/notifications/instrumenter.rb +93 -25
  156. data/lib/active_support/notifications.rb +38 -31
  157. data/lib/active_support/number_helper/number_converter.rb +16 -7
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  159. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  160. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  161. data/lib/active_support/number_helper.rb +379 -317
  162. data/lib/active_support/option_merger.rb +4 -4
  163. data/lib/active_support/ordered_hash.rb +3 -3
  164. data/lib/active_support/ordered_options.rb +68 -16
  165. data/lib/active_support/parameter_filter.rb +103 -84
  166. data/lib/active_support/proxy_object.rb +8 -3
  167. data/lib/active_support/railtie.rb +30 -25
  168. data/lib/active_support/reloader.rb +13 -5
  169. data/lib/active_support/rescuable.rb +12 -10
  170. data/lib/active_support/secure_compare_rotator.rb +17 -10
  171. data/lib/active_support/string_inquirer.rb +4 -2
  172. data/lib/active_support/subscriber.rb +10 -27
  173. data/lib/active_support/syntax_error_proxy.rb +60 -0
  174. data/lib/active_support/tagged_logging.rb +64 -25
  175. data/lib/active_support/test_case.rb +160 -7
  176. data/lib/active_support/testing/assertions.rb +29 -13
  177. data/lib/active_support/testing/autorun.rb +0 -2
  178. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  179. data/lib/active_support/testing/deprecation.rb +20 -27
  180. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  181. data/lib/active_support/testing/isolation.rb +46 -33
  182. data/lib/active_support/testing/method_call_assertions.rb +7 -8
  183. data/lib/active_support/testing/parallelization/server.rb +3 -0
  184. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  185. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  186. data/lib/active_support/testing/stream.rb +1 -1
  187. data/lib/active_support/testing/strict_warnings.rb +43 -0
  188. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  189. data/lib/active_support/testing/time_helpers.rb +38 -16
  190. data/lib/active_support/time_with_zone.rb +28 -54
  191. data/lib/active_support/values/time_zone.rb +26 -15
  192. data/lib/active_support/version.rb +1 -1
  193. data/lib/active_support/xml_mini/jdom.rb +3 -10
  194. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  195. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  196. data/lib/active_support/xml_mini/rexml.rb +1 -1
  197. data/lib/active_support/xml_mini.rb +13 -4
  198. data/lib/active_support.rb +15 -3
  199. metadata +142 -21
  200. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  201. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
  202. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
  203. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  204. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
  205. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
  206. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  207. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
  208. data/lib/active_support/core_ext/uri.rb +0 -5
  209. data/lib/active_support/deprecation/instance_delegator.rb +0 -38
  210. data/lib/active_support/per_thread_registry.rb +0 -65
  211. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,80 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Enumerable
4
- INDEX_WITH_DEFAULT = Object.new
5
- private_constant :INDEX_WITH_DEFAULT
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
6
17
 
18
+ module Enumerable
7
19
  # Error generated by +sole+ when called on an enumerable that doesn't have
8
20
  # exactly one item.
9
21
  class SoleItemExpectedError < StandardError; end
10
22
 
11
- # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
12
- # when we omit an identity.
13
-
14
- # :stopdoc:
15
-
16
- # We can't use Refinements here because Refinements with Module which will be prepended
17
- # doesn't work well https://bugs.ruby-lang.org/issues/13446
18
- alias :_original_sum_with_required_identity :sum
19
- private :_original_sum_with_required_identity
20
-
21
- # :startdoc:
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)
22
27
 
23
28
  # Calculates the minimum from the extracted elements.
24
29
  #
25
- # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
26
- # payments.minimum(:price) # => 5
30
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
31
+ # payments.minimum(:price) # => 5
27
32
  def minimum(key)
28
33
  map(&key).min
29
34
  end
30
35
 
31
36
  # Calculates the maximum from the extracted elements.
32
37
  #
33
- # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
34
- # payments.maximum(:price) # => 15
38
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
39
+ # payments.maximum(:price) # => 15
35
40
  def maximum(key)
36
41
  map(&key).max
37
42
  end
38
43
 
39
- # Calculates a sum from the elements.
40
- #
41
- # payments.sum { |p| p.price * p.tax_rate }
42
- # payments.sum(&:price)
43
- #
44
- # The latter is a shortcut for:
45
- #
46
- # payments.inject(0) { |sum, p| sum + p.price }
47
- #
48
- # It can also calculate the sum without the use of a block.
49
- #
50
- # [5, 15, 10].sum # => 30
51
- # ['foo', 'bar'].sum('') # => "foobar"
52
- # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
53
- #
54
- # The default sum of an empty list is zero. You can override this default:
55
- #
56
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
57
- def sum(identity = nil, &block)
58
- if identity
59
- _original_sum_with_required_identity(identity, &block)
60
- elsif block_given?
61
- map(&block).sum
62
- # we check `first(1) == []` to check if we have an
63
- # empty Enumerable; checking `empty?` would return
64
- # true for `[nil]`, which we want to deprecate to
65
- # keep consistent with Ruby
66
- elsif first.is_a?(Numeric) || first(1) == []
67
- identity ||= 0
68
- _original_sum_with_required_identity(identity, &block)
69
- else
70
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
71
- Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
72
- Sum of non-numeric elements requires an initial argument.
73
- MSG
74
- inject(:+) || 0
75
- end
76
- end
77
-
78
44
  # Convert an enumerable to a hash, using the block result as the key and the
79
45
  # element as the value.
80
46
  #
@@ -106,17 +72,17 @@ module Enumerable
106
72
  #
107
73
  # %i( created_at updated_at ).index_with(Time.now)
108
74
  # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
109
- def index_with(default = INDEX_WITH_DEFAULT)
75
+ def index_with(default = (no_default = true))
110
76
  if block_given?
111
77
  result = {}
112
78
  each { |elem| result[elem] = yield(elem) }
113
79
  result
114
- elsif default != INDEX_WITH_DEFAULT
80
+ elsif no_default
81
+ to_enum(:index_with) { size if respond_to?(:size) }
82
+ else
115
83
  result = {}
116
84
  each { |elem| result[elem] = default }
117
85
  result
118
- else
119
- to_enum(:index_with) { size if respond_to?(:size) }
120
86
  end
121
87
  end
122
88
 
@@ -127,8 +93,8 @@ module Enumerable
127
93
  def many?
128
94
  cnt = 0
129
95
  if block_given?
130
- any? do |element|
131
- cnt += 1 if yield element
96
+ any? do |*args|
97
+ cnt += 1 if yield(*args)
132
98
  cnt > 1
133
99
  end
134
100
  else
@@ -205,16 +171,16 @@ module Enumerable
205
171
  # Returns a new +Array+ without the blank items.
206
172
  # Uses Object#blank? for determining if an item is blank.
207
173
  #
208
- # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
209
- # # => [1, 2, true]
174
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
175
+ # # => [1, 2, true]
210
176
  #
211
- # Set.new([nil, "", 1, 2])
212
- # # => [2, 1] (or [1, 2])
177
+ # Set.new([nil, "", 1, false]).compact_blank
178
+ # # => [1]
213
179
  #
214
180
  # When called on a +Hash+, returns a new +Hash+ without the blank values.
215
181
  #
216
- # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
217
- # #=> { b: 1, f: true }
182
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
183
+ # # => { b: 1, f: true }
218
184
  def compact_blank
219
185
  reject(&:blank?)
220
186
  end
@@ -223,25 +189,25 @@ module Enumerable
223
189
  # objects in the original enumerable.
224
190
  #
225
191
  # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
226
- # => [ Person.find(1), Person.find(5), Person.find(3) ]
192
+ # # => [ Person.find(1), Person.find(5), Person.find(3) ]
227
193
  #
228
194
  # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
229
195
  # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
230
196
  def in_order_of(key, series)
231
- index_by(&key).values_at(*series).compact
197
+ group_by(&key).values_at(*series).flatten(1).compact
232
198
  end
233
199
 
234
200
  # Returns the sole item in the enumerable. If there are no items, or more
235
201
  # than one item, raises +Enumerable::SoleItemExpectedError+.
236
202
  #
237
- # ["x"].sole # => "x"
238
- # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
239
- # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
203
+ # ["x"].sole # => "x"
204
+ # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
205
+ # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
240
206
  def sole
241
207
  case count
242
208
  when 1 then return first # rubocop:disable Style/RedundantReturn
243
- when 0 then raise SoleItemExpectedError, "no item found"
244
- when 2.. then raise SoleItemExpectedError, "multiple items found"
209
+ when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
210
+ when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
245
211
  end
246
212
  end
247
213
  end
@@ -255,9 +221,9 @@ class Hash
255
221
  # Removes all blank values from the +Hash+ in place and returns self.
256
222
  # Uses Object#blank? for determining if a value is blank.
257
223
  #
258
- # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
259
- # h.compact_blank!
260
- # # => { b: 1, f: true }
224
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
225
+ # h.compact_blank!
226
+ # # => { b: 1, f: true }
261
227
  def compact_blank!
262
228
  # use delete_if rather than reject! because it always returns self even if nothing changed
263
229
  delete_if { |_k, v| v.blank? }
@@ -267,44 +233,28 @@ end
267
233
  class Range # :nodoc:
268
234
  # Optimize range sum to use arithmetic progression if a block is not given and
269
235
  # we have a range of numeric values.
270
- def sum(identity = nil)
236
+ def sum(initial_value = 0)
271
237
  if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
272
238
  super
273
239
  else
274
240
  actual_last = exclude_end? ? (last - 1) : last
275
241
  if actual_last >= first
276
- sum = identity || 0
242
+ sum = initial_value || 0
277
243
  sum + (actual_last - first + 1) * (actual_last + first) / 2
278
244
  else
279
- identity || 0
245
+ initial_value || 0
280
246
  end
281
247
  end
282
248
  end
283
249
  end
284
250
 
285
- # Using Refinements here in order not to expose our internal method
286
- using Module.new {
287
- refine Array do
288
- alias :orig_sum :sum
289
- end
290
- }
291
-
292
251
  class Array # :nodoc:
293
- def sum(init = nil, &block)
294
- if init.is_a?(Numeric) || first.is_a?(Numeric)
295
- init ||= 0
296
- orig_sum(init, &block)
297
- else
298
- super
299
- end
300
- end
301
-
302
252
  # Removes all blank elements from the +Array+ in place and returns self.
303
253
  # Uses Object#blank? for determining if an item is blank.
304
254
  #
305
- # a = [1, "", nil, 2, " ", [], {}, false, true]
306
- # a.compact_blank!
307
- # # => [1, 2, true]
255
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
256
+ # a.compact_blank!
257
+ # # => [1, 2, true]
308
258
  def compact_blank!
309
259
  # use delete_if rather than reject! because it always returns self even if nothing changed
310
260
  delete_if(&:blank?)
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ActiveSupport
6
+ module CoreExt
7
+ module ERBUtil
8
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
9
+ # This method is not for public consumption! Seriously!
10
+ def html_escape(s) # :nodoc:
11
+ s = s.to_s
12
+ if s.html_safe?
13
+ s
14
+ else
15
+ super(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
16
+ end
17
+ end
18
+ alias :unwrapped_html_escape :html_escape # :nodoc:
19
+
20
+ # A utility method for escaping HTML tag characters.
21
+ # This method is also aliased as <tt>h</tt>.
22
+ #
23
+ # puts html_escape('is a > 0 & a < 10?')
24
+ # # => is a &gt; 0 &amp; a &lt; 10?
25
+ def html_escape(s) # rubocop:disable Lint/DuplicateMethods
26
+ unwrapped_html_escape(s).html_safe
27
+ end
28
+ alias h html_escape
29
+ end
30
+
31
+ module ERBUtilPrivate
32
+ include ERBUtil
33
+ private :unwrapped_html_escape, :html_escape, :h
34
+ end
35
+ end
36
+ end
37
+
38
+ class ERB
39
+ module Util
40
+ HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
41
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
42
+
43
+ # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
44
+ TAG_NAME_START_CODEPOINTS = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
45
+ "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
46
+ "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
47
+ INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
48
+ TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
49
+ INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
50
+ SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
51
+ TAG_NAME_REPLACEMENT_CHAR = "_"
52
+
53
+ prepend ActiveSupport::CoreExt::ERBUtilPrivate
54
+ singleton_class.prepend ActiveSupport::CoreExt::ERBUtil
55
+
56
+ # A utility method for escaping HTML without affecting existing escaped entities.
57
+ #
58
+ # html_escape_once('1 < 2 &amp; 3')
59
+ # # => "1 &lt; 2 &amp; 3"
60
+ #
61
+ # html_escape_once('&lt;&lt; Accept & Checkout')
62
+ # # => "&lt;&lt; Accept &amp; Checkout"
63
+ def html_escape_once(s)
64
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
65
+ end
66
+
67
+ module_function :html_escape_once
68
+
69
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
70
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
71
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
72
+ # escaped as they are treated as newline characters in some JavaScript engines.
73
+ # These sequences have identical meaning as the original characters inside the
74
+ # context of a JSON string, so assuming the input is a valid and well-formed
75
+ # JSON value, the output will have equivalent meaning when parsed:
76
+ #
77
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
78
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
79
+ #
80
+ # json_escape(json)
81
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
82
+ #
83
+ # JSON.parse(json) == JSON.parse(json_escape(json))
84
+ # # => true
85
+ #
86
+ # The intended use case for this method is to escape JSON strings before including
87
+ # them inside a script tag to avoid XSS vulnerability:
88
+ #
89
+ # <script>
90
+ # var currentUser = <%= raw json_escape(current_user.to_json) %>;
91
+ # </script>
92
+ #
93
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
94
+ # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
95
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
96
+ # use inside HTML attributes.
97
+ #
98
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
99
+ # whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
100
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
101
+ # content returned by your JSON.
102
+ #
103
+ # If you need to output JSON elsewhere in your HTML, you can just do something
104
+ # like this, as any unsafe characters (including quotation marks) will be
105
+ # automatically escaped for you:
106
+ #
107
+ # <div data-user-info="<%= current_user.to_json %>">...</div>
108
+ #
109
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
110
+ # will open up serious XSS vulnerabilities. For example, if you replace the
111
+ # +current_user.to_json+ in the example above with user input instead, the browser
112
+ # will happily <tt>eval()</tt> that string as JavaScript.
113
+ #
114
+ # The escaping performed in this method is identical to those performed in the
115
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
116
+ # set to true. Because this transformation is idempotent, this helper can be
117
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
118
+ #
119
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
120
+ # is enabled, or if you are unsure where your JSON string originated from, it
121
+ # is recommended that you always apply this helper (other libraries, such as the
122
+ # JSON gem, do not provide this kind of protection by default; also some gems
123
+ # might override +to_json+ to bypass Active Support's encoder).
124
+ def json_escape(s)
125
+ result = s.to_s.dup
126
+ result.gsub!(">", '\u003e')
127
+ result.gsub!("<", '\u003c')
128
+ result.gsub!("&", '\u0026')
129
+ result.gsub!("\u2028", '\u2028')
130
+ result.gsub!("\u2029", '\u2029')
131
+ s.html_safe? ? result.html_safe : result
132
+ end
133
+
134
+ module_function :json_escape
135
+
136
+ # A utility method for escaping XML names of tags and names of attributes.
137
+ #
138
+ # xml_name_escape('1 < 2 & 3')
139
+ # # => "1___2___3"
140
+ #
141
+ # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
142
+ def xml_name_escape(name)
143
+ name = name.to_s
144
+ return "" if name.blank?
145
+ return name if name.match?(SAFE_XML_TAG_NAME_REGEXP)
146
+
147
+ starting_char = name[0]
148
+ starting_char.gsub!(INVALID_TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
149
+
150
+ return starting_char if name.size == 1
151
+
152
+ following_chars = name[1..-1]
153
+ following_chars.gsub!(INVALID_TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
154
+
155
+ starting_char << following_chars
156
+ end
157
+ module_function :xml_name_escape
158
+
159
+ # Tokenizes a line of ERB. This is really just for error reporting and
160
+ # nobody should use it.
161
+ def self.tokenize(source) # :nodoc:
162
+ require "strscan"
163
+ source = StringScanner.new(source.chomp)
164
+ tokens = []
165
+
166
+ start_re = /<%(?:={1,2}|-|\#|%)?/m
167
+ finish_re = /(?:[-=])?%>/m
168
+
169
+ while !source.eos?
170
+ pos = source.pos
171
+ source.scan_until(/(?:#{start_re}|#{finish_re})/)
172
+ raise NotImplementedError if source.matched.nil?
173
+ len = source.pos - source.matched.bytesize - pos
174
+
175
+ case source.matched
176
+ when start_re
177
+ tokens << [:TEXT, source.string[pos, len]] if len > 0
178
+ tokens << [:OPEN, source.matched]
179
+ if source.scan(/(.*?)(?=#{finish_re}|\z)/m)
180
+ tokens << [:CODE, source.matched] unless source.matched.empty?
181
+ tokens << [:CLOSE, source.scan(finish_re)] unless source.eos?
182
+ else
183
+ raise NotImplementedError
184
+ end
185
+ when finish_re
186
+ tokens << [:CODE, source.string[pos, len]] if len > 0
187
+ tokens << [:CLOSE, source.matched]
188
+ else
189
+ raise NotImplementedError, source.matched
190
+ end
191
+
192
+ unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
193
+ tokens << [:TEXT, source.rest]
194
+ source.terminate
195
+ end
196
+ end
197
+
198
+ tokens
199
+ end
200
+ end
201
+ end
@@ -64,6 +64,8 @@ class File
64
64
  file_name = join(dir, basename)
65
65
  FileUtils.touch(file_name)
66
66
  stat(file_name)
67
+ rescue Errno::ENOENT
68
+ file_name = nil
67
69
  ensure
68
70
  FileUtils.rm_f(file_name) if file_name
69
71
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/xml_mini"
4
3
  require "active_support/core_ext/object/blank"
5
4
  require "active_support/core_ext/object/to_param"
6
5
  require "active_support/core_ext/object/to_query"
@@ -69,7 +68,7 @@ class Hash
69
68
  #
70
69
  # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
71
70
  #
72
- # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
71
+ # The default XML builder is a fresh instance of +Builder::XmlMarkup+. You can
73
72
  # configure your own builder with the <tt>:builder</tt> option. The method also accepts
74
73
  # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
75
74
  def to_xml(options = {})
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deep_mergeable"
4
+
3
5
  class Hash
6
+ include ActiveSupport::DeepMergeable
7
+
8
+ ##
9
+ # :method: deep_merge
10
+ # :call-seq: deep_merge(other_hash, &block)
11
+ #
4
12
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
13
  #
6
14
  # h1 = { a: true, b: { c: [1, 2, 3] } }
@@ -15,20 +23,20 @@ class Hash
15
23
  # h2 = { b: 250, c: { c1: 200 } }
16
24
  # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
17
25
  # # => { a: 100, b: 450, c: { c1: 300 } }
18
- def deep_merge(other_hash, &block)
19
- dup.deep_merge!(other_hash, &block)
20
- end
26
+ #
27
+ #--
28
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge.
29
+
30
+ ##
31
+ # :method: deep_merge!
32
+ # :call-seq: deep_merge!(other_hash, &block)
33
+ #
34
+ # Same as #deep_merge, but modifies +self+.
35
+ #
36
+ #--
37
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge!.
21
38
 
22
- # Same as +deep_merge+, but modifies +self+.
23
- def deep_merge!(other_hash, &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)
29
- else
30
- other_val
31
- end
32
- end
39
+ def deep_merge?(other) # :nodoc:
40
+ other.is_a?(Hash)
33
41
  end
34
42
  end
@@ -5,10 +5,10 @@ class Hash
5
5
  # This includes the values from the root hash and from all
6
6
  # nested hashes and arrays.
7
7
  #
8
- # hash = { person: { name: 'Rob', age: '28' } }
8
+ # hash = { person: { name: 'Rob', age: '28' } }
9
9
  #
10
- # hash.deep_transform_values{ |value| value.to_s.upcase }
11
- # # => {person: {name: "ROB", age: "28"}}
10
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
11
+ # # => {person: {name: "ROB", age: "28"}}
12
12
  def deep_transform_values(&block)
13
13
  _deep_transform_values_in_object(self, &block)
14
14
  end
@@ -3,7 +3,7 @@
3
3
  require "active_support/hash_with_indifferent_access"
4
4
 
5
5
  class Hash
6
- # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
6
+ # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
7
7
  #
8
8
  # { a: 1 }.with_indifferent_access['a'] # => 1
9
9
  def with_indifferent_access
@@ -13,8 +13,8 @@ class Hash
13
13
  # Called when object is nested under an object that receives
14
14
  # #with_indifferent_access. This method will be called on the current object
15
15
  # by the enclosing object and is aliased to #with_indifferent_access by
16
- # default. Subclasses of Hash may overwrite this method to return +self+ if
17
- # 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
18
18
  # desirable.
19
19
  #
20
20
  # b = { b: 1 }
@@ -8,13 +8,13 @@ class Hash
8
8
  # hash.stringify_keys
9
9
  # # => {"name"=>"Rob", "age"=>"28"}
10
10
  def stringify_keys
11
- transform_keys(&:to_s)
11
+ transform_keys { |k| Symbol === k ? k.name : k.to_s }
12
12
  end
13
13
 
14
14
  # Destructively converts all keys to strings. Same as
15
15
  # +stringify_keys+, but modifies +self+.
16
16
  def stringify_keys!
17
- transform_keys!(&:to_s)
17
+ transform_keys! { |k| Symbol === k ? k.name : k.to_s }
18
18
  end
19
19
 
20
20
  # Returns a new hash with all keys converted to symbols, as long as
@@ -58,10 +58,10 @@ class Hash
58
58
  # This includes the keys from the root hash and from all
59
59
  # nested hashes and arrays.
60
60
  #
61
- # hash = { person: { name: 'Rob', age: '28' } }
61
+ # hash = { person: { name: 'Rob', age: '28' } }
62
62
  #
63
- # hash.deep_transform_keys{ |key| key.to_s.upcase }
64
- # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
63
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
64
+ # # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
65
65
  def deep_transform_keys(&block)
66
66
  _deep_transform_keys_in_object(self, &block)
67
67
  end
@@ -82,14 +82,14 @@ class Hash
82
82
  # hash.deep_stringify_keys
83
83
  # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
84
84
  def deep_stringify_keys
85
- deep_transform_keys(&:to_s)
85
+ deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
86
86
  end
87
87
 
88
88
  # Destructively converts all keys to strings.
89
89
  # This includes the keys from the root hash and from all
90
90
  # nested hashes and arrays.
91
91
  def deep_stringify_keys!
92
- deep_transform_keys!(&:to_s)
92
+ deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
93
93
  end
94
94
 
95
95
  # Returns a new hash with all keys converted to symbols, as long as
@@ -6,12 +6,12 @@ class Integer
6
6
  # Ordinalize turns a number into an ordinal string used to denote the
7
7
  # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
8
8
  #
9
- # 1.ordinalize # => "1st"
10
- # 2.ordinalize # => "2nd"
11
- # 1002.ordinalize # => "1002nd"
12
- # 1003.ordinalize # => "1003rd"
13
- # -11.ordinalize # => "-11th"
14
- # -1001.ordinalize # => "-1001st"
9
+ # 1.ordinalize # => "1st"
10
+ # 2.ordinalize # => "2nd"
11
+ # 1002.ordinalize # => "1002nd"
12
+ # 1003.ordinalize # => "1003rd"
13
+ # -11.ordinalize # => "-11th"
14
+ # -1001.ordinalize # => "-1001st"
15
15
  def ordinalize
16
16
  ActiveSupport::Inflector.ordinalize(self)
17
17
  end
@@ -19,12 +19,12 @@ class Integer
19
19
  # Ordinal returns the suffix used to denote the position
20
20
  # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
21
21
  #
22
- # 1.ordinal # => "st"
23
- # 2.ordinal # => "nd"
24
- # 1002.ordinal # => "nd"
25
- # 1003.ordinal # => "rd"
26
- # -11.ordinal # => "th"
27
- # -1001.ordinal # => "st"
22
+ # 1.ordinal # => "st"
23
+ # 2.ordinal # => "nd"
24
+ # 1002.ordinal # => "nd"
25
+ # 1003.ordinal # => "rd"
26
+ # -11.ordinal # => "th"
27
+ # -1001.ordinal # => "st"
28
28
  def ordinal
29
29
  ActiveSupport::Inflector.ordinal(self)
30
30
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kernel
4
- # class_eval on an object acts like singleton_class.class_eval.
4
+ # class_eval on an object acts like +singleton_class.class_eval+.
5
5
  def class_eval(*args, &block)
6
6
  singleton_class.class_eval(*args, &block)
7
7
  end