activesupport 5.2.4.3 → 7.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +244 -459
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +31 -5
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +47 -41
  10. data/lib/active_support/cache/mem_cache_store.rb +151 -40
  11. data/lib/active_support/cache/memory_store.rb +68 -34
  12. data/lib/active_support/cache/null_store.rb +16 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +103 -101
  14. data/lib/active_support/cache/strategy/local_cache.rb +56 -64
  15. data/lib/active_support/cache.rb +333 -116
  16. data/lib/active_support/callbacks.rb +244 -128
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +72 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  20. data/lib/active_support/concurrency/share_lock.rb +2 -3
  21. data/lib/active_support/configurable.rb +15 -16
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +15 -7
  24. data/lib/active_support/core_ext/array/conversions.rb +18 -17
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +2 -1
  30. data/lib/active_support/core_ext/benchmark.rb +2 -2
  31. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  32. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  33. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  34. data/lib/active_support/core_ext/date/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date/calculations.rb +15 -14
  36. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  37. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  38. data/lib/active_support/core_ext/date.rb +1 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  44. data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
  45. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  46. data/lib/active_support/core_ext/date_time.rb +1 -0
  47. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  48. data/lib/active_support/core_ext/enumerable.rb +241 -76
  49. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  50. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  52. data/lib/active_support/core_ext/hash/except.rb +2 -2
  53. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  54. data/lib/active_support/core_ext/hash/keys.rb +2 -31
  55. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  56. data/lib/active_support/core_ext/hash.rb +1 -2
  57. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  58. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  59. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  60. data/lib/active_support/core_ext/kernel.rb +0 -1
  61. data/lib/active_support/core_ext/load_error.rb +1 -1
  62. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  63. data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
  64. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
  65. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  66. data/lib/active_support/core_ext/module/delegation.rb +70 -33
  67. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  68. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  69. data/lib/active_support/core_ext/module.rb +0 -1
  70. data/lib/active_support/core_ext/name_error.rb +23 -2
  71. data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
  72. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  73. data/lib/active_support/core_ext/numeric.rb +1 -1
  74. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  75. data/lib/active_support/core_ext/object/blank.rb +3 -4
  76. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  77. data/lib/active_support/core_ext/object/duplicable.rb +14 -110
  78. data/lib/active_support/core_ext/object/json.rb +44 -27
  79. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  80. data/lib/active_support/core_ext/object/try.rb +24 -14
  81. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  82. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  83. data/lib/active_support/core_ext/pathname.rb +3 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +23 -27
  85. data/lib/active_support/core_ext/range/conversions.rb +32 -30
  86. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  87. data/lib/active_support/core_ext/range/each.rb +1 -2
  88. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  89. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  90. data/lib/active_support/core_ext/range.rb +1 -1
  91. data/lib/active_support/core_ext/regexp.rb +8 -5
  92. data/lib/active_support/core_ext/securerandom.rb +23 -3
  93. data/lib/active_support/core_ext/string/access.rb +5 -16
  94. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  95. data/lib/active_support/core_ext/string/filters.rb +42 -1
  96. data/lib/active_support/core_ext/string/inflections.rb +46 -7
  97. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  98. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  99. data/lib/active_support/core_ext/string/output_safety.rb +129 -20
  100. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  101. data/lib/active_support/core_ext/string/strip.rb +3 -1
  102. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  103. data/lib/active_support/core_ext/symbol.rb +3 -0
  104. data/lib/active_support/core_ext/time/calculations.rb +59 -10
  105. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  106. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  107. data/lib/active_support/core_ext/time/zones.rb +7 -22
  108. data/lib/active_support/core_ext/time.rb +1 -0
  109. data/lib/active_support/core_ext/uri.rb +3 -22
  110. data/lib/active_support/core_ext.rb +2 -1
  111. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  112. data/lib/active_support/current_attributes.rb +47 -16
  113. data/lib/active_support/dependencies/interlock.rb +10 -18
  114. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  115. data/lib/active_support/dependencies.rb +60 -715
  116. data/lib/active_support/deprecation/behaviors.rb +21 -5
  117. data/lib/active_support/deprecation/disallowed.rb +56 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  119. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
  121. data/lib/active_support/deprecation/reporting.rb +50 -7
  122. data/lib/active_support/deprecation.rb +7 -2
  123. data/lib/active_support/descendants_tracker.rb +190 -34
  124. data/lib/active_support/digest.rb +5 -3
  125. data/lib/active_support/duration/iso8601_parser.rb +5 -7
  126. data/lib/active_support/duration/iso8601_serializer.rb +27 -15
  127. data/lib/active_support/duration.rb +149 -67
  128. data/lib/active_support/encrypted_configuration.rb +12 -5
  129. data/lib/active_support/encrypted_file.rb +23 -5
  130. data/lib/active_support/environment_inquirer.rb +20 -0
  131. data/lib/active_support/error_reporter.rb +117 -0
  132. data/lib/active_support/evented_file_update_checker.rb +85 -122
  133. data/lib/active_support/execution_context/test_helper.rb +13 -0
  134. data/lib/active_support/execution_context.rb +53 -0
  135. data/lib/active_support/execution_wrapper.rb +44 -21
  136. data/lib/active_support/executor/test_helper.rb +7 -0
  137. data/lib/active_support/file_update_checker.rb +0 -1
  138. data/lib/active_support/fork_tracker.rb +71 -0
  139. data/lib/active_support/gem_version.rb +5 -5
  140. data/lib/active_support/hash_with_indifferent_access.rb +73 -43
  141. data/lib/active_support/html_safe_translation.rb +43 -0
  142. data/lib/active_support/i18n.rb +2 -0
  143. data/lib/active_support/i18n_railtie.rb +15 -8
  144. data/lib/active_support/inflector/inflections.rb +25 -14
  145. data/lib/active_support/inflector/methods.rb +38 -71
  146. data/lib/active_support/inflector/transliterate.rb +47 -18
  147. data/lib/active_support/isolated_execution_state.rb +72 -0
  148. data/lib/active_support/json/decoding.rb +25 -26
  149. data/lib/active_support/json/encoding.rb +14 -6
  150. data/lib/active_support/key_generator.rb +23 -38
  151. data/lib/active_support/lazy_load_hooks.rb +19 -5
  152. data/lib/active_support/locale/en.rb +33 -0
  153. data/lib/active_support/locale/en.yml +8 -4
  154. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  155. data/lib/active_support/log_subscriber.rb +51 -11
  156. data/lib/active_support/logger.rb +6 -22
  157. data/lib/active_support/logger_silence.rb +11 -19
  158. data/lib/active_support/logger_thread_safe_level.rb +45 -10
  159. data/lib/active_support/message_encryptor.rb +20 -19
  160. data/lib/active_support/message_verifier.rb +53 -21
  161. data/lib/active_support/messages/metadata.rb +13 -4
  162. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  163. data/lib/active_support/messages/rotator.rb +10 -9
  164. data/lib/active_support/multibyte/chars.rb +17 -76
  165. data/lib/active_support/multibyte/unicode.rb +7 -331
  166. data/lib/active_support/multibyte.rb +1 -1
  167. data/lib/active_support/notifications/fanout.rb +163 -37
  168. data/lib/active_support/notifications/instrumenter.rb +90 -11
  169. data/lib/active_support/notifications.rb +88 -30
  170. data/lib/active_support/number_helper/number_converter.rb +6 -9
  171. data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
  172. data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
  173. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  174. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
  175. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  176. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
  177. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  178. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  179. data/lib/active_support/number_helper.rb +36 -12
  180. data/lib/active_support/option_merger.rb +15 -4
  181. data/lib/active_support/ordered_hash.rb +2 -2
  182. data/lib/active_support/ordered_options.rb +14 -4
  183. data/lib/active_support/parameter_filter.rb +138 -0
  184. data/lib/active_support/per_thread_registry.rb +6 -1
  185. data/lib/active_support/rails.rb +1 -10
  186. data/lib/active_support/railtie.rb +77 -5
  187. data/lib/active_support/reloader.rb +5 -6
  188. data/lib/active_support/rescuable.rb +8 -8
  189. data/lib/active_support/ruby_features.rb +7 -0
  190. data/lib/active_support/secure_compare_rotator.rb +51 -0
  191. data/lib/active_support/security_utils.rb +19 -12
  192. data/lib/active_support/string_inquirer.rb +2 -3
  193. data/lib/active_support/subscriber.rb +79 -46
  194. data/lib/active_support/tagged_logging.rb +58 -9
  195. data/lib/active_support/test_case.rb +79 -0
  196. data/lib/active_support/testing/assertions.rb +62 -11
  197. data/lib/active_support/testing/deprecation.rb +52 -2
  198. data/lib/active_support/testing/file_fixtures.rb +2 -0
  199. data/lib/active_support/testing/isolation.rb +4 -4
  200. data/lib/active_support/testing/method_call_assertions.rb +32 -5
  201. data/lib/active_support/testing/parallelization/server.rb +82 -0
  202. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  203. data/lib/active_support/testing/parallelization.rb +55 -0
  204. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  205. data/lib/active_support/testing/stream.rb +4 -7
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +60 -14
  208. data/lib/active_support/time_with_zone.rb +139 -64
  209. data/lib/active_support/values/time_zone.rb +66 -30
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +3 -4
  212. data/lib/active_support/xml_mini/libxml.rb +7 -7
  213. data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
  214. data/lib/active_support/xml_mini/nokogiri.rb +6 -6
  215. data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
  216. data/lib/active_support/xml_mini/rexml.rb +11 -4
  217. data/lib/active_support/xml_mini.rb +7 -14
  218. data/lib/active_support.rb +30 -1
  219. metadata +64 -35
  220. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  221. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  222. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  223. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  224. data/lib/active_support/core_ext/marshal.rb +0 -24
  225. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  226. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  227. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  228. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module DeprecatedRangeWithFormat # :nodoc:
