activesupport 5.2.1.1 → 6.0.1

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +416 -351
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -2
  5. data/lib/active_support.rb +2 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +28 -1
  8. data/lib/active_support/cache.rb +45 -23
  9. data/lib/active_support/cache/file_store.rb +22 -22
  10. data/lib/active_support/cache/mem_cache_store.rb +5 -0
  11. data/lib/active_support/cache/memory_store.rb +9 -2
  12. data/lib/active_support/cache/null_store.rb +5 -0
  13. data/lib/active_support/cache/redis_cache_store.rb +37 -10
  14. data/lib/active_support/callbacks.rb +16 -5
  15. data/lib/active_support/concern.rb +31 -4
  16. data/lib/active_support/configurable.rb +7 -11
  17. data/lib/active_support/core_ext/array.rb +1 -1
  18. data/lib/active_support/core_ext/array/access.rb +18 -6
  19. data/lib/active_support/core_ext/array/extract.rb +21 -0
  20. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  21. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  22. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  23. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  24. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  25. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  26. data/lib/active_support/core_ext/digest.rb +3 -0
  27. data/lib/active_support/core_ext/enumerable.rb +97 -73
  28. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  29. data/lib/active_support/core_ext/hash.rb +1 -2
  30. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  31. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  32. data/lib/active_support/core_ext/hash/except.rb +1 -1
  33. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  34. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  35. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  36. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  37. data/lib/active_support/core_ext/kernel.rb +0 -1
  38. data/lib/active_support/core_ext/load_error.rb +1 -1
  39. data/lib/active_support/core_ext/module.rb +0 -1
  40. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  41. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  42. data/lib/active_support/core_ext/module/delegation.rb +33 -7
  43. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  44. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  45. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  46. data/lib/active_support/core_ext/numeric.rb +0 -1
  47. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  48. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  49. data/lib/active_support/core_ext/object/blank.rb +1 -2
  50. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  51. data/lib/active_support/core_ext/object/json.rb +1 -0
  52. data/lib/active_support/core_ext/object/try.rb +15 -7
  53. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  54. data/lib/active_support/core_ext/range.rb +1 -1
  55. data/lib/active_support/core_ext/range/compare_range.rb +76 -0
  56. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  57. data/lib/active_support/core_ext/range/include_range.rb +6 -22
  58. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  59. data/lib/active_support/core_ext/regexp.rb +0 -4
  60. data/lib/active_support/core_ext/securerandom.rb +23 -3
  61. data/lib/active_support/core_ext/string/access.rb +8 -0
  62. data/lib/active_support/core_ext/string/filters.rb +42 -1
  63. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  64. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  65. data/lib/active_support/core_ext/string/output_safety.rb +63 -5
  66. data/lib/active_support/core_ext/string/strip.rb +3 -1
  67. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  68. data/lib/active_support/core_ext/uri.rb +2 -1
  69. data/lib/active_support/current_attributes.rb +8 -0
  70. data/lib/active_support/dependencies.rb +74 -17
  71. data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
  72. data/lib/active_support/deprecation.rb +1 -1
  73. data/lib/active_support/deprecation/behaviors.rb +1 -1
  74. data/lib/active_support/deprecation/method_wrappers.rb +13 -12
  75. data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
  76. data/lib/active_support/descendants_tracker.rb +56 -9
  77. data/lib/active_support/duration.rb +6 -5
  78. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  79. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  80. data/lib/active_support/encrypted_configuration.rb +1 -5
  81. data/lib/active_support/encrypted_file.rb +2 -1
  82. data/lib/active_support/evented_file_update_checker.rb +39 -9
  83. data/lib/active_support/execution_wrapper.rb +1 -0
  84. data/lib/active_support/gem_version.rb +3 -3
  85. data/lib/active_support/hash_with_indifferent_access.rb +36 -18
  86. data/lib/active_support/i18n.rb +1 -0
  87. data/lib/active_support/i18n_railtie.rb +14 -2
  88. data/lib/active_support/inflector/inflections.rb +1 -4
  89. data/lib/active_support/inflector/methods.rb +17 -27
  90. data/lib/active_support/inflector/transliterate.rb +47 -18
  91. data/lib/active_support/json/decoding.rb +23 -23
  92. data/lib/active_support/json/encoding.rb +6 -2
  93. data/lib/active_support/key_generator.rb +0 -32
  94. data/lib/active_support/lazy_load_hooks.rb +5 -1
  95. data/lib/active_support/locale/en.rb +31 -0
  96. data/lib/active_support/log_subscriber.rb +31 -8
  97. data/lib/active_support/logger.rb +0 -15
  98. data/lib/active_support/logger_silence.rb +28 -12
  99. data/lib/active_support/logger_thread_safe_level.rb +28 -5
  100. data/lib/active_support/message_encryptor.rb +3 -5
  101. data/lib/active_support/message_verifier.rb +3 -3
  102. data/lib/active_support/multibyte/chars.rb +29 -48
  103. data/lib/active_support/multibyte/unicode.rb +44 -281
  104. data/lib/active_support/notifications.rb +41 -4
  105. data/lib/active_support/notifications/fanout.rb +100 -15
  106. data/lib/active_support/notifications/instrumenter.rb +80 -8
  107. data/lib/active_support/number_helper.rb +7 -0
  108. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  109. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  110. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  111. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  112. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  113. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  114. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  115. data/lib/active_support/ordered_hash.rb +1 -1
  116. data/lib/active_support/ordered_options.rb +1 -1
  117. data/lib/active_support/parameter_filter.rb +129 -0
  118. data/lib/active_support/rails.rb +0 -6
  119. data/lib/active_support/reloader.rb +4 -5
  120. data/lib/active_support/security_utils.rb +1 -1
  121. data/lib/active_support/subscriber.rb +65 -22
  122. data/lib/active_support/tagged_logging.rb +13 -4
  123. data/lib/active_support/test_case.rb +91 -0
  124. data/lib/active_support/testing/assertions.rb +15 -1
  125. data/lib/active_support/testing/deprecation.rb +0 -1
  126. data/lib/active_support/testing/file_fixtures.rb +2 -0
  127. data/lib/active_support/testing/isolation.rb +2 -2
  128. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  129. data/lib/active_support/testing/parallelization.rb +128 -0
  130. data/lib/active_support/testing/stream.rb +1 -1
  131. data/lib/active_support/testing/time_helpers.rb +7 -7
  132. data/lib/active_support/time_with_zone.rb +15 -5
  133. data/lib/active_support/values/time_zone.rb +12 -7
  134. data/lib/active_support/xml_mini.rb +2 -9
  135. data/lib/active_support/xml_mini/jdom.rb +2 -2
  136. data/lib/active_support/xml_mini/libxml.rb +2 -2
  137. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  138. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  139. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  140. data/lib/active_support/xml_mini/rexml.rb +2 -2
  141. metadata +33 -10
  142. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  143. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -45,32 +45,32 @@ module ActiveSupport
