activesupport 7.0.4 → 7.1.5.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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1076 -230
  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 +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +30 -5
  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 +37 -10
  14. data/lib/active_support/cache/mem_cache_store.rb +100 -76
  15. data/lib/active_support/cache/memory_store.rb +78 -24
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +153 -141
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +333 -253
  21. data/lib/active_support/callbacks.rb +44 -21
  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 +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  30. data/lib/active_support/core_ext/date/calculations.rb +15 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  32. data/lib/active_support/core_ext/date.rb +0 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  34. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  35. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  36. data/lib/active_support/core_ext/date_time.rb +0 -1
  37. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  38. data/lib/active_support/core_ext/enumerable.rb +8 -75
  39. data/lib/active_support/core_ext/erb/util.rb +196 -0
  40. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  42. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  43. data/lib/active_support/core_ext/hash/keys.rb +3 -3
  44. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  45. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  47. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  48. data/lib/active_support/core_ext/module/delegation.rb +81 -37
  49. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  50. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  51. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  52. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  55. data/lib/active_support/core_ext/object/duplicable.rb +25 -16
  56. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  57. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  58. data/lib/active_support/core_ext/object/json.rb +16 -6
  59. data/lib/active_support/core_ext/object/to_query.rb +0 -2
  60. data/lib/active_support/core_ext/object/with.rb +44 -0
  61. data/lib/active_support/core_ext/object/with_options.rb +9 -9
  62. data/lib/active_support/core_ext/object.rb +1 -0
  63. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  64. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  65. data/lib/active_support/core_ext/pathname.rb +1 -0
  66. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  67. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  68. data/lib/active_support/core_ext/range.rb +1 -2
  69. data/lib/active_support/core_ext/securerandom.rb +24 -12
  70. data/lib/active_support/core_ext/string/filters.rb +20 -14
  71. data/lib/active_support/core_ext/string/indent.rb +1 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +16 -9
  73. data/lib/active_support/core_ext/string/output_safety.rb +42 -174
  74. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  75. data/lib/active_support/core_ext/time/calculations.rb +22 -2
  76. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  77. data/lib/active_support/core_ext/time/zones.rb +7 -8
  78. data/lib/active_support/core_ext/time.rb +0 -1
  79. data/lib/active_support/current_attributes.rb +15 -6
  80. data/lib/active_support/deep_mergeable.rb +53 -0
  81. data/lib/active_support/dependencies/autoload.rb +17 -12
  82. data/lib/active_support/deprecation/behaviors.rb +65 -42
  83. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  84. data/lib/active_support/deprecation/deprecators.rb +104 -0
  85. data/lib/active_support/deprecation/disallowed.rb +6 -8
  86. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  87. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  88. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  89. data/lib/active_support/deprecation/reporting.rb +43 -26
  90. data/lib/active_support/deprecation.rb +32 -5
  91. data/lib/active_support/deprecator.rb +7 -0
  92. data/lib/active_support/descendants_tracker.rb +104 -132
  93. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  94. data/lib/active_support/duration.rb +2 -1
  95. data/lib/active_support/encrypted_configuration.rb +63 -11
  96. data/lib/active_support/encrypted_file.rb +16 -12
  97. data/lib/active_support/environment_inquirer.rb +22 -2
  98. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  99. data/lib/active_support/error_reporter.rb +121 -35
  100. data/lib/active_support/evented_file_update_checker.rb +17 -2
  101. data/lib/active_support/execution_wrapper.rb +4 -4
  102. data/lib/active_support/file_update_checker.rb +4 -2
  103. data/lib/active_support/fork_tracker.rb +10 -2
  104. data/lib/active_support/gem_version.rb +4 -4
  105. data/lib/active_support/gzip.rb +2 -0
  106. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  107. data/lib/active_support/html_safe_translation.rb +16 -6
  108. data/lib/active_support/i18n.rb +1 -1
  109. data/lib/active_support/i18n_railtie.rb +20 -13
  110. data/lib/active_support/inflector/inflections.rb +2 -0
  111. data/lib/active_support/inflector/methods.rb +28 -18
  112. data/lib/active_support/inflector/transliterate.rb +3 -1
  113. data/lib/active_support/isolated_execution_state.rb +26 -22
  114. data/lib/active_support/json/decoding.rb +2 -1
  115. data/lib/active_support/json/encoding.rb +25 -43
  116. data/lib/active_support/key_generator.rb +9 -1
  117. data/lib/active_support/lazy_load_hooks.rb +7 -5
  118. data/lib/active_support/locale/en.yml +2 -0
  119. data/lib/active_support/log_subscriber.rb +85 -33
  120. data/lib/active_support/logger.rb +9 -60
  121. data/lib/active_support/logger_thread_safe_level.rb +10 -24
  122. data/lib/active_support/message_encryptor.rb +197 -53
  123. data/lib/active_support/message_encryptors.rb +141 -0
  124. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  125. data/lib/active_support/message_pack/extensions.rb +292 -0
  126. data/lib/active_support/message_pack/serializer.rb +63 -0
  127. data/lib/active_support/message_pack.rb +50 -0
  128. data/lib/active_support/message_verifier.rb +212 -93
  129. data/lib/active_support/message_verifiers.rb +135 -0
  130. data/lib/active_support/messages/codec.rb +65 -0
  131. data/lib/active_support/messages/metadata.rb +111 -45
  132. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  133. data/lib/active_support/messages/rotator.rb +34 -32
  134. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  135. data/lib/active_support/multibyte/chars.rb +2 -0
  136. data/lib/active_support/multibyte/unicode.rb +9 -37
  137. data/lib/active_support/notifications/fanout.rb +245 -81
  138. data/lib/active_support/notifications/instrumenter.rb +87 -22
  139. data/lib/active_support/notifications.rb +3 -3
  140. data/lib/active_support/number_helper/number_converter.rb +14 -5
  141. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  142. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  144. data/lib/active_support/number_helper.rb +379 -317
  145. data/lib/active_support/ordered_hash.rb +3 -3
  146. data/lib/active_support/ordered_options.rb +14 -0
  147. data/lib/active_support/parameter_filter.rb +103 -84
  148. data/lib/active_support/proxy_object.rb +2 -0
  149. data/lib/active_support/railtie.rb +33 -21
  150. data/lib/active_support/reloader.rb +12 -4
  151. data/lib/active_support/rescuable.rb +2 -0
  152. data/lib/active_support/secure_compare_rotator.rb +16 -9
  153. data/lib/active_support/string_inquirer.rb +3 -1
  154. data/lib/active_support/subscriber.rb +9 -27
  155. data/lib/active_support/syntax_error_proxy.rb +60 -0
  156. data/lib/active_support/tagged_logging.rb +64 -24
  157. data/lib/active_support/test_case.rb +153 -6
  158. data/lib/active_support/testing/assertions.rb +26 -10
  159. data/lib/active_support/testing/autorun.rb +0 -2
  160. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  161. data/lib/active_support/testing/deprecation.rb +25 -25
  162. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  163. data/lib/active_support/testing/isolation.rb +29 -28
  164. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  165. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  166. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  167. data/lib/active_support/testing/stream.rb +1 -1
  168. data/lib/active_support/testing/strict_warnings.rb +39 -0
  169. data/lib/active_support/testing/time_helpers.rb +37 -15
  170. data/lib/active_support/time_with_zone.rb +8 -37
  171. data/lib/active_support/values/time_zone.rb +18 -7
  172. data/lib/active_support/version.rb +1 -1
  173. data/lib/active_support/xml_mini/jdom.rb +3 -10
  174. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  175. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  176. data/lib/active_support/xml_mini/rexml.rb +1 -1
  177. data/lib/active_support/xml_mini.rb +2 -2
  178. data/lib/active_support.rb +14 -3
  179. metadata +148 -19
  180. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  181. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
  182. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
  183. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  184. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
  185. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
  186. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  187. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
  188. data/lib/active_support/core_ext/uri.rb +0 -5
  189. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -7,9 +7,9 @@ class Module