5
+ NOT_SET = Object.new # :nodoc:
6
+ def to_s(format = NOT_SET)
7
+ if formatter = RangeWithFormat::RANGE_FORMATS[format]
8
+ ActiveSupport::Deprecation.warn(
9
+ "Range#to_s(#{format.inspect}) is deprecated. Please use Range#to_fs(#{format.inspect}) instead."
10
+ )
11
+ formatter.call(first, last)
12
+ elsif format == NOT_SET
13
+ super()
14
+ else
15
+ ActiveSupport::Deprecation.warn(
16
+ "Range#to_s(#{format.inspect}) is deprecated. Please use Range#to_fs(#{format.inspect}) instead."
17
+ )
18
+ super()
19
+ end
20
+ end
21
+ alias_method :to_default_s, :to_s
22
+ deprecate :to_default_s
23
+ end
24
+ end
25
+
26
+ Range.prepend(ActiveSupport::DeprecatedRangeWithFormat)
@@ -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
@@ -15,7 +15,6 @@ module ActiveSupport
15
15
  end
16
16
 
17
17
  private
18
-
19
18
  def ensure_iteration_allowed
20
19
  raise TypeError, "can't iterate from #{first.class}" if first.is_a?(TimeWithZone)
21
20
  end
@@ -1,23 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/time_with_zone"
4
-
5
- module ActiveSupport
6
- module IncludeTimeWithZone #:nodoc:
7
- # Extends the default Range#include? to support ActiveSupport::TimeWithZone.
8
- #
9
- # (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
10
- #
11
- def include?(value)
12
- if first.is_a?(TimeWithZone)
13
- cover?(value)
14
- elsif last.is_a?(TimeWithZone)
15
- cover?(value)
16
- else
17
- super
18
- end
19
- end
20
- end
21
- end
3
+ # frozen_string_literal: true
22
4
 