45
45
 
46
46
  private
47
47
 
48
- def convert_dates_from(data)
49
- case data
50
- when nil
51
- nil
52
- when DATE_REGEX
53
- begin
54
- Date.parse(data)
55
- rescue ArgumentError
48
+ def convert_dates_from(data)
49
+ case data
50
+ when nil
51
+ nil
52
+ when DATE_REGEX
53
+ begin
54
+ Date.parse(data)
55
+ rescue ArgumentError
56
+ data
57
+ end
58
+ when DATETIME_REGEX
59
+ begin
60
+ Time.zone.parse(data)
61
+ rescue ArgumentError
62
+ data
63
+ end
64
+ when Array
65
+ data.map! { |d| convert_dates_from(d) }
66
+ when Hash
67
+ data.each do |key, value|
68
+ data[key] = convert_dates_from(value)
69
+ end
70
+ else
56
71
  data
57
72
  end
58
- when DATETIME_REGEX
59
- begin
60
- Time.zone.parse(data)
61
- rescue ArgumentError
62
- data
63
- end
64
- when Array
65
- data.map! { |d| convert_dates_from(d) }
66
- when Hash
67
- data.each do |key, value|
68
- data[key] = convert_dates_from(value)
69
- end
70
- else
71
- data
72
73
  end
73
- end
74
74
  end
75
75
  end
76
76
  end
@@ -54,9 +54,13 @@ module ActiveSupport
54
54
  class EscapedString < String #:nodoc:
55
55
  def to_json(*)
