activesupport 6.1.0 → 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 (225) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1075 -325
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +32 -7
  8. data/lib/active_support/benchmarkable.rb +3 -2
  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 +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +201 -62
  15. data/lib/active_support/cache/memory_store.rb +86 -24
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +186 -193
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +63 -71
  20. data/lib/active_support/cache.rb +487 -249
  21. data/lib/active_support/callbacks.rb +227 -105
  22. data/lib/active_support/code_generator.rb +70 -0
  23. data/lib/active_support/concern.rb +9 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +18 -5
  28. data/lib/active_support/configuration_file.rb +7 -2
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/class/subclasses.rb +37 -26
  35. data/lib/active_support/core_ext/date/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  37. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  38. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  39. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  41. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  42. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  43. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  44. data/lib/active_support/core_ext/enumerable.rb +85 -83
  45. data/lib/active_support/core_ext/erb/util.rb +196 -0
  46. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  49. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  50. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  51. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  52. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  53. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  54. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
  57. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  58. data/lib/active_support/core_ext/module/delegation.rb +81 -43
  59. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  60. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  61. data/lib/active_support/core_ext/name_error.rb +2 -8
  62. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  63. data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +31 -11
  68. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  69. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  70. data/lib/active_support/core_ext/object/json.rb +49 -27
  71. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  72. data/lib/active_support/core_ext/object/try.rb +20 -20
  73. data/lib/active_support/core_ext/object/with.rb +44 -0
  74. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  75. data/lib/active_support/core_ext/object.rb +1 -0
  76. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  77. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  78. data/lib/active_support/core_ext/pathname.rb +4 -0
  79. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  80. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  81. data/lib/active_support/core_ext/range/each.rb +1 -1
  82. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  83. data/lib/active_support/core_ext/range.rb +1 -2
  84. data/lib/active_support/core_ext/securerandom.rb +25 -13
  85. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  86. data/lib/active_support/core_ext/string/filters.rb +21 -15
  87. data/lib/active_support/core_ext/string/indent.rb +1 -1
  88. data/lib/active_support/core_ext/string/inflections.rb +17 -10
  89. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  90. data/lib/active_support/core_ext/string/output_safety.rb +85 -165
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  92. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +30 -8
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -13
  95. data/lib/active_support/core_ext/time/zones.rb +12 -28
  96. data/lib/active_support/core_ext.rb +2 -1
  97. data/lib/active_support/current_attributes.rb +47 -20
  98. data/lib/active_support/deep_mergeable.rb +53 -0
  99. data/lib/active_support/dependencies/autoload.rb +17 -12
  100. data/lib/active_support/dependencies/interlock.rb +10 -18
  101. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  102. data/lib/active_support/dependencies.rb +58 -788
  103. data/lib/active_support/deprecation/behaviors.rb +66 -40
  104. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  105. data/lib/active_support/deprecation/deprecators.rb +104 -0
  106. data/lib/active_support/deprecation/disallowed.rb +6 -8
  107. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  108. data/lib/active_support/deprecation/method_wrappers.rb +9 -26
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
  110. data/lib/active_support/deprecation/reporting.rb +43 -26
  111. data/lib/active_support/deprecation.rb +32 -5
  112. data/lib/active_support/deprecator.rb +7 -0
  113. data/lib/active_support/descendants_tracker.rb +150 -72
  114. data/lib/active_support/digest.rb +5 -3
  115. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  116. data/lib/active_support/duration/iso8601_serializer.rb +9 -3
  117. data/lib/active_support/duration.rb +83 -52
  118. data/lib/active_support/encrypted_configuration.rb +72 -9
  119. data/lib/active_support/encrypted_file.rb +29 -13
  120. data/lib/active_support/environment_inquirer.rb +23 -3
  121. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  122. data/lib/active_support/error_reporter.rb +203 -0
  123. data/lib/active_support/evented_file_update_checker.rb +20 -7
  124. data/lib/active_support/execution_context/test_helper.rb +13 -0
  125. data/lib/active_support/execution_context.rb +53 -0
  126. data/lib/active_support/execution_wrapper.rb +44 -22
  127. data/lib/active_support/executor/test_helper.rb +7 -0
  128. data/lib/active_support/file_update_checker.rb +4 -2
  129. data/lib/active_support/fork_tracker.rb +28 -11
  130. data/lib/active_support/gem_version.rb +4 -4
  131. data/lib/active_support/gzip.rb +2 -0
  132. data/lib/active_support/hash_with_indifferent_access.rb +44 -19
  133. data/lib/active_support/html_safe_translation.rb +53 -0
  134. data/lib/active_support/i18n.rb +2 -1
  135. data/lib/active_support/i18n_railtie.rb +21 -14
  136. data/lib/active_support/inflector/inflections.rb +25 -7
  137. data/lib/active_support/inflector/methods.rb +50 -64
  138. data/lib/active_support/inflector/transliterate.rb +4 -2
  139. data/lib/active_support/isolated_execution_state.rb +76 -0
  140. data/lib/active_support/json/decoding.rb +2 -1
  141. data/lib/active_support/json/encoding.rb +27 -45
  142. data/lib/active_support/key_generator.rb +31 -6
  143. data/lib/active_support/lazy_load_hooks.rb +33 -7
  144. data/lib/active_support/locale/en.yml +4 -2
  145. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +97 -35
  147. data/lib/active_support/logger.rb +9 -60
  148. data/lib/active_support/logger_thread_safe_level.rb +11 -34
  149. data/lib/active_support/message_encryptor.rb +206 -56
  150. data/lib/active_support/message_encryptors.rb +141 -0
  151. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  152. data/lib/active_support/message_pack/extensions.rb +292 -0
  153. data/lib/active_support/message_pack/serializer.rb +63 -0
  154. data/lib/active_support/message_pack.rb +50 -0
  155. data/lib/active_support/message_verifier.rb +235 -84
  156. data/lib/active_support/message_verifiers.rb +135 -0
  157. data/lib/active_support/messages/codec.rb +65 -0
  158. data/lib/active_support/messages/metadata.rb +112 -46
  159. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  160. data/lib/active_support/messages/rotator.rb +34 -32
  161. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  162. data/lib/active_support/multibyte/chars.rb +12 -11
  163. data/lib/active_support/multibyte/unicode.rb +9 -49
  164. data/lib/active_support/multibyte.rb +1 -1
  165. data/lib/active_support/notifications/fanout.rb +304 -114
  166. data/lib/active_support/notifications/instrumenter.rb +117 -35
  167. data/lib/active_support/notifications.rb +25 -25
  168. data/lib/active_support/number_helper/number_converter.rb +14 -7
  169. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  170. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  171. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
  172. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  173. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  174. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  175. data/lib/active_support/number_helper.rb +379 -319
  176. data/lib/active_support/option_merger.rb +10 -18
  177. data/lib/active_support/ordered_hash.rb +4 -4
  178. data/lib/active_support/ordered_options.rb +15 -1
  179. data/lib/active_support/parameter_filter.rb +105 -81
  180. data/lib/active_support/proxy_object.rb +2 -0
  181. data/lib/active_support/railtie.rb +83 -21
  182. data/lib/active_support/reloader.rb +13 -5
  183. data/lib/active_support/rescuable.rb +18 -16
  184. data/lib/active_support/ruby_features.rb +7 -0
  185. data/lib/active_support/secure_compare_rotator.rb +18 -11
  186. data/lib/active_support/security_utils.rb +1 -1
  187. data/lib/active_support/string_inquirer.rb +3 -3
  188. data/lib/active_support/subscriber.rb +11 -40
  189. data/lib/active_support/syntax_error_proxy.rb +60 -0
  190. data/lib/active_support/tagged_logging.rb +65 -25
  191. data/lib/active_support/test_case.rb +166 -27
  192. data/lib/active_support/testing/assertions.rb +61 -15
  193. data/lib/active_support/testing/autorun.rb +0 -2
  194. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  195. data/lib/active_support/testing/deprecation.rb +53 -2
  196. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  197. data/lib/active_support/testing/isolation.rb +30 -29
  198. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  199. data/lib/active_support/testing/parallelization/server.rb +4 -0
  200. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  201. data/lib/active_support/testing/parallelization.rb +4 -0
  202. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  203. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  204. data/lib/active_support/testing/stream.rb +4 -6
  205. data/lib/active_support/testing/strict_warnings.rb +39 -0
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +49 -16
  208. data/lib/active_support/time_with_zone.rb +39 -28
  209. data/lib/active_support/values/time_zone.rb +50 -18
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +4 -11
  212. data/lib/active_support/xml_mini/libxml.rb +5 -5
  213. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  214. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  215. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  216. data/lib/active_support/xml_mini/rexml.rb +2 -2
  217. data/lib/active_support/xml_mini.rb +7 -6
  218. data/lib/active_support.rb +28 -1
  219. metadata +150 -18
  220. data/lib/active_support/core_ext/marshal.rb +0 -26
  221. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
  222. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  223. data/lib/active_support/core_ext/uri.rb +0 -29
  224. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  225. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -3,32 +3,32 @@