23
- Range.prepend(ActiveSupport::IncludeTimeWithZone)
5
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
6
+ `active_support/core_ext/range/include_time_with_zone` is deprecated and will be removed in Rails 7.1.
7
+ MSG
@@ -5,6 +5,6 @@ class Range
5
5
  # (1..5).overlaps?(4..6) # => true
6
6
  # (1..5).overlaps?(7..9) # => false
7
7
  def overlaps?(other)
8
- cover?(other.first) || other.cover?(first)
8
+ other.begin == self.begin || cover?(other.begin) || other.cover?(self.begin)
9
9
  end
10
10
  end
@@ -1,7 +1,7 @@
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"]
4
5
  require "active_support/core_ext/range/compare_range"
5
- require "active_support/core_ext/range/include_time_with_zone"
6
6
  require "active_support/core_ext/range/overlaps"
7
7
  require "active_support/core_ext/range/each"
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Regexp #:nodoc:
3
+ class Regexp
4
+ # Returns +true+ if the regexp has the multiline flag set.
5
+ #
6
+ # (/./).multiline? # => false
7
+ # (/./m).multiline? # => true
8
+ #
9
+ # Regexp.new(".").multiline? # => false
10
+ # Regexp.new(".", Regexp::MULTILINE).multiline? # => true
4
11
  def multiline?