56
56
  if Encoding.escape_html_entities_in_json
57
- super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
57
+ s = super
58
+ s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
+ s
58
60
  else
59
- super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
61
+ s = super
62
+ s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
+ s
60
64
  end
61
65
  end
62
66
 
@@ -38,36 +38,4 @@ module ActiveSupport
38
38
  @cache_keys[args.join] ||= @key_generator.generate_key(*args)
39
39
  end
40
40
  end
41
-
42
- class LegacyKeyGenerator # :nodoc:
43
- SECRET_MIN_LENGTH = 30 # Characters
44
-
45
- def initialize(secret)
46
- ensure_secret_secure(secret)
47
- @secret = secret
48
- end
49
-
50
- def generate_key(salt)
51
- @secret
52
- end
53
-
54
- private
55
-
56
- # To prevent users from using something insecure like "Password" we make sure that the
57
- # secret they've provided is at least 30 characters in length.
58
- def ensure_secret_secure(secret)
59
- if secret.blank?
60
- raise ArgumentError, "A secret is required to generate an integrity hash " \
61
- "for cookie session data. Set a secret_key_base of at least " \
62
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
63
- end
64
-
65
- if secret.length < SECRET_MIN_LENGTH
66
- raise ArgumentError, "Secret should be something secure, " \
67
- "like \"#{SecureRandom.hex(16)}\". The value you " \
68
- "provided, \"#{secret}\", is shorter than the minimum length " \
69
- "of #{SECRET_MIN_LENGTH} characters."
70
- end
71
- end
72
- end
73
41
  end
@@ -68,7 +68,11 @@ module ActiveSupport
68
68
  if options[:yield]
69
69
  block.call(base)
70
70
  else
71
- base.instance_eval(&block)
71
+ if base.is_a?(Module)
72
+ base.class_eval(&block)
73
+ else
74
+ base.instance_eval(&block)
75
+ end
72
76
  end
73
77
  end
74
78
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ en: {
5
+ number: {
6
+ nth: {
7
+ ordinals: lambda do |_key, number:, **_options|
8
+ case number
9
+ when 1; "st"
10
+ when 2; "nd"
11
+ when 3; "rd"
12
+ when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; "th"
13
+ else
14
+ num_modulo = number.to_i.abs % 100
15
+ num_modulo %= 10 if num_modulo > 13
16
+ case num_modulo
17
+ when 1; "st"
18
+ when 2; "nd"
19
+ when 3; "rd"
20
+ else "th"
21
+ end
22
+ end
23
+ end,
24
+
25
+ ordinalized: lambda do |_key, number:, **_options|
26
+ "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
27
+ end
28
+ }
29
+ }
30
+ }
31
+ }
@@ -5,8 +5,8 @@ require "active_support/core_ext/class/attribute"
5
5
  require "active_support/subscriber"
6
6
 
7
7
  module ActiveSupport
8
- # ActiveSupport::LogSubscriber is an object set to consume
9
- # ActiveSupport::Notifications with the sole purpose of logging them.
8
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
+ # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
10
10
  # The log subscriber dispatches notifications to a registered object based
11
11
  # on its given namespace.
12
12
  #
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  # module ActiveRecord
17
17
  # class LogSubscriber < ActiveSupport::LogSubscriber
18
18
  # def sql(event)
19
- # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
19
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
20
  # end
21
21
  # end
22
22
  # end
@@ -29,13 +29,36 @@ module ActiveSupport
29
29
  # subscriber, the line above should be called after your
30
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
31
  #
32
- # After configured, whenever a "sql.active_record" notification is published,
33
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
34
- # the sql method.
32
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
33
+ # it will properly dispatch the event
34
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
35
+ #
36
+ # Being an <tt>ActiveSupport::Notifications</tt> consumer,
37
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
38
+ # instrumented code raises an exception. It is common to log a different
39
+ # message in case of an error, and this can be achieved by extending
40
+ # the previous example:
41
+ #
42
+ # module ActiveRecord
43
+ # class LogSubscriber < ActiveSupport::LogSubscriber
44
+ # def sql(event)
45
+ # exception = event.payload[:exception]
46
+ #
47
+ # if exception
48
+ # exception_object = event.payload[:exception_object]
49
+ #
50
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
51
+ # "(#{exception_object.backtrace.first})"
52
+ # else
53
+ # # standard logger code
54
+ # end
55
+ # end
56
+ # end
57
+ # end
35
58
  #