3
3
  require "delegate"
4
4
 
5
5
  module ActiveSupport
6
- module Tryable #:nodoc:
7
- def try(method_name = nil, *args, &b)
8
- if method_name.nil? && block_given?
9
- if b.arity == 0
10
- instance_eval(&b)
6
+ module Tryable # :nodoc:
7
+ def try(*args, &block)
8
+ if args.empty? && block_given?
9
+ if block.arity == 0
10
+ instance_eval(&block)
11
11
  else
12
12
  yield self
13
13
  end
14
- elsif respond_to?(method_name)
15
- public_send(method_name, *args, &b)
14
+ elsif respond_to?(args.first)
15
+ public_send(*args, &block)
16
16
  end
17
17
  end
18
- ruby2_keywords(:try) if respond_to?(:ruby2_keywords, true)
18
+ ruby2_keywords(:try)
19
19
 
20
- def try!(method_name = nil, *args, &b)
21
- if method_name.nil? && block_given?
22
- if b.arity == 0
23
- instance_eval(&b)
20
+ def try!(*args, &block)
21
+ if args.empty? && block_given?
22
+ if block.arity == 0
23
+ instance_eval(&block)
24
24
  else
25
25
  yield self
26
26
  end
27
27
  else
28
- public_send(method_name, *args, &b)
28
+ public_send(*args, &block)
29
29
  end