5
12
  options & MULTILINE == MULTILINE
6
13
  end
7
-
8
- def match?(string, pos = 0)
9
- !!match(string, pos)
10
- end unless //.respond_to?(:match?)
11
14
  end
@@ -4,17 +4,18 @@ require "securerandom"
4
4
 
5
5
  module SecureRandom
6
6
  BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
7
+ BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
8
+
7
9
  # SecureRandom.base58 generates a random base58 string.
8
10
  #
9
- # The argument _n_ specifies the length, of the random string to be generated.
11
+ # The argument _n_ specifies the length of the random string to be generated.
10
12
  #
11
13
  # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
12
14
  #
13
- # 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.
14
16
  #
15
17
  # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
16
18
  # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
17
- #
18
19
  def self.base58(n = 16)
19
20
  SecureRandom.random_bytes(n).unpack("C*").map do |byte|
20
21
  idx = byte % 64
@@ -22,4 +23,23 @@ module SecureRandom
22
23
  BASE58_ALPHABET[idx]
23
24
  end.join
24
25
  end
26
+
27
+ # SecureRandom.base36 generates a random base36 string in lowercase.
28
+ #
29
+ # The argument _n_ specifies the length of the random string to be generated.
30
+ #
31
+ # If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
32
+ # This method can be used over +base58+ if a deterministic case key is necessary.
33
+ #
34
+ # The result will contain alphanumeric characters in lowercase.
35
+ #
36
+ # p SecureRandom.base36 # => "4kugl2pdqmscqtje"
37
+ # 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
+ end
25
45
  end
@@ -44,7 +44,7 @@ class String
44
44
  # str.from(0).to(-1) # => "hello"
45
45
  # str.from(1).to(-2) # => "ell"
46
46
  def from(position)
47
- self[position..-1]
47
+ self[position, length]
48
48
  end
49
49
 
50
50
  # Returns a substring from the beginning of the string to the given position.
@@ -61,7 +61,8 @@ class String
61
61
  # str.from(0).to(-1) # => "hello"
62
62
  # str.from(1).to(-2) # => "ell"
63
63
  def to(position)
64
- self[0..position]
64
+ position += size if position < 0
65
+ self[0, position + 1] || +""
65
66
  end
66
67
 
67
68
  # Returns the first character. If a limit is supplied, returns a substring
@@ -75,13 +76,7 @@ class String
75
76
  # str.first(0) # => ""
76
77
  # str.first(6) # => "hello"
77
78
  def first(limit = 1)