7
7
  # option is not used.
8
8
  class DelegationError < NoMethodError; end
9
9
 
10
- RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
11
- else elsif END end ensure false for if in module next nil not or redo rescue retry
12
- return self super then true undef unless until when while yield)
10
+ RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
11
+ case class def defined? do else elsif END end ensure false for if in module next nil
12
+ not or redo rescue retry return self super then true undef unless until when while yield)
13
13
  DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
14
14
  DELEGATION_RESERVED_METHOD_NAMES = Set.new(
15
15
  RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
@@ -187,19 +187,49 @@ class Module
187
187
  location = caller_locations(1, 1).first
188
188
  file, line = location.path, location.lineno
189
189
 
190
- to = to.to_s
191
- to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
190
+ receiver = to.to_s
191
+ receiver = "self.#{receiver}" if DELEGATION_RESERVED_METHOD_NAMES.include?(receiver)
192
192
 
193
193
  method_def = []
194
194
  method_names = []
195
195
 
196
- methods.map do |method|
196
+ method_def << "self.private" if private
197
+
198
+ methods.each do |method|
197
199
  method_name = prefix ? "#{method_prefix}#{method}" : method
198
200
  method_names << method_name.to_sym
199
201
 
200
202
  # Attribute writer methods only accept one argument. Makes sure []=
201
203
  # methods still accept two arguments.
202
- definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
204
+ definition = \
205
+ if /[^\]]=\z/.match?(method)
206
+ "arg"
207
+ else
208
+ method_object =
209
+ begin
210
+ if to.is_a?(Module)
211
+ to.method(method)
212
+ elsif receiver == "self.class"
213
+ method(method)
214
+ end
215
+ rescue NameError
216
+ # Do nothing. Fall back to `"..."`
217
+ end
218
+
219
+ if method_object
220
+ parameters = method_object.parameters
221
+
222
+ if (parameters.map(&:first) & [:opt, :rest, :keyreq, :key, :keyrest]).any?
223
+ "..."
224
+ else
225
+ defn = parameters.filter_map { |type, arg| arg if type == :req }
226
+ defn << "&block"
227
+ defn.join(", ")
228
+ end
229
+ else
230
+ "..."
231
+ end
232
+ end
203
233
 