30
30
  end
31
- ruby2_keywords(:try!) if respond_to?(:ruby2_keywords, true)
31
+ ruby2_keywords(:try!)
32
32
  end
33
33
  end
34
34
 
@@ -39,7 +39,7 @@ class Object
39
39
  # :method: try
40
40
  #
41
41
  # :call-seq:
42
- # try(*a, &b)
42
+ # try(*args, &block)
43
43
  #
44
44
  # Invokes the public method whose name goes as first argument just like
45
45
  # +public_send+ does, except that if the receiver does not respond to it the
@@ -104,7 +104,7 @@ class Object
104
104
  # :method: try!
105
105
  #
106
106
  # :call-seq:
107
- # try!(*a, &b)
107
+ # try!(*args, &block)
108
108
  #
109
109
  # Same as #try, but raises a +NoMethodError+ exception if the receiver is
110
110
  # not +nil+ and does not implement the tried method.
@@ -121,7 +121,7 @@ class Delegator
121
121
  # :method: try
122
122
  #
123
123
  # :call-seq:
124
- # try(a*, &b)
124
+ # try(*args, &block)
125
125
  #
126
126
  # See Object#try
127
127
 
@@ -129,7 +129,7 @@ class Delegator
129
129
  # :method: try!
130
130
  #
131
131
  # :call-seq:
132
- # try!(a*, &b)
132
+ # try!(*args, &block)
133
133
  #
134
134
  # See Object#try!
135
135
  end
@@ -145,14 +145,14 @@ class NilClass
145
145
  #
146
146
  # With +try+
147
147
  # @person.try(:children).try(:first).try(:name)
148
- def try(_method_name = nil, *)
148
+ def try(*)
149
149
  nil
150
150
  end
151
151
 
152
152
  # Calling +try!+ on +nil+ always returns +nil+.
153
153
  #
154
154
  # nil.try!(:name) # => nil
155
- def try!(_method_name = nil, *)
155
+ def try!(*)
156
156
  nil
157
157
  end
158
158
  end