78
- if limit == 0
79
- ""
80
- elsif limit >= size
81
- dup
82
- else
83
- to(limit - 1)
84
- end
79
+ self[0, limit] || raise(ArgumentError, "negative limit")
85
80
  end
86
81
 
87
82
  # Returns the last character of the string. If a limit is supplied, returns a substring
@@ -95,12 +90,6 @@ class String
95
90
  # str.last(0) # => ""
96
91
  # str.last(6) # => "hello"
97
92
  def last(limit = 1)
98
- if limit == 0
99
- ""
100
- elsif limit >= size
101
- dup
102
- else
103
- from(-limit)
104
- end
93
+ self[[length - limit, 0].max, limit] || raise(ArgumentError, "negative limit")
105
94
  end
106
95
  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
  #
@@ -18,6 +18,7 @@ class String
18
18
  # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
19
19
  # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
20
20
  # "12/13/2012".to_time # => ArgumentError: argument out of range
21
+ # "1604326192".to_time # => ArgumentError: argument out of range
21
22
  def to_time(form = :local)
22
23
  parts = Date._parse(self, false)
23
24
  used_keys = %i(year mon mday hour min sec sec_fraction offset)
@@ -75,7 +75,48 @@ class String
75
75
  length_with_room_for_omission
76
76
  end
77
77
 
78
- "#{self[0, stop]}#{omission}"
78
+ +"#{self[0, stop]}#{omission}"
79
+ end
80
+
81
+ # Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
82
+ # breaking string encoding by splitting multibyte characters or breaking
83
+ # grapheme clusters ("perceptual characters") by truncating at combining
84
+ # characters.
85
+ #
86
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
87
+ # => 20
88
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
89
+ # => 80
90
+ # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
91
+ # => "🔪🔪🔪🔪…"
92
+ #
93
+ # 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: "…")
96
+ omission ||= ""
97
+
98
+ case
99
+ when bytesize <= truncate_at
100
+ 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
104
+ omission.dup
105
+ else
106
+ self.class.new.tap do |cut|
107
+ cut_at = truncate_at - omission.bytesize
108
+
109
+ each_grapheme_cluster do |grapheme|
110
+ if cut.bytesize + grapheme.bytesize <= cut_at
111
+ cut << grapheme
112
+ else
113
+ break
114
+ end
115
+ end
116
+
117
+ cut << omission
118
+ end
119
+ end
79
120
  end
80
121
 
81
122
  # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
@@ -30,6 +30,8 @@ class String
30
30
  # 'apple'.pluralize(2) # => "apples"
31
31
  # 'ley'.pluralize(:es) # => "leyes"
32
32
  # 'ley'.pluralize(1, :es) # => "ley"
33
+ #
34
+ # See ActiveSupport::Inflector.pluralize.
33
35
  def pluralize(count = nil, locale = :en)
34
36
  locale = count if count.is_a?(Symbol)
35
37
  if count == 1
@@ -53,28 +55,34 @@ class String
53
55
  # 'the blue mailmen'.singularize # => "the blue mailman"
54
56
  # 'CamelOctopi'.singularize # => "CamelOctopus"
55
57
  # 'leyes'.singularize(:es) # => "ley"
58
+ #
59
+ # See ActiveSupport::Inflector.singularize.
56
60
  def singularize(locale = :en)
57
61
  ActiveSupport::Inflector.singularize(self, locale)
58
62
  end
59
63
 
60
64
  # +constantize+ tries to find a declared constant with the name specified
61
65
  # in the string. It raises a NameError when the name is not in CamelCase
62
- # or is not initialized. See ActiveSupport::Inflector.constantize
66
+ # or is not initialized.
63
67
  #
64
68
  # 'Module'.constantize # => Module
65
69
  # 'Class'.constantize # => Class
66
70
  # 'blargle'.constantize # => NameError: wrong constant name blargle
71
+ #
72
+ # See ActiveSupport::Inflector.constantize.
67
73
  def constantize
68
74
  ActiveSupport::Inflector.constantize(self)