204
234
  # The following generated method calls the target exactly once, storing
205
235
  # the returned value in a dummy variable.
@@ -213,7 +243,7 @@ class Module
213
243
 
214
244
  method_def <<
215
245
  "def #{method_name}(#{definition})" <<
216
- " _ = #{to}" <<
246
+ " _ = #{receiver}" <<
217
247
  " if !_.nil? || nil.respond_to?(:#{method})" <<
218
248
  " _.#{method}(#{definition})" <<
219
249
  " end" <<
@@ -224,11 +254,11 @@ class Module
224
254
 
225
255
  method_def <<
226
256
  "def #{method_name}(#{definition})" <<
227
- " _ = #{to}" <<
257
+ " _ = #{receiver}" <<
228
258
  " _.#{method}(#{definition})" <<
229
259
  "rescue NoMethodError => e" <<
230
260
  " if _.nil? && e.name == :#{method}" <<
231
- %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
261
+ %( raise DelegationError, "#{self}##{method_name} delegated to #{receiver}.#{method}, but #{receiver} is nil: \#{self.inspect}") <<
232
262
  " else" <<
233
263
  " raise" <<
234
264
  " end" <<
@@ -236,7 +266,6 @@ class Module
236
266
  end
237
267
  end
238
268
  module_eval(method_def.join(";"), file, line)
239
- private(*method_names) if private
240
269
  method_names
241
270
  end
242
271
 
@@ -288,37 +317,52 @@ class Module
288
317
  # of <tt>object</tt> add or remove instance variables.
289
318
  def delegate_missing_to(target, allow_nil: nil)
290
319
  target = target.to_s
291
- target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
320
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) || target == "__target"
292
321
 
293
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
294
- def respond_to_missing?(name, include_private = false)
295
- # It may look like an oversight, but we deliberately do not pass
296
- # +include_private+, because they do not get delegated.
322
+ if allow_nil
323
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
324
+ def respond_to_missing?(name, include_private = false)
325
+ # It may look like an oversight, but we deliberately do not pass
326
+ # +include_private+, because they do not get delegated.
297
327
 
298
- return false if name == :marshal_dump || name == :_dump
299
- #{target}.respond_to?(name) || super
300
- end
328
+ return false if name == :marshal_dump || name == :_dump
329
+ #{target}.respond_to?(name) || super
330
+ end
301
331
 
302
- def method_missing(method, *args, &block)
303
- if #{target}.respond_to?(method)
304
- #{target}.public_send(method, *args, &block)
305
- else
306
- begin
332
+ def method_missing(method, *args, &block)
333
+ __target = #{target}
334
+ if __target.nil? && !nil.respond_to?(method)
335
+ nil
336
+ elsif __target.respond_to?(method)
337
+ __target.public_send(method, *args, &block)
338
+ else
307
339
  super