36
59
  # Log subscriber also has some helpers to deal with logging and automatically
37
- # flushes all logs when the request finishes (via action_dispatch.callback
38
- # notification) in a Rails environment.
60
+ # flushes all logs when the request finishes
61
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
39
62
  class LogSubscriber < Subscriber
40
63
  # Embed in a String to clear all previous ANSI sequences.
41
64
  CLEAR = "\e[0m"
@@ -6,7 +6,6 @@ require "logger"
6
6
 
7
7
  module ActiveSupport
8
8
  class Logger < ::Logger
9
- include ActiveSupport::LoggerThreadSafeLevel
10
9
  include LoggerSilence
11
10
 
12
11
  # Returns true if the logger destination matches one of the sources
@@ -81,20 +80,6 @@ module ActiveSupport
81
80
  def initialize(*args)
82
81
  super
83
82
  @formatter = SimpleFormatter.new
84
- after_initialize if respond_to? :after_initialize
85
- end
86
-
87
- def add(severity, message = nil, progname = nil, &block)
88
- return true if @logdev.nil? || (severity || UNKNOWN) < level
89
- super
90
- end
91
-
92
- Logger::Severity.constants.each do |severity|
93
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
94
- def #{severity.downcase}? # def debug?
95
- Logger::#{severity} >= level # DEBUG >= level
96
- end # end
97
- EOT
98
83
  end
99
84
 
100
85
  # Simple formatter which only displays the message.
@@ -2,28 +2,44 @@
2
2
 
3
3
  require "active_support/concern"
4
4
  require "active_support/core_ext/module/attribute_accessors"
5
- require "concurrent"
5
+ require "active_support/logger_thread_safe_level"
6
6
 
7
7
  module LoggerSilence
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  included do
11
- cattr_accessor :silencer, default: true
11
+ ActiveSupport::Deprecation.warn(
12
+ "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \
13
+ "Please use `ActiveSupport::LoggerSilence` instead"
14
+ )
15
+
16
+ include ActiveSupport::LoggerSilence
12
17
  end
18
+ end
19
+
20
+ module ActiveSupport
21
+ module LoggerSilence
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ cattr_accessor :silencer, default: true
26
+ include ActiveSupport::LoggerThreadSafeLevel
27
+ end
13
28
 
14
- # Silences the logger for the duration of the block.
15
- def silence(temporary_level = Logger::ERROR)
16
- if silencer
17
- begin
18
- old_local_level = local_level
19
- self.local_level = temporary_level
29
+ # Silences the logger for the duration of the block.
30
+ def silence(temporary_level = Logger::ERROR)
31
+ if silencer
32
+ begin
33
+ old_local_level = local_level
34
+ self.local_level = temporary_level
20
35
 
36
+ yield self
37
+ ensure
38
+ self.local_level = old_local_level
39
+ end
40
+ else
21
41
  yield self
22
- ensure
23
- self.local_level = old_local_level
24
42
  end
25
- else
26
- yield self
27
43
  end
28
44
  end
29
45
  end
@@ -1,33 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "concurrent"
6
+ require "fiber"
4
7
 
5
8
  module ActiveSupport
6
9
  module LoggerThreadSafeLevel # :nodoc:
7
10
  extend ActiveSupport::Concern
8
11
 
12
+ included do
13
+ cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
14
+ end
15
+
16
+ Logger::Severity.constants.each do |severity|
17
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
18
+ def #{severity.downcase}? # def debug?
19
+ Logger::#{severity} >= level # DEBUG >= level
20
+ end # end
21
+ EOT
22
+ end
23
+
9
24
  def after_initialize
10
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
25
+ ActiveSupport::Deprecation.warn(
26
+ "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \
27
+ "Rails 6.1."
28
+ )
11
29
  end
12
30
 
13
31
  def local_log_id
14
- Thread.current.__id__
32
+ Fiber.current.__id__
15
33
  end
16
34
 
17
35
  def local_level
18
- @local_levels[local_log_id]
36
+ self.class.local_levels[local_log_id]
19
37
  end
20
38
 
21
39
  def local_level=(level)
22
40
  if level