69
75
  end
70
76
 
71
77
  # +safe_constantize+ tries to find a declared constant with the name specified
72
78
  # in the string. It returns +nil+ when the name is not in CamelCase
73
- # or is not initialized. See ActiveSupport::Inflector.safe_constantize
79
+ # or is not initialized.
74
80
  #
75
81
  # 'Module'.safe_constantize # => Module
76
82
  # 'Class'.safe_constantize # => Class
77
83
  # 'blargle'.safe_constantize # => nil
84
+ #
85
+ # See ActiveSupport::Inflector.safe_constantize.
78
86
  def safe_constantize
79
87
  ActiveSupport::Inflector.safe_constantize(self)
80
88
  end
@@ -88,6 +96,10 @@ class String
88
96
  # 'active_record'.camelize(:lower) # => "activeRecord"
89
97
  # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
90
98
  # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
99
+ #
100
+ # +camelize+ is also aliased as +camelcase+.
101
+ #
102
+ # See ActiveSupport::Inflector.camelize.
91
103
  def camelize(first_letter = :upper)
92
104
  case first_letter
93
105
  when :upper
@@ -108,11 +120,13 @@ class String
108
120
  # optional parameter +keep_id_suffix+ to true.
109
121
  # By default, this parameter is false.
110
122
  #
111
- # +titleize+ is also aliased as +titlecase+.
112
- #
113
123
  # 'man from the boondocks'.titleize # => "Man From The Boondocks"
114
124
  # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
115
125
  # 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
126
+ #
127
+ # +titleize+ is also aliased as +titlecase+.
128
+ #
129
+ # See ActiveSupport::Inflector.titleize.
116
130
  def titleize(keep_id_suffix: false)
117
131
  ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
118
132
  end
@@ -124,6 +138,8 @@ class String
124
138
  #
125
139
  # 'ActiveModel'.underscore # => "active_model"
126
140
  # 'ActiveModel::Errors'.underscore # => "active_model/errors"
141
+ #
142
+ # See ActiveSupport::Inflector.underscore.
127
143
  def underscore
128
144
  ActiveSupport::Inflector.underscore(self)
129
145
  end
@@ -131,6 +147,8 @@ class String
131
147
  # Replaces underscores with dashes in the string.
132
148
  #
133
149
  # 'puni_puni'.dasherize # => "puni-puni"
150
+ #
151
+ # See ActiveSupport::Inflector.dasherize.
134
152
  def dasherize
135
153
  ActiveSupport::Inflector.dasherize(self)
136
154
  end
@@ -142,6 +160,8 @@ class String
142
160
  # '::Inflections'.demodulize # => "Inflections"
143
161
  # ''.demodulize # => ''
144
162
  #
163
+ # See ActiveSupport::Inflector.demodulize.
164
+ #
145
165
  # See also +deconstantize+.
146
166
  def demodulize
147
167
  ActiveSupport::Inflector.demodulize(self)
@@ -155,6 +175,8 @@ class String
155
175
  # '::String'.deconstantize # => ""
156
176
  # ''.deconstantize # => ""
157
177
  #
178
+ # See ActiveSupport::Inflector.deconstantize.
179
+ #
158
180
  # See also +demodulize+.
159
181
  def deconstantize
160
182
  ActiveSupport::Inflector.deconstantize(self)
@@ -162,6 +184,11 @@ class String
162
184
 
163
185
  # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
164
186
  #
187
+ # If the optional parameter +locale+ is specified,
188
+ # the word will be parameterized as a word of that language.
189
+ # By default, this parameter is set to <tt>nil</tt> and it will use
190
+ # the configured <tt>I18n.locale</tt>.
191
+ #
165
192
  # class Person
166
193
  # def to_param
167
194
  # "#{id}-#{name.parameterize}"
@@ -187,8 +214,10 @@ class String
187
214
  #
188
215
  # <%= link_to(@person.name, person_path) %>
189
216
  # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