308
- rescue NoMethodError
309
- if #{target}.nil?
310
- if #{allow_nil == true}
311
- nil
312
- else
313
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
314
- end
315
- else
316
- raise
317
- end
318
340
  end
319
341
  end
320
- end
321
- ruby2_keywords(:method_missing)
322
- RUBY
342
+ ruby2_keywords(:method_missing)
343
+ RUBY
344
+ else
345
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
346
+ def respond_to_missing?(name, include_private = false)
347
+ # It may look like an oversight, but we deliberately do not pass
348
+ # +include_private+, because they do not get delegated.
349
+
350
+ return false if name == :marshal_dump || name == :_dump
351
+ #{target}.respond_to?(name) || super
352
+ end
353
+
354
+ def method_missing(method, *args, &block)
355
+ __target = #{target}
356
+ if __target.nil? && !nil.respond_to?(method)
357
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
358
+ elsif __target.respond_to?(method)
359
+ __target.public_send(method, *args, &block)
360
+ else
361
+ super
362
+ end
363
+ end
364
+ ruby2_keywords(:method_missing)
365
+ RUBY
366
+ end
323
367
  end
324
368
  end
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Module
4
- # deprecate :foo
5
- # deprecate bar: 'message'
6
- # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
4
+ # deprecate :foo, deprecator: MyLib.deprecator
5
+ # deprecate :foo, bar: "warning!", deprecator: MyLib.deprecator
7
6
  #
8
- # You can also use custom deprecator instance:
9
- #
10
- # deprecate :foo, deprecator: MyLib::Deprecator.new
11
- # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
12
- #
13
- # \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
14
- # method where you can implement your custom warning behavior.
7
+ # A deprecator is typically an instance of ActiveSupport::Deprecation, but you can also pass any object that responds
8
+ # to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt> where you can implement your
9
+ # custom warning behavior.
15
10
  #
16
11
  # class MyLib::Deprecator
17
12
  # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
@@ -19,7 +14,15 @@ class Module
19
14
  # Kernel.warn message
20
15
  # end
21
16
  # end
22
- def deprecate(*method_names)
23
- ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
17
+ def deprecate(*method_names, deprecator: nil, **options)
18
+ if deprecator.is_a?(ActiveSupport::Deprecation)
19
+ deprecator.deprecate_methods(self, *method_names, **options)
20
+ elsif deprecator
21
+ # we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator
22
+ ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator)
23
+ else
24
+ ActiveSupport.deprecator.warn("Module.deprecate without a deprecator is deprecated")
25
+ ActiveSupport::Deprecation._instance.deprecate_methods(self, *method_names, **options)
26
+ end
24
27
  end
25
28
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/filters"
4
3
  require "active_support/inflector"
5
4
 
6
5
  class Module
@@ -7,6 +7,7 @@ class Numeric
7
7
  TERABYTE = GIGABYTE * 1024
8
8
  PETABYTE = TERABYTE * 1024
9
9
  EXABYTE = PETABYTE * 1024
10
+ ZETTABYTE = EXABYTE * 1024
10
11
 
11
12
  # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
12
13
  #
@@ -63,4 +64,12 @@ class Numeric
63
64
  self * EXABYTE
64
65
  end
65
66
  alias :exabyte :exabytes
67
+
68
+ # Returns the number of bytes equivalent to the zettabytes provided.
69
+ #
70
+ # 2.zettabytes # => 2_361_183_241_434_822_606_848
71
+ def zettabytes
72
+ self * ZETTABYTE
73
+ end
74
+ alias :zettabyte :zettabytes
66
75
  end
@@ -5,6 +5,8 @@ require "active_support/number_helper"
5
5
 
6
6
  module ActiveSupport
7
7
  module NumericWithFormat
8
+ # \Numeric With Format
9
+ #
8
10
  # Provides options for converting numbers into formatted strings.
9
11
  # Options are provided for phone numbers, currency, percentage,
10
12
  # precision, positional notation, file size, and pretty printing.
@@ -3,4 +3,3 @@
3
3
  require "active_support/core_ext/numeric/bytes"
4
4
  require "active_support/core_ext/numeric/time"
5
5
  require "active_support/core_ext/numeric/conversions"
6
- require "active_support/core_ext/numeric/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
@@ -53,3 +53,19 @@ class Hash
53
53
  hash
