activesupport 7.0.8.7 → 7.1.0.beta1

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +722 -314
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  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 +25 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/builder.rb +1 -1
  10. data/lib/active_support/cache/coder.rb +153 -0
  11. data/lib/active_support/cache/entry.rb +128 -0
  12. data/lib/active_support/cache/file_store.rb +36 -9
  13. data/lib/active_support/cache/mem_cache_store.rb +84 -68
  14. data/lib/active_support/cache/memory_store.rb +76 -24
  15. data/lib/active_support/cache/null_store.rb +6 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +126 -131
  17. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +20 -8
  19. data/lib/active_support/cache.rb +304 -246
  20. data/lib/active_support/callbacks.rb +38 -18
  21. data/lib/active_support/concern.rb +4 -2
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  23. data/lib/active_support/concurrency/null_lock.rb +13 -0
  24. data/lib/active_support/configurable.rb +10 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  26. data/lib/active_support/core_ext/array.rb +0 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  28. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  29. data/lib/active_support/core_ext/date.rb +0 -1
  30. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  31. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  32. data/lib/active_support/core_ext/date_time.rb +0 -1
  33. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  34. data/lib/active_support/core_ext/enumerable.rb +3 -75
  35. data/lib/active_support/core_ext/erb/util.rb +196 -0
  36. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  39. data/lib/active_support/core_ext/module/delegation.rb +40 -11
  40. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  41. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  42. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  43. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  44. data/lib/active_support/core_ext/numeric.rb +0 -1
  45. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  46. data/lib/active_support/core_ext/object/duplicable.rb +15 -24
  47. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  48. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  49. data/lib/active_support/core_ext/object/json.rb +10 -2
  50. data/lib/active_support/core_ext/object/with.rb +44 -0
  51. data/lib/active_support/core_ext/object/with_options.rb +3 -3
  52. data/lib/active_support/core_ext/object.rb +1 -0
  53. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  54. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  55. data/lib/active_support/core_ext/pathname.rb +1 -0
  56. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  57. data/lib/active_support/core_ext/range/{overlaps.rb → overlap.rb} +5 -3
  58. data/lib/active_support/core_ext/range.rb +1 -2
  59. data/lib/active_support/core_ext/securerandom.rb +24 -12
  60. data/lib/active_support/core_ext/string/filters.rb +20 -14
  61. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  62. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  63. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  64. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  65. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  66. data/lib/active_support/core_ext/time/zones.rb +4 -4
  67. data/lib/active_support/core_ext/time.rb +0 -1
  68. data/lib/active_support/current_attributes.rb +15 -6
  69. data/lib/active_support/dependencies/autoload.rb +17 -12
  70. data/lib/active_support/deprecation/behaviors.rb +53 -32
  71. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  72. data/lib/active_support/deprecation/deprecators.rb +104 -0
  73. data/lib/active_support/deprecation/disallowed.rb +3 -5
  74. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  75. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  76. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  77. data/lib/active_support/deprecation/reporting.rb +35 -21
  78. data/lib/active_support/deprecation.rb +32 -5
  79. data/lib/active_support/deprecator.rb +7 -0
  80. data/lib/active_support/descendants_tracker.rb +104 -132
  81. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  82. data/lib/active_support/duration.rb +2 -1
  83. data/lib/active_support/encrypted_configuration.rb +30 -9
  84. data/lib/active_support/encrypted_file.rb +8 -3
  85. data/lib/active_support/environment_inquirer.rb +22 -2
  86. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  87. data/lib/active_support/error_reporter.rb +121 -35
  88. data/lib/active_support/execution_wrapper.rb +4 -4
  89. data/lib/active_support/file_update_checker.rb +4 -2
  90. data/lib/active_support/fork_tracker.rb +10 -2
  91. data/lib/active_support/gem_version.rb +4 -4
  92. data/lib/active_support/gzip.rb +2 -0
  93. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  94. data/lib/active_support/i18n.rb +1 -1
  95. data/lib/active_support/i18n_railtie.rb +20 -13
  96. data/lib/active_support/inflector/inflections.rb +2 -0
  97. data/lib/active_support/inflector/methods.rb +22 -10
  98. data/lib/active_support/inflector/transliterate.rb +3 -1
  99. data/lib/active_support/isolated_execution_state.rb +26 -22
  100. data/lib/active_support/json/decoding.rb +2 -1
  101. data/lib/active_support/json/encoding.rb +25 -43
  102. data/lib/active_support/key_generator.rb +9 -1
  103. data/lib/active_support/lazy_load_hooks.rb +6 -4
  104. data/lib/active_support/locale/en.yml +2 -0
  105. data/lib/active_support/log_subscriber.rb +78 -33
  106. data/lib/active_support/logger.rb +1 -1
  107. data/lib/active_support/logger_thread_safe_level.rb +9 -21
  108. data/lib/active_support/message_encryptor.rb +197 -53
  109. data/lib/active_support/message_encryptors.rb +140 -0
  110. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  111. data/lib/active_support/message_pack/extensions.rb +292 -0
  112. data/lib/active_support/message_pack/serializer.rb +63 -0
  113. data/lib/active_support/message_pack.rb +50 -0
  114. data/lib/active_support/message_verifier.rb +212 -93
  115. data/lib/active_support/message_verifiers.rb +134 -0
  116. data/lib/active_support/messages/codec.rb +65 -0
  117. data/lib/active_support/messages/metadata.rb +111 -45
  118. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  119. data/lib/active_support/messages/rotator.rb +34 -32
  120. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  121. data/lib/active_support/multibyte/chars.rb +2 -0
  122. data/lib/active_support/multibyte/unicode.rb +9 -37
  123. data/lib/active_support/notifications/fanout.rb +239 -81
  124. data/lib/active_support/notifications/instrumenter.rb +71 -14
  125. data/lib/active_support/notifications.rb +1 -1
  126. data/lib/active_support/number_helper/number_converter.rb +2 -2
  127. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  128. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  129. data/lib/active_support/ordered_hash.rb +3 -3
  130. data/lib/active_support/ordered_options.rb +14 -0
  131. data/lib/active_support/parameter_filter.rb +84 -69
  132. data/lib/active_support/proxy_object.rb +2 -0
  133. data/lib/active_support/railtie.rb +33 -21
  134. data/lib/active_support/reloader.rb +12 -4
  135. data/lib/active_support/rescuable.rb +2 -0
  136. data/lib/active_support/secure_compare_rotator.rb +16 -9
  137. data/lib/active_support/string_inquirer.rb +3 -1
  138. data/lib/active_support/subscriber.rb +9 -27
  139. data/lib/active_support/syntax_error_proxy.rb +49 -0
  140. data/lib/active_support/tagged_logging.rb +60 -24
  141. data/lib/active_support/test_case.rb +153 -6
  142. data/lib/active_support/testing/assertions.rb +25 -9
  143. data/lib/active_support/testing/autorun.rb +0 -2
  144. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  145. data/lib/active_support/testing/deprecation.rb +25 -25
  146. data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
  147. data/lib/active_support/testing/isolation.rb +1 -1
  148. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  149. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  150. data/lib/active_support/testing/stream.rb +1 -1
  151. data/lib/active_support/testing/strict_warnings.rb +38 -0
  152. data/lib/active_support/testing/time_helpers.rb +32 -14
  153. data/lib/active_support/time_with_zone.rb +4 -14
  154. data/lib/active_support/values/time_zone.rb +9 -7
  155. data/lib/active_support/version.rb +1 -1
  156. data/lib/active_support/xml_mini/jdom.rb +3 -10
  157. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  158. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  159. data/lib/active_support/xml_mini/rexml.rb +1 -1
  160. data/lib/active_support/xml_mini.rb +2 -2
  161. data/lib/active_support.rb +13 -3
  162. metadata +106 -21
  163. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  164. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  165. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  166. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  167. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  168. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  169. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  170. data/lib/active_support/core_ext/uri.rb +0 -5
  171. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -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"