23
- @local_levels[local_log_id] = level
41
+ self.class.local_levels[local_log_id] = level
24
42
  else
25
- @local_levels.delete(local_log_id)
43
+ self.class.local_levels.delete(local_log_id)
26
44
  end
27
45
  end
28
46
 
29
47
  def level
30
48
  local_level || super
31
49
  end
50
+
51
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
52
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
53
+ super
54
+ end
32
55
  end
33
56
  end
@@ -53,7 +53,7 @@ module ActiveSupport
53
53
  # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
54
54
  # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
55
55
  #
56
- # Then the messages can be verified and returned upto the expire time.
56
+ # Then the messages can be verified and returned up to the expire time.
57
57
  # Thereafter, verifying returns +nil+.
58
58
  #
59
59
  # === Rotating keys
@@ -182,7 +182,7 @@ module ActiveSupport
182
182
 
183
183
  def _decrypt(encrypted_message, purpose)
184
184
  cipher = new_cipher
185
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
185
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
186
186
 
187
187
  # Currently the OpenSSL bindings do not raise an error if auth_tag is
188
188
  # truncated, which would allow an attacker to easily forge it. See
@@ -210,9 +210,7 @@ module ActiveSupport
210
210
  OpenSSL::Cipher.new(@cipher)
211
211
  end
212
212
 
213
- def verifier
214
- @verifier
215
- end
213
+ attr_reader :verifier
216
214
 
217
215
  def aead_mode?
218
216
  @aead_mode ||= new_cipher.authenticated?
@@ -71,7 +71,7 @@ module ActiveSupport
71
71
  # @verifier.generate(parcel, expires_in: 1.month)
72
72
  # @verifier.generate(doowad, expires_at: Time.now.end_of_year)
73
73
  #
74
- # Then the messages can be verified and returned upto the expire time.
74
+ # Then the messages can be verified and returned up to the expire time.
75
75
  # Thereafter, the +verified+ method returns +nil+ while +verify+ raises
76
76
  # <tt>ActiveSupport::MessageVerifier::InvalidSignature</tt>.
77
77
  #
@@ -122,7 +122,7 @@ module ActiveSupport
122
122
  def valid_message?(signed_message)
123
123
  return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
124
124
 
125
- data, digest = signed_message.split("--".freeze)
125
+ data, digest = signed_message.split("--")
126
126
  data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
127
127
  end
128
128
 
@@ -150,7 +150,7 @@ module ActiveSupport
150
150
  def verified(signed_message, purpose: nil, **)
151
151
  if valid_message?(signed_message)
152
152
  begin
153
- data = signed_message.split("--".freeze)[0]
153
+ data = signed_message.split("--")[0]
154
154
  message = Messages::Metadata.verify(decode(data), purpose)
155
155
  @serializer.load(message) if message
156
156
  rescue ArgumentError => argument_error
@@ -4,7 +4,6 @@ require "active_support/json"
4
4
  require "active_support/core_ext/string/access"
5
5
  require "active_support/core_ext/string/behavior"
6
6
  require "active_support/core_ext/module/delegation"
7
- require "active_support/core_ext/regexp"
8
7
 
9
8
  module ActiveSupport #:nodoc:
10
9
  module Multibyte #:nodoc:
@@ -18,7 +17,7 @@ module ActiveSupport #:nodoc:
18
17
  # through the +mb_chars+ method. Methods which would normally return a
19
18
  # String object now return a Chars object so methods can be chained.
20
19
  #
21
- # 'The Perfect String '.mb_chars.downcase.strip.normalize
20
+ # 'The Perfect String '.mb_chars.downcase.strip
22
21
  # # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
23
22
  #
24
23
  # Chars objects are perfectly interchangeable with String objects as long as
@@ -77,6 +76,11 @@ module ActiveSupport #:nodoc:
77
76
  # Returns +true+ when the proxy class can handle the string. Returns
78
77
  # +false+ otherwise.
79
78
  def self.consumes?(string)
79
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
80
+ ActiveSupport::Multibyte::Chars.consumes? is deprecated and will be
81
+ removed from Rails 6.1. Use string.is_utf8? instead.
82
+ MSG
83
+
80
84
  string.encoding == Encoding::UTF_8
81
85
  end
82
86
 