54
54
  end
55
55
  end
56
+
57
+ class Module
58
+ # Returns a copy of module or class if it's anonymous. If it's
59
+ # named, returns +self+.
60
+ #
61
+ # Object.deep_dup == Object # => true
62
+ # klass = Class.new
63
+ # klass.deep_dup == klass # => false
64
+ def deep_dup
65
+ if name.nil?
66
+ super
67
+ else
68
+ self
69
+ end
70
+ end
71
+ end
@@ -28,23 +28,32 @@ class Object
28
28
  end
29
29
  end
30
30
 
31
- class Method
32
- # Methods are not duplicable:
33
- #
34
- # method(:puts).duplicable? # => false
35
- # method(:puts).dup # => TypeError: allocator undefined for Method
36
- def duplicable?
37
- false
38
- end
31
+ methods_are_duplicable = begin
32
+ Object.instance_method(:duplicable?).dup
33
+ true
34
+ rescue TypeError
35
+ false
39
36
  end
40
37
 
41
- class UnboundMethod
42
- # Unbound methods are not duplicable:
43
- #
44
- # method(:puts).unbind.duplicable? # => false
45
- # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
46
- def duplicable?
47
- false
38
+ unless methods_are_duplicable
39
+ class Method
40
+ # Methods are not duplicable:
41
+ #
42
+ # method(:puts).duplicable? # => false
43
+ # method(:puts).dup # => TypeError: allocator undefined for Method
44
+ def duplicable?
45
+ false
46
+ end
47
+ end
48
+
49
+ class UnboundMethod
50
+ # Unbound methods are not duplicable:
51
+ #
52
+ # method(:puts).unbind.duplicable? # => false
53
+ # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
54
+ def duplicable?
55
+ false
56
+ end
48
57
  end
49
58
  end
50
59
 
@@ -53,7 +62,7 @@ require "singleton"
53
62
  module Singleton
54
63
  # Singleton instances are not duplicable:
55
64
  #
56
- # Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
65
+ # Class.new.include(Singleton).instance.dup # TypeError (can't dup instance of singleton
57
66
  def duplicable?
58
67
  false
59
68
  end
@@ -1,16 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Object
4
- # Returns true if this object is included in the argument. Argument must be
5
- # any object which responds to +#include?+. Usage:
4
+ # Returns true if this object is included in the argument.
5
+ #
6
+ # When argument is a +Range+, +#cover?+ is used to properly handle inclusion
7
+ # check within open ranges. Otherwise, argument must be any object which responds
8
+ # to +#include?+. Usage:
6
9
  #
7
10
  # characters = ["Konata", "Kagami", "Tsukasa"]
8
11
  # "Konata".in?(characters) # => true
9
12
  #
10
- # This will throw an +ArgumentError+ if the argument doesn't respond
11
- # to +#include?+.
13
+ # For non +Range+ arguments, this will throw an +ArgumentError+ if the argument
14
+ # doesn't respond to +#include?+.
12
15
  def in?(another_object)
13
- another_object.include?(self)
16
+ case another_object
17
+ when Range
18
+ another_object.cover?(self)
19
+ else
20
+ another_object.include?(self)
21
+ end
14
22
  rescue NoMethodError
15
23
  raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
16
24
  end
@@ -12,19 +12,29 @@ class Object
12
12
  #
13
13
  # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
14
14
  def instance_values
15
- Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
15
+ instance_variables.to_h do |ivar|
16
+ [ivar[1..-1].freeze, instance_variable_get(ivar)]
17
+ end
16
18
  end
17
19
 
18
- # Returns an array of instance variable names as strings including "@".
19
- #
20
- # class C
21
- # def initialize(x, y)
22
- # @x, @y = x, y
23
- # end
24
- # end
25
- #
26
- # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
27
- def instance_variable_names
28
- instance_variables.map(&:to_s)
20
+ if Symbol.method_defined?(:name) # RUBY_VERSION >= "3.0"
21
+ # Returns an array of instance variable names as strings including "@".
22
+ #
23
+ # class C
24
+ # def initialize(x, y)
25
+ # @x, @y = x, y
26
+ # end
27
+ # end
28
+ #
29
+ # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
30
+ def instance_variable_names
31
+ instance_variables.map(&:name)
32
+ end
33
+ else
34
+ def instance_variable_names
35
+ variables = instance_variables
36
+ variables.map! { |s| s.to_s.freeze }
37
+ variables
38
+ end
29
39
  end