@@ -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
 
@@ -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
  #
@@ -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
@@ -2,9 +2,11 @@
2
2
 
3
3
  class Range
4
4
  # Compare two ranges and see if they overlap each other
5
- # (1..5).overlaps?(4..6) # => true
6
- # (1..5).overlaps?(7..9) # => false
7
- def overlaps?(other)
5
+ # (1..5).overlap?(4..6) # => true
6
+ # (1..5).overlap?(7..9) # => false
7
+ def overlap?(other)
8
8
  other.begin == self.begin || cover?(other.begin) || other.cover?(self.begin)
9
9
  end
10
+
11
+ alias :overlaps? :overlap?
10
12
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/range/conversions"
4
- require "active_support/core_ext/range/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
5
4
  require "active_support/core_ext/range/compare_range"
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"
@@ -16,12 +16,18 @@ module SecureRandom
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
@@ -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,20 +95,22 @@ 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
115
  each_grapheme_cluster do |grapheme|
110
116
  if cut.bytesize + grapheme.bytesize <= cut_at
@@ -112,7 +112,7 @@ class String
112
112
 
113
113
  # Capitalizes all the words and replaces some characters in the string to create
114
114
  # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
115
- # used in the Rails internals.
115
+ # used in the \Rails internals.
116
116
  #
117
117
  # The trailing '_id','Id'.. can be kept and capitalized by setting the
118
118
  # optional parameter +keep_id_suffix+ to true.
@@ -216,7 +216,7 @@ class String
216
216
  ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
217
217
  end
218
218
 
219
- # 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
220
220
  # uses the +pluralize+ method on the last word in the string.
221
221
  #
222
222
  # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
@@ -228,7 +228,7 @@ class String
228
228
  ActiveSupport::Inflector.tableize(self)
229
229
  end
230
230
 
231
- # 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.
232
232
  # Note that this returns a string and not a class. (To convert to an actual class
233
233
  # follow +classify+ with +constantize+.)
234
234
  #
@@ -240,7 +240,7 @@ class String
240
240
  ActiveSupport::Inflector.classify(self)
241
241
  end
242
242
 
243
- # 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
244
244
  # trailing '_id' if present.
245
245
  # Like +titleize+, this is meant for creating pretty output.
246
246
  #
@@ -263,7 +263,7 @@ class String
263
263
  ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
264
264
  end
265
265
 
266
- # Converts just the first character to uppercase.
266
+ # Converts the first character to uppercase.
267
267
  #
268
268
  # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
269
269
  # 'w'.upcase_first # => "W"
@@ -274,6 +274,17 @@ class String
274
274
  ActiveSupport::Inflector.upcase_first(self)
275
275
  end
276
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
+
277
288
  # Creates a foreign key name from a class name.
278
289
  # +separate_class_name_and_id_with_underscore+ sets whether
279
290
  # the method should put '_' between the name and 'id'.