@@ -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,19 +64,38 @@ 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 }
75
75
  # end
76
76
  # end
77
77
  #
78
+ # When the block argument is omitted, the decorated Object instance is returned:
79
+ #
80
+ # module MyStyledHelpers
81
+ # def styled
82
+ # with_options style: "color: red;"
83
+ # end
84
+ # end
85
+ #
86
+ # styled.link_to "I'm red", "/"
87
+ # # => <a href="/" style="color: red;">I'm red</a>
88
+ #
89
+ # styled.button_tag "I'm red too!"
90
+ # # => <button style="color: red;">I'm red too!</button>
91
+ #
78
92
  def with_options(options, &block)
79
93
  option_merger = ActiveSupport::OptionMerger.new(self, options)
80
- block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
94
+
95
+ if block
96
+ block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
97
+ else
98
+ option_merger
99
+ end
81
100
  end
82
101
  end
@@ -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
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ class Pathname
6
+ # Returns the receiver if the named file exists otherwise returns +nil+.
7
+ # <tt>pathname.existence</tt> is equivalent to
8
+ #
9
+ # pathname.exist? ? pathname : nil
10
+ #
11
+ # For example, something like
12
+ #
13
+ # content = pathname.read if pathname.exist?
14
+ #
15
+ # becomes
16
+ #
17
+ # content = pathname.existence&.read
18
+ #
19
+ # @return [Pathname]
20
+ def existence
21
+ self if exist?
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/pathname/blank"
4
+ require "active_support/core_ext/pathname/existence"
@@ -51,31 +51,6 @@ module ActiveSupport
51
51
  super
52
52
  end
53
53
  end
54
-
55
- # Extends the default Range#cover? to support range comparisons.
56
- # (1..5).cover?(1..5) # => true
57
- # (1..5).cover?(2..3) # => true
58
- # (1..5).cover?(1...6) # => true
59
- # (1..5).cover?(2..6) # => false
60
- #
61
- # The native Range#cover? behavior is untouched.
62
- # ('a'..'f').cover?('c') # => true
63
- # (5..9).cover?(11) # => false
64
- #
65
- # The given range must be fully bounded, with both start and end.
66
- def cover?(value)
67
- if value.is_a?(::Range)
68
- is_backwards_op = value.exclude_end? ? :>= : :>
69
- return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
70
- # 1...10 covers 1..9 but it does not cover 1..10.
71
- # 1..10 covers 1...11 but it does not cover 1...12.
72
- operator = exclude_end? && !value.exclude_end? ? :< : :<=
73
- value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
74
- super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
75
- else
76
- super
77
- end
78
- end
79
54
  end
80
55
  end
81
56
 
@@ -1,40 +1,61 @@
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_s(:db)}' AND '#{stop.to_s(: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
  }
14
29
 
15
30
  # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
16
31
  #
32
+ # This method is aliased to <tt>to_formatted_s</tt>.
33
+ #
17
34
  # range = (1..100) # => 1..100
18
35
  #
19
36
  # range.to_s # => "1..100"
20
- # range.to_s(:db) # => "BETWEEN '1' AND '100'"
37
+ # range.to_fs(:db) # => "BETWEEN '1' AND '100'"
38
+ #
39
+ # range = (1..) # => 1..
40
+ # range.to_fs(:db) # => ">= '1'"
21
41
  #
22
- # == Adding your own range formats to to_s
42
+ # range = (..100) # => ..100
43
+ # range.to_fs(:db) # => "<= '100'"
44
+ #
45
+ # == Adding your own range formats to to_fs
23
46
  # You can add your own formats to the Range::RANGE_FORMATS hash.
24
47
  # Use the format name as the hash key and a Proc instance.
25
48
  #
26
49
  # # config/initializers/range_formats.rb
27
- # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
28
- def to_s(format = :default)
50
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_fs(:db)} and #{stop.to_fs(:db)}" }
51
+ def to_fs(format = :default)
29
52
  if formatter = RANGE_FORMATS[format]
30
- formatter.call(first, last)
53
+ formatter.call(self.begin, self.end)
31
54
  else
32
- super()
55
+ to_s
33
56
  end
34
57
  end