30
40
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Hack to load json gem first so we can override its to_json.
3
+ # Hack to load JSON gem first so we can override its to_json.
4
4
  require "json"
5
5
  require "bigdecimal"
6
6
  require "ipaddr"
@@ -29,7 +29,7 @@ require "active_support/core_ext/date/conversions"
29
29
  # It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
30
30
  # bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
31
31
  # ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
32
- # should give exactly the same results with or without active support.
32
+ # should give exactly the same results with or without Active Support.
33
33
 
34
34
  module ActiveSupport
35
35
  module ToJsonWithActiveSupportEncoder # :nodoc:
@@ -65,9 +65,17 @@ class Object
65
65
  end
66
66
  end
67
67
 
68
+ if RUBY_VERSION >= "3.2"
69
+ class Data # :nodoc:
70
+ def as_json(options = nil)
71
+ to_h.as_json(options)
72
+ end
73
+ end
74
+ end
75
+
68
76
  class Struct # :nodoc:
69
77
  def as_json(options = nil)
70
- Hash[members.zip(values)].as_json(options)
78
+ to_h.as_json(options)
71
79
  end
72
80
  end
73
81
 
@@ -225,9 +233,11 @@ class Pathname # :nodoc:
225
233
  end
226
234
  end
227
235
 
228
- class IPAddr # :nodoc:
229
- def as_json(options = nil)
230
- to_s
236
+ unless IPAddr.method_defined?(:as_json, false)
237
+ class IPAddr # :nodoc:
238
+ def as_json(options = nil)
239
+ to_s
240
+ end
231
241
  end
232
242
  end
233
243
 
@@ -72,8 +72,6 @@ class Hash
72
72
  #
73
73
  # The string pairs "key=value" that conform the query string
74
74
  # are sorted lexicographically in ascending order.
75
- #
76
- # This method is also aliased as +to_param+.
77
75
  def to_query(namespace = nil)
78
76
  query = filter_map do |key, value|
79
77
  unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Object