190
- def parameterize(separator: "-", preserve_case: false)
191
- ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
217
+ #
218
+ # See ActiveSupport::Inflector.parameterize.
219
+ def parameterize(separator: "-", preserve_case: false, locale: nil)
220
+ ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
192
221
  end
193
222
 
194
223
  # Creates the name of a table like Rails does for models to table names. This method
@@ -197,6 +226,8 @@ class String
197
226
  # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
198
227
  # 'ham_and_egg'.tableize # => "ham_and_eggs"
199
228
  # 'fancyCategory'.tableize # => "fancy_categories"
229
+ #
230
+ # See ActiveSupport::Inflector.tableize.
200
231
  def tableize
201
232
  ActiveSupport::Inflector.tableize(self)
202
233
  end
@@ -207,6 +238,8 @@ class String
207
238
  #
208
239
  # 'ham_and_eggs'.classify # => "HamAndEgg"
209
240
  # 'posts'.classify # => "Post"
241
+ #
242
+ # See ActiveSupport::Inflector.classify.
210
243
  def classify
211
244
  ActiveSupport::Inflector.classify(self)
212
245
  end
@@ -227,7 +260,9 @@ class String
227
260
  # 'author_id'.humanize # => "Author"
228
261
  # 'author_id'.humanize(capitalize: false) # => "author"
229
262
  # '_id'.humanize # => "Id"
230
- # 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
263
+ # 'author_id'.humanize(keep_id_suffix: true) # => "Author id"
264
+ #
265
+ # See ActiveSupport::Inflector.humanize.
231
266
  def humanize(capitalize: true, keep_id_suffix: false)
232
267
  ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
233
268
  end
@@ -237,6 +272,8 @@ class String
237
272
  # 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
238
273
  # 'w'.upcase_first # => "W"
239
274
  # ''.upcase_first # => ""
275
+ #
276
+ # See ActiveSupport::Inflector.upcase_first.
240
277
  def upcase_first
241
278
  ActiveSupport::Inflector.upcase_first(self)
242
279
  end
@@ -248,6 +285,8 @@ class String
248
285
  # 'Message'.foreign_key # => "message_id"
249
286
  # 'Message'.foreign_key(false) # => "messageid"
250
287
  # 'Admin::Post'.foreign_key # => "post_id"
288
+ #
289
+ # See ActiveSupport::Inflector.foreign_key.
251
290
  def foreign_key(separate_class_name_and_id_with_underscore = true)
252
291
  ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
253
292
  end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/string_inquirer"
4
+ require "active_support/environment_inquirer"
4
5
 
5
6
  class String
6
- # Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
7
+ # Wraps the current string in the ActiveSupport::StringInquirer class,
7
8
  # which gives you a prettier way to test for equality.
8
9
  #
9
10
  # env = 'production'.inquiry
@@ -11,12 +11,13 @@ class String
11
11
  # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
12
12
  # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
13
13
  #
14
- # >> "lj".upcase
15
- # => "lj"
16
14
  # >> "lj".mb_chars.upcase.to_s
17
15
  # => "LJ"
18
16
  #
19
- # NOTE: An above example is useful for pre Ruby 2.4. Ruby 2.4 supports Unicode case mappings.
17
+ # NOTE: Ruby 2.4 and later support native Unicode case mappings:
18
+ #
19
+ # >> "lj".upcase
20
+ # => "LJ"
20
21
  #
21
22
  # == Method chaining
22
23
  #
@@ -46,9 +47,9 @@ class String
46
47
  # iso_str.is_utf8? # => false
47
48
  def is_utf8?
48
49
  case encoding
49
- when Encoding::UTF_8
50
+ when Encoding::UTF_8, Encoding::US_ASCII
50
51
  valid_encoding?
51
- when Encoding::ASCII_8BIT, Encoding::US_ASCII
52
+ when Encoding::ASCII_8BIT
52
53
  dup.force_encoding(Encoding::UTF_8).valid_encoding?
53
54
  else
54
55
  false