35
-
36
- alias_method :to_default_s, :to_s
37
- alias_method :to_formatted_s, :to_s
58
+ alias_method :to_formatted_s, :to_fs
38
59
  end
39
60
  end
40
61
 
@@ -3,7 +3,7 @@
3
3
  require "active_support/time_with_zone"
4
4
 
5
5
  module ActiveSupport
6
- module EachTimeWithZone #:nodoc:
6
+ module EachTimeWithZone # :nodoc:
7
7
  def each(&block)
8
8
  ensure_iteration_allowed
9
9
  super
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Range
4
+ # Compare two ranges and see if they overlap each other
5
+ # (1..5).overlap?(4..6) # => true
6
+ # (1..5).overlap?(7..9) # => false
7
+ unless Range.method_defined?(:overlap?)
8
+ def overlap?(other)
9
+ raise TypeError unless other.is_a? Range
10
+
11
+ self_begin = self.begin
12
+ other_end = other.end
13
+ other_excl = other.exclude_end?
14
+
15
+ return false if _empty_range?(self_begin, other_end, other_excl)
16
+
17
+ other_begin = other.begin
18
+ self_end = self.end
19
+ self_excl = self.exclude_end?
20
+
21
+ return false if _empty_range?(other_begin, self_end, self_excl)
22
+ return true if self_begin == other_begin
23
+
24
+ return false if _empty_range?(self_begin, self_end, self_excl)
25
+ return false if _empty_range?(other_begin, other_end, other_excl)
26
+
27
+ true
28
+ end
29
+
30
+ private
31
+ def _empty_range?(b, e, excl)
32
+ return false if b.nil? || e.nil?
33
+
34
+ comp = b <=> e
35
+ comp.nil? || comp > 0 || (comp == 0 && excl)
36
+ end
37
+ end
38
+
39
+ alias :overlaps? :overlap?
40
+ end
@@ -2,6 +2,5 @@
2
2
 
3
3
  require "active_support/core_ext/range/conversions"
4
4
  require "active_support/core_ext/range/compare_range"
5
- require "active_support/core_ext/range/include_time_with_zone"
6
- require "active_support/core_ext/range/overlaps"
5
+ require "active_support/core_ext/range/overlap"
7
6
  require "active_support/core_ext/range/each"
@@ -12,16 +12,22 @@ module SecureRandom
12
12
  #
13
13
  # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
14
14
  #
15
- # The result may contain alphanumeric characters except 0, O, I and l.
15
+ # The result may contain alphanumeric characters except 0, O, I, and l.
16
16
  #
17
17
  # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
18
18
  # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
19
- def self.base58(n = 16)
20
- SecureRandom.random_bytes(n).unpack("C*").map do |byte|
21
- idx = byte % 64
22
- idx = SecureRandom.random_number(58) if idx >= 58
23
- BASE58_ALPHABET[idx]
24
- end.join
19
+ if RUBY_VERSION >= "3.3"
20
+ def self.base58(n = 16)
21
+ SecureRandom.alphanumeric(n, chars: BASE58_ALPHABET)
22
+ end
23
+ else
24
+ def self.base58(n = 16)
25
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
26
+ idx = byte % 64
27
+ idx = SecureRandom.random_number(58) if idx >= 58
28
+ BASE58_ALPHABET[idx]
29
+ end.join
30
+ end
25
31
  end
26
32
 
27
33
  # SecureRandom.base36 generates a random base36 string in lowercase.
@@ -35,11 +41,17 @@ module SecureRandom
35
41
  #
36
42
  # p SecureRandom.base36 # => "4kugl2pdqmscqtje"
37
43
  # p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
38
- def self.base36(n = 16)
39
- SecureRandom.random_bytes(n).unpack("C*").map do |byte|
40
- idx = byte % 64
41
- idx = SecureRandom.random_number(36) if idx >= 36
42
- BASE36_ALPHABET[idx]
43
- end.join
44
+ if RUBY_VERSION >= "3.3"
45
+ def self.base36(n = 16)
46
+ SecureRandom.alphanumeric(n, chars: BASE36_ALPHABET)
47
+ end
48
+ else
49
+ def self.base36(n = 16)
50
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
51
+ idx = byte % 64
52
+ idx = SecureRandom.random_number(36) if idx >= 36
53
+ BASE36_ALPHABET[idx]
54
+ end.join
55
+ end
44
56
  end