@@ -109,7 +113,7 @@ module ActiveSupport #:nodoc:
109
113
  #
110
114
  # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
111
115
  def reverse
112
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
116
+ chars(@wrapped_string.scan(/\X/).reverse.join)
113
117
  end
114
118
 
115
119
  # Limits the byte size of the string to a number of bytes without breaking
@@ -118,35 +122,7 @@ module ActiveSupport #:nodoc:
118
122
  #
119
123
  # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
120
124
  def limit(limit)
121
- slice(0...translate_offset(limit))
122
- end
123
-
124
- # Converts characters in the string to uppercase.
125
- #
126
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
127
- def upcase
128
- chars Unicode.upcase(@wrapped_string)
129
- end
130
-
131
- # Converts characters in the string to lowercase.
132
- #
133
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
134
- def downcase
135
- chars Unicode.downcase(@wrapped_string)
136
- end
137
-
138
- # Converts characters in the string to the opposite case.
139
- #
140
- # 'El Cañón'.mb_chars.swapcase.to_s # => "eL cAÑÓN"
141
- def swapcase
142
- chars Unicode.swapcase(@wrapped_string)
143
- end
144
-
145
- # Converts the first character to uppercase and the remainder to lowercase.
146
- #
147
- # 'über'.mb_chars.capitalize.to_s # => "Über"
148
- def capitalize
149
- (slice(0) || chars("")).upcase + (slice(1..-1) || chars("")).downcase
125
+ truncate_bytes(limit, omission: nil)
150
126
  end
151
127
 
152
128
  # Capitalizes the first letter of every word, when possible.
@@ -154,7 +130,7 @@ module ActiveSupport #:nodoc:
154
130
  # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
155
131
  # "日本語".mb_chars.titleize.to_s # => "日本語"
156
132
  def titleize
157
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
133
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
158
134
  end
159
135
  alias_method :titlecase, :titleize
160
136
 
@@ -166,7 +142,24 @@ module ActiveSupport #:nodoc:
166
142
  # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
167
143
  # ActiveSupport::Multibyte::Unicode.default_normalization_form
168
144
  def normalize(form = nil)
169
- chars(Unicode.normalize(@wrapped_string, form))
145
+ form ||= Unicode.default_normalization_form
146
+
147
+ # See https://www.unicode.org/reports/tr15, Table 1
148
+ if alias_form = Unicode::NORMALIZATION_FORM_ALIASES[form]
149
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
150
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
151
+ removed from Rails 6.1. Use #unicode_normalize(:#{alias_form}) instead.
152
+ MSG
153
+
154
+ send(:unicode_normalize, alias_form)
155
+ else
156
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
157
+ ActiveSupport::Multibyte::Chars#normalize is deprecated and will be
158
+ removed from Rails 6.1. Use #unicode_normalize instead.
159
+ MSG
160
+
161
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
162
+ end
170
163
  end
171
164
 
172
165
  # Performs canonical decomposition on all the characters.
@@ -190,7 +183,7 @@ module ActiveSupport #:nodoc:
190
183
  # 'क्षि'.mb_chars.length # => 4
191
184
  # 'क्षि'.mb_chars.grapheme_length # => 3
192
185
  def grapheme_length
193
- Unicode.unpack_graphemes(@wrapped_string).length
186
+ @wrapped_string.scan(/\X/).length
194
187
  end
195
188
 
196
189
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -206,7 +199,7 @@ module ActiveSupport #:nodoc:
206
199
  to_s.as_json(options)
207
200
  end
208
201
 
209
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
202
+ %w(reverse tidy_bytes).each do |method|
210
203
  define_method("#{method}!") do |*args|
211
204
  @wrapped_string = send(method, *args).to_s
212
205
  self
@@ -215,18 +208,6 @@ module ActiveSupport #:nodoc:
215
208
 
216
209
  private
217
210
 
218
- def translate_offset(byte_offset)
219
- return nil if byte_offset.nil?
220
- return 0 if @wrapped_string == ""
221
-
222
- begin
223
- @wrapped_string.byteslice(0...byte_offset).unpack("U*").length
224
- rescue ArgumentError
225
- byte_offset -= 1
226
- retry
227
- end
228
- end
229
-
230
211
  def chars(string)
231
212
  self.class.new(string)
232
213
  end