4
+ # Set and restore public attributes around a block.
5
+ #
6
+ # client.timeout # => 5
7
+ # client.with(timeout: 1) do
8
+ # client.timeout # => 1
9
+ # end
10
+ # client.timeout # => 5
11
+ #
12
+ # This method is a shorthand for the common begin/ensure pattern:
13
+ #
14
+ # old_value = object.attribute
15
+ # begin
16
+ # object.attribute = new_value
17
+ # # do things
18
+ # ensure
19
+ # object.attribute = old_value
20
+ # end
21
+ #
22
+ # It can be used on any object as long as both the reader and writer methods
23
+ # are public.
24
+ def with(**attributes)
25
+ old_values = {}
26
+ begin
27
+ attributes.each do |key, value|
28
+ old_values[key] = public_send(key)
29
+ public_send("#{key}=", value)
30
+ end
31
+ yield
32
+ ensure
33
+ old_values.each do |key, old_value|
34
+ public_send("#{key}=", old_value)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # #with isn't usable on immediates, so we might as well undefine the
41
+ # method in common immediate classes to avoid potential confusion.
42
+ [NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
43
+ klass.undef_method(:with)
44
+ end
@@ -5,9 +5,9 @@ require "active_support/option_merger"
5
5
  class Object
6
6
  # An elegant way to factor duplication out of options passed to a series of
7
7
  # method calls. Each method called in the block, with the block variable as
8
- # the receiver, will have its options merged with the default +options+ hash
9
- # provided. Each method called on the block variable must take an options
10
- # hash as its final argument.
8
+ # the receiver, will have its options merged with the default +options+
9
+ # <tt>Hash</tt> or <tt>Hash</tt>-like object provided. Each method called on
10
+ # the block variable must take an options hash as its final argument.
11
11
  #
12
12
  # Without <tt>with_options</tt>, this code contains duplication:
13
13
  #
@@ -64,11 +64,11 @@ class Object
64
64
  #
65
65
  # Hence the inherited default for +if+ key is ignored.
66
66
  #
67
- # NOTE: You cannot call class methods implicitly inside of with_options.
67
+ # NOTE: You cannot call class methods implicitly inside of +with_options+.
68
68
  # You can access these methods using the class name instead:
69
69
  #
70
70
  # class Phone < ActiveRecord::Base
71
- # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
71
+ # enum :phone_number_type, { home: 0, office: 1, mobile: 2 }
72
72
  #
73
73
  # with_options presence: true do
74
74
  # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
@@ -83,11 +83,11 @@ class Object
83
83
  # end
84
84
  # end
85
85
  #
86
- # # styled.link_to "I'm red", "/"
87
- # # #=> <a href="/" style="color: red;">I'm red</a>
86
+ # styled.link_to "I'm red", "/"
87
+ # # => <a href="/" style="color: red;">I'm red</a>
88
88
  #
89
- # # styled.button_tag "I'm red too!"
90
- # # #=> <button style="color: red;">I'm red too!</button>
89
+ # styled.button_tag "I'm red too!"
90
+ # # => <button style="color: red;">I'm red too!</button>
91
91
  #
92
92
  def with_options(options, &block)
93
93
  option_merger = ActiveSupport::OptionMerger.new(self, options)
@@ -13,4 +13,5 @@ require "active_support/core_ext/object/instance_variables"
13
13
  require "active_support/core_ext/object/json"
14
14
  require "active_support/core_ext/object/to_param"
15
15
  require "active_support/core_ext/object/to_query"
16
+ require "active_support/core_ext/object/with"
16
17
  require "active_support/core_ext/object/with_options"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ class Pathname
6
+ # An Pathname is blank if it's empty:
7
+ #
8
+ # Pathname.new("").blank? # => true
9
+ # Pathname.new(" ").blank? # => false
10
+ # Pathname.new("test").blank? # => false
11
+ #
12
+ # @return [true, false]
13
+ def blank?
14
+ to_s.empty?
15
+ end
16
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pathname"
4
+
3
5
  class Pathname
4
6
  # Returns the receiver if the named file exists otherwise returns +nil+.
5
7
  # <tt>pathname.existence</tt> is equivalent to
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/pathname/blank"
3
4
  require "active_support/core_ext/pathname/existence"
@@ -1,13 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
+ # = \Range With Format
4
5
  module RangeWithFormat
5
6
  RANGE_FORMATS = {
6
7
  db: -> (start, stop) do
7
- case start
8
- when String then "BETWEEN '#{start}' AND '#{stop}'"
9
- else
10
- "BETWEEN '#{start.to_fs(:db)}' AND '#{stop.to_fs(:db)}'"
8
+ if start && stop
9
+ case start
10
+ when String then "BETWEEN '#{start}' AND '#{stop}'"
11
+ else
12
+ "BETWEEN '#{start.to_fs(:db)}' AND '#{stop.to_fs(:db)}'"
13
+ end
14
+ elsif start
15
+ case start
16
+ when String then ">= '#{start}'"
17
+ else
18
+ ">= '#{start.to_fs(:db)}'"
19
+ end
20
+ elsif stop
21
+ case stop
22
+ when String then "<= '#{stop}'"
23
+ else
24
+ "<= '#{stop.to_fs(:db)}'"
25
+ end
11
26
  end
12
27
  end
13
28
  }
@@ -19,9 +34,15 @@ module ActiveSupport
19
34
  # range = (1..100) # => 1..100
20
35
  #
21
36
  # range.to_s # => "1..100"
22
- # range.to_fs(:db) # => "BETWEEN '1' AND '100'"
37
+ # range.to_fs(:db) # => "BETWEEN '1' AND '100'"
23
38
  #
24
- # == Adding your own range formats to to_s
39
+ # range = (1..) # => 1..
40
+ # range.to_fs(:db) # => ">= '1'"
41
+ #
42
+ # range = (..100) # => ..100
43
+ # range.to_fs(:db) # => "<= '100'"
44
+ #
45
+ # == Adding your own range formats to to_fs
25
46
  # You can add your own formats to the Range::RANGE_FORMATS hash.
26
47
  # Use the format name as the hash key and a Proc instance.
27
48
  #
@@ -29,7 +50,7 @@ module ActiveSupport
29
50
  # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_fs(:db)} and #{stop.to_fs(:db)}" }
30
51
  def to_fs(format = :default)
31
52
  if formatter = RANGE_FORMATS[format]
32
- formatter.call(first, last)
53
+ formatter.call(self.begin, self.end)
33
54
  else
34
55
  to_s
35
56
  end