45
57
  end
@@ -5,10 +5,10 @@ require "active_support/core_ext/time/calculations"
5
5
 
6
6
  class String
7
7
  # Converts a string to a Time value.
8
- # The +form+ can be either :utc or :local (default :local).
8
+ # The +form+ can be either +:utc+ or +:local+ (default +:local+).
9
9
  #
10
10
  # The time is parsed using Time.parse method.
11
- # If +form+ is :local, then the time is in the system timezone.
11
+ # If +form+ is +:local+, then the time is in the system timezone.
12
12
  # If the date part is missing then the current date is used and if
13
13
  # the time part is missing then it is assumed to be 00:00:00.
14
14
  #
@@ -45,7 +45,7 @@ class String
45
45
  self
46
46
  end
47
47
 
48
- # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
48
+ # Truncates a given +text+ to length <tt>truncate_to</tt> if +text+ is longer than <tt>truncate_to</tt>:
49
49
  #
50
50
  # 'Once upon a time in a world far far away'.truncate(27)
51
51
  # # => "Once upon a time in a wo..."
@@ -58,16 +58,20 @@ class String
58
58
  # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
59
59
  # # => "Once upon a time in a..."
60
60
  #
61
- # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
62
- # for a total length not exceeding <tt>length</tt>:
61
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...").
62
+ # The total length will not exceed <tt>truncate_to</tt> unless both +text+ and <tt>:omission</tt>
63
+ # are longer than <tt>truncate_to</tt>:
63
64
  #
64
65
  # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
65
66
  # # => "And they f... (continued)"
66
- def truncate(truncate_at, options = {})
67
- return dup unless length > truncate_at
67
+ #
68
+ # 'And they found that many people were sleeping better.'.truncate(4, omission: '... (continued)')
69
+ # # => "... (continued)"
70
+ def truncate(truncate_to, options = {})
71
+ return dup unless length > truncate_to
68
72
 
69
73
  omission = options[:omission] || "..."
70
- length_with_room_for_omission = truncate_at - omission.length
74
+ length_with_room_for_omission = truncate_to - omission.length
71
75
  stop = \
72
76
  if options[:separator]
73
77
  rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
@@ -78,7 +82,7 @@ class String
78
82
  +"#{self[0, stop]}#{omission}"
79
83
  end
80
84
 
81
- # Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
85
+ # Truncates +text+ to at most <tt>truncate_to</tt> bytes in length without
82
86
  # breaking string encoding by splitting multibyte characters or breaking
83
87
  # grapheme clusters ("perceptual characters") by truncating at combining
84
88
  # characters.
@@ -91,22 +95,24 @@ class String
91
95
  # => "🔪🔪🔪🔪…"
92
96
  #
93
97
  # The truncated text ends with the <tt>:omission</tt> string, defaulting
94
- # to "…", for a total length not exceeding <tt>bytesize</tt>.
95
- def truncate_bytes(truncate_at, omission: "…")
98
+ # to "…", for a total length not exceeding <tt>truncate_to</tt>.
99
+ #
100
+ # Raises +ArgumentError+ when the bytesize of <tt>:omission</tt> exceeds <tt>truncate_to</tt>.
101
+ def truncate_bytes(truncate_to, omission: "…")
96
102
  omission ||= ""
97
103
 
98
104
  case
99
- when bytesize <= truncate_at
105
+ when bytesize <= truncate_to
100
106
  dup
101
- when omission.bytesize > truncate_at
102
- raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_at} bytes"
103
- when omission.bytesize == truncate_at
107
+ when omission.bytesize > truncate_to
108
+ raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_to} bytes"
109
+ when omission.bytesize == truncate_to
104
110
  omission.dup
105
111
  else
106
112
  self.class.new.tap do |cut|
107
- cut_at = truncate_at - omission.bytesize
113
+ cut_at = truncate_to - omission.bytesize
108
114
 
109
- scan(/\X/) do |grapheme|
115
+ each_grapheme_cluster do |grapheme|
110
116
  if cut.bytesize + grapheme.bytesize <= cut_at
111
117
  cut << grapheme
112
118
  else
@@ -24,7 +24,7 @@ class String
24
24
  #
25
25
  # The second argument, +indent_string+, specifies which indent string to
26
26
  # use. The default is +nil+, which tells the method to make a guess by
27
- # peeking at the first indented line, and fallback to a space if there is
27
+ # peeking at the first indented line, and fall back to a space if there is
28
28
  # none.
29
29
  #
30
30
  # " foo".indent(2) # => " foo"
@@ -97,8 +97,6 @@ class String
97
97
  # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
98
98
  # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
99
99
  #
100
- # +camelize+ is also aliased as +camelcase+.
101
- #
102
100
  # See ActiveSupport::Inflector.camelize.
103
101
  def camelize(first_letter = :upper)
104
102
  case first_letter
@@ -114,7 +112,7 @@ class String
114
112
 
115
113
  # Capitalizes all the words and replaces some characters in the string to create
116
114
  # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
117
- # used in the Rails internals.
115
+ # used in the \Rails internals.
118
116
  #
119
117
  # The trailing '_id','Id'.. can be kept and capitalized by setting the
120
118
  # optional parameter +keep_id_suffix+ to true.
@@ -124,8 +122,6 @@ class String
124
122
  # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
125
123
  # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
126
124
  #
127
- # +titleize+ is also aliased as +titlecase+.
128
- #
129
125
  # See ActiveSupport::Inflector.titleize.
130
126
  def titleize(keep_id_suffix: false)
131
127
  ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
@@ -220,7 +216,7 @@ class String
220
216
  ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
221
217
  end
222
218
 
223
- # Creates the name of a table like Rails does for models to table names. This method
219
+ # Creates the name of a table like \Rails does for models to table names. This method
224
220
  # uses the +pluralize+ method on the last word in the string.
225
221
  #
226
222
  # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
@@ -232,7 +228,7 @@ class String
232
228
  ActiveSupport::Inflector.tableize(self)
233
229
  end
234
230
 
235
- # Creates a class name from a plural table name like Rails does for table names to models.
231
+ # Creates a class name from a plural table name like \Rails does for table names to models.
236
232
  # Note that this returns a string and not a class. (To convert to an actual class
237
233
  # follow +classify+ with +constantize+.)
238
234
  #
@@ -244,7 +240,7 @@ class String
244
240
  ActiveSupport::Inflector.classify(self)
245
241
  end
246
242
 
247
- # Capitalizes the first word, turns underscores into spaces, and (by default)strips a
243
+ # Capitalizes the first word, turns underscores into spaces, and (by default) strips a
248
244
  # trailing '_id' if present.
249
245
  # Like +titleize+, this is meant for creating pretty output.
250
246
  #
@@ -260,14 +256,14 @@ class String
260
256
  # 'author_id'.humanize # => "Author"
261
257
  # 'author_id'.humanize(capitalize: false) # => "author"
262
258
  # '_id'.humanize # => "Id"
263
- # 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
259
+ # 'author_id'.humanize(keep_id_suffix: true) # => "Author id"
264
260
  #
265
261
  # See ActiveSupport::Inflector.humanize.
266
262
  def humanize(capitalize: true, keep_id_suffix: false)
267
263
  ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
268
264
  end
269
265
 
270
- # Converts just the first character to uppercase.
266
+ # Converts the first character to uppercase.
271
267
  #
272
268
  # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
273
269
  # 'w'.upcase_first # => "W"
@@ -278,6 +274,17 @@ class String
278
274
  ActiveSupport::Inflector.upcase_first(self)
279
275
  end
280
276
 
277
+ # Converts the first character to lowercase.
278
+ #
279
+ # 'If they enjoyed The Matrix'.downcase_first # => "if they enjoyed The Matrix"
280
+ # 'I'.downcase_first # => "i"
281
+ # ''.downcase_first # => ""
282
+ #
283
+ # See ActiveSupport::Inflector.downcase_first.
284
+ def downcase_first
285
+ ActiveSupport::Inflector.downcase_first(self)
286
+ end
287
+
281
288
  # Creates a foreign key name from a class name.
282
289
  # +separate_class_name_and_id_with_underscore+ sets whether
283
290
  # the method should put '_' between the name and 'id'.