activesupport 5.2.7.1 → 6.1.4.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -434
  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 +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +34 -34
  10. data/lib/active_support/cache/mem_cache_store.rb +39 -24
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +72 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +148 -78
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +70 -3
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/enumerable.rb +171 -75
  37. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +2 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  41. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  42. data/lib/active_support/core_ext/hash.rb +1 -2
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/marshal.rb +2 -0
  47. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  48. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  49. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  50. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  51. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  52. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  53. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  54. data/lib/active_support/core_ext/module.rb +0 -1
  55. data/lib/active_support/core_ext/name_error.rb +29 -2
  56. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  57. data/lib/active_support/core_ext/numeric.rb +0 -1
  58. data/lib/active_support/core_ext/object/blank.rb +1 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  61. data/lib/active_support/core_ext/object/json.rb +14 -2
  62. data/lib/active_support/core_ext/object/try.rb +17 -7
  63. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  64. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  65. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  66. data/lib/active_support/core_ext/range/each.rb +0 -1
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  68. data/lib/active_support/core_ext/regexp.rb +8 -5
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +5 -16
  71. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  72. data/lib/active_support/core_ext/string/filters.rb +42 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  74. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  75. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  76. data/lib/active_support/core_ext/string/output_safety.rb +70 -41
  77. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  78. data/lib/active_support/core_ext/string/strip.rb +3 -1
  79. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  80. data/lib/active_support/core_ext/symbol.rb +3 -0
  81. data/lib/active_support/core_ext/time/calculations.rb +51 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  83. data/lib/active_support/core_ext/uri.rb +6 -1
  84. data/lib/active_support/core_ext.rb +1 -1
  85. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  86. data/lib/active_support/current_attributes.rb +16 -2
  87. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/deprecation/behaviors.rb +16 -3
  90. data/lib/active_support/deprecation/disallowed.rb +56 -0
  91. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  92. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  94. data/lib/active_support/deprecation/reporting.rb +50 -7
  95. data/lib/active_support/deprecation.rb +6 -1
  96. data/lib/active_support/descendants_tracker.rb +59 -9
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  98. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  99. data/lib/active_support/duration.rb +78 -30
  100. data/lib/active_support/encrypted_configuration.rb +0 -4
  101. data/lib/active_support/encrypted_file.rb +22 -4
  102. data/lib/active_support/environment_inquirer.rb +20 -0
  103. data/lib/active_support/evented_file_update_checker.rb +82 -117
  104. data/lib/active_support/execution_wrapper.rb +2 -1
  105. data/lib/active_support/file_update_checker.rb +0 -1
  106. data/lib/active_support/fork_tracker.rb +64 -0
  107. data/lib/active_support/gem_version.rb +4 -4
  108. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  109. data/lib/active_support/i18n.rb +1 -0
  110. data/lib/active_support/i18n_railtie.rb +15 -8
  111. data/lib/active_support/inflector/inflections.rb +2 -7
  112. data/lib/active_support/inflector/methods.rb +49 -58
  113. data/lib/active_support/inflector/transliterate.rb +47 -18
  114. data/lib/active_support/json/decoding.rb +25 -26
  115. data/lib/active_support/json/encoding.rb +11 -3
  116. data/lib/active_support/key_generator.rb +1 -33
  117. data/lib/active_support/lazy_load_hooks.rb +5 -2
  118. data/lib/active_support/locale/en.rb +33 -0
  119. data/lib/active_support/locale/en.yml +7 -3
  120. data/lib/active_support/log_subscriber.rb +39 -9
  121. data/lib/active_support/logger.rb +2 -17
  122. data/lib/active_support/logger_silence.rb +11 -19
  123. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  124. data/lib/active_support/message_encryptor.rb +8 -13
  125. data/lib/active_support/message_verifier.rb +10 -10
  126. data/lib/active_support/messages/metadata.rb +11 -2
  127. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  128. data/lib/active_support/messages/rotator.rb +10 -9
  129. data/lib/active_support/multibyte/chars.rb +10 -68
  130. data/lib/active_support/multibyte/unicode.rb +15 -327
  131. data/lib/active_support/notifications/fanout.rb +116 -16
  132. data/lib/active_support/notifications/instrumenter.rb +71 -9
  133. data/lib/active_support/notifications.rb +72 -8
  134. data/lib/active_support/number_helper/number_converter.rb +5 -6
  135. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  136. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  137. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  138. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  142. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  143. data/lib/active_support/number_helper.rb +38 -12
  144. data/lib/active_support/option_merger.rb +22 -3
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +13 -3
  147. data/lib/active_support/parameter_filter.rb +133 -0
  148. data/lib/active_support/per_thread_registry.rb +1 -1
  149. data/lib/active_support/rails.rb +1 -10
  150. data/lib/active_support/railtie.rb +23 -1
  151. data/lib/active_support/reloader.rb +4 -5
  152. data/lib/active_support/rescuable.rb +4 -4
  153. data/lib/active_support/secure_compare_rotator.rb +51 -0
  154. data/lib/active_support/security_utils.rb +19 -12
  155. data/lib/active_support/string_inquirer.rb +4 -3
  156. data/lib/active_support/subscriber.rb +72 -28
  157. data/lib/active_support/tagged_logging.rb +42 -8
  158. data/lib/active_support/test_case.rb +91 -0
  159. data/lib/active_support/testing/assertions.rb +30 -9
  160. data/lib/active_support/testing/deprecation.rb +0 -1
  161. data/lib/active_support/testing/file_fixtures.rb +2 -0
  162. data/lib/active_support/testing/isolation.rb +2 -2
  163. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  164. data/lib/active_support/testing/parallelization/server.rb +78 -0
  165. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  166. data/lib/active_support/testing/parallelization.rb +51 -0
  167. data/lib/active_support/testing/stream.rb +1 -2
  168. data/lib/active_support/testing/time_helpers.rb +47 -12
  169. data/lib/active_support/time_with_zone.rb +81 -47
  170. data/lib/active_support/values/time_zone.rb +32 -17
  171. data/lib/active_support/xml_mini/jdom.rb +2 -3
  172. data/lib/active_support/xml_mini/libxml.rb +2 -2
  173. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  174. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  176. data/lib/active_support/xml_mini/rexml.rb +10 -3
  177. data/lib/active_support/xml_mini.rb +2 -10
  178. data/lib/active_support.rb +14 -1
  179. metadata +55 -29
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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
@@ -15,7 +14,7 @@ module ActiveSupport
15
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
16
15
  # # => true
17
16
  def self.logger_outputs_to?(logger, *sources)
18
- logdev = logger.instance_variable_get("@logdev")
17
+ logdev = logger.instance_variable_get(:@logdev)
19
18
  logger_source = logdev.dev if logdev.respond_to?(:dev)
20
19
  sources.any? { |source| source == logger_source }
21
20
  end
@@ -78,23 +77,9 @@ module ActiveSupport
78
77
  end
79
78
  end
80
79
 
81
- def initialize(*args)
80
+ def initialize(*args, **kwargs)
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,20 @@
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
- module LoggerSilence
8
- extend ActiveSupport::Concern
7
+ module ActiveSupport
8
+ module LoggerSilence
9
+ extend ActiveSupport::Concern
9
10
 
10
- included do
11
- cattr_accessor :silencer, default: true
12
- end
13
-
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
11
+ included do
12
+ cattr_accessor :silencer, default: true
13
+ include ActiveSupport::LoggerThreadSafeLevel
14
+ end
20
15
 
21
- yield self
22
- ensure
23
- self.local_level = old_local_level
24
- end
25
- else
26
- yield self
16
+ # Silences the logger for the duration of the block.
17
+ def silence(severity = Logger::ERROR)
18
+ silencer ? log_at(severity) { yield self } : yield(self)
27
19
  end
28
20
  end
29
21
  end
@@ -1,14 +1,24 @@
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"
4
6
  require "fiber"
5
7
 
6
8
  module ActiveSupport
7
9
  module LoggerThreadSafeLevel # :nodoc:
8
10
  extend ActiveSupport::Concern
9
11
 
10
- def after_initialize
11
- @local_levels = Concurrent::Map.new(initial_capacity: 2)
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
12
22
  end
13
23
 
14
24
  def local_log_id
@@ -16,19 +26,53 @@ module ActiveSupport
16
26
  end
17
27
 
18
28
  def local_level
19
- @local_levels[local_log_id]
29
+ self.class.local_levels[local_log_id]
20
30
  end
21
31
 
22
32
  def local_level=(level)
23
- if level
24
- @local_levels[local_log_id] = level
33
+ case level
34
+ when Integer
35
+ self.class.local_levels[local_log_id] = level
36
+ when Symbol
37
+ self.class.local_levels[local_log_id] = Logger::Severity.const_get(level.to_s.upcase)
38
+ when nil
39
+ self.class.local_levels.delete(local_log_id)
25
40
  else
26
- @local_levels.delete(local_log_id)
41
+ raise ArgumentError, "Invalid log level: #{level.inspect}"
27
42
  end
28
43
  end
29
44
 
30
45
  def level
31
46
  local_level || super
32
47
  end
48
+
49
+ # Change the thread-local level for the duration of the given block.
50
+ def log_at(level)
51
+ old_local_level, self.local_level = local_level, level
52
+ yield
53
+ ensure
54
+ self.local_level = old_local_level
55
+ end
56
+
57
+ # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
58
+ # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
59
+ def add(severity, message = nil, progname = nil, &block) #:nodoc:
60
+ severity ||= UNKNOWN
61
+ progname ||= @progname
62
+
63
+ return true if @logdev.nil? || severity < level
64
+
65
+ if message.nil?
66
+ if block_given?
67
+ message = yield
68
+ else
69
+ message = progname
70
+ progname = @progname
71
+ end
72
+ end
73
+
74
+ @logdev.write \
75
+ format_message(format_severity(severity), Time.now, progname, message)
76
+ end
33
77
  end
34
78
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "openssl"
4
4
  require "base64"
5
- require "active_support/core_ext/array/extract_options"
6
5
  require "active_support/core_ext/module/attribute_accessors"
7
6
  require "active_support/message_verifier"
8
7
  require "active_support/messages/metadata"
@@ -53,7 +52,7 @@ module ActiveSupport
53
52
  # crypt.encrypt_and_sign(parcel, expires_in: 1.month)
54
53
  # crypt.encrypt_and_sign(doowad, expires_at: Time.now.end_of_year)
55
54
  #
56
- # Then the messages can be verified and returned upto the expire time.
55
+ # Then the messages can be verified and returned up to the expire time.
57
56
  # Thereafter, verifying returns +nil+.
58
57
  #
59
58
  # === Rotating keys
@@ -134,15 +133,13 @@ module ActiveSupport
134
133
  # * <tt>:digest</tt> - String of digest to use for signing. Default is
135
134
  # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
136
135
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
137
- def initialize(secret, *signature_key_or_options)
138
- options = signature_key_or_options.extract_options!
139
- sign_secret = signature_key_or_options.first
136
+ def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer: nil)
140
137
  @secret = secret
141
138
  @sign_secret = sign_secret
142
- @cipher = options[:cipher] || self.class.default_cipher
143
- @digest = options[:digest] || "SHA1" unless aead_mode?
139
+ @cipher = cipher || self.class.default_cipher
140
+ @digest = digest || "SHA1" unless aead_mode?
144
141
  @verifier = resolve_verifier
145
- @serializer = options[:serializer] || Marshal
142
+ @serializer = serializer || Marshal
146
143
  end
147
144
 
148
145
  # Encrypt and sign a message. We need to sign the message in order to avoid
@@ -172,7 +169,7 @@ module ActiveSupport
172
169
  iv = cipher.random_iv
173
170
  cipher.auth_data = "" if aead_mode?
174
171
 
175
- encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), metadata_options))
172
+ encrypted_data = cipher.update(Messages::Metadata.wrap(@serializer.dump(value), **metadata_options))
176
173
  encrypted_data << cipher.final
177
174
 
178
175
  blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
@@ -182,7 +179,7 @@ module ActiveSupport
182
179
 
183
180
  def _decrypt(encrypted_message, purpose)
184
181
  cipher = new_cipher
185
- encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }
182
+ encrypted_data, iv, auth_tag = encrypted_message.split("--").map { |v| ::Base64.strict_decode64(v) }
186
183
 
187
184
  # Currently the OpenSSL bindings do not raise an error if auth_tag is
188
185
  # truncated, which would allow an attacker to easily forge it. See
@@ -210,9 +207,7 @@ module ActiveSupport
210
207
  OpenSSL::Cipher.new(@cipher)
211
208
  end
212
209
 
213
- def verifier
214
- @verifier
215
- end
210
+ attr_reader :verifier
216
211
 
217
212
  def aead_mode?
218
213
  @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
  #
@@ -103,11 +103,11 @@ module ActiveSupport
103
103
 
104
104
  class InvalidSignature < StandardError; end
105
105
 
106
- def initialize(secret, options = {})
106
+ def initialize(secret, digest: nil, serializer: nil)
107
107
  raise ArgumentError, "Secret should not be nil." unless secret
108
108
  @secret = secret
109
- @digest = options[:digest] || "SHA1"
110
- @serializer = options[:serializer] || Marshal
109
+ @digest = digest || "SHA1"
110
+ @serializer = serializer || Marshal
111
111
  end
112
112
 
113
113
  # Checks if a signed message could have been generated by signing an object
@@ -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
@@ -172,14 +172,14 @@ module ActiveSupport
172
172
  #
173
173
  # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
174
174
  # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
175
- def verify(*args)
176
- verified(*args) || raise(InvalidSignature)
175
+ def verify(*args, **options)
176
+ verified(*args, **options) || raise(InvalidSignature)
177
177
  end
178
178
 
179
179
  # Generates a signed message for the provided value.
180
180
  #
181
- # The message is signed with the +MessageVerifier+'s secret. Without knowing
182
- # the secret, the original value cannot be extracted from the message.
181
+ # The message is signed with the +MessageVerifier+'s secret.
182
+ # Returns Base64-encoded message joined with the generated signature.
183
183
  #
184
184
  # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
185
185
  # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
@@ -6,7 +6,8 @@ module ActiveSupport
6
6
  module Messages #:nodoc:
7
7
  class Metadata #:nodoc:
8
8
  def initialize(message, expires_at = nil, purpose = nil)
9
- @message, @expires_at, @purpose = message, expires_at, purpose
9
+ @message, @purpose = message, purpose
10
+ @expires_at = expires_at.is_a?(String) ? parse_expires_at(expires_at) : expires_at
10
11
  end
11
12
 
12
13
  def as_json(options = {})
@@ -64,7 +65,15 @@ module ActiveSupport
64
65
  end
65
66
 
66
67
  def fresh?
67
- @expires_at.nil? || Time.now.utc < Time.iso8601(@expires_at)
68
+ @expires_at.nil? || Time.now.utc < @expires_at
69
+ end
70
+
71
+ def parse_expires_at(expires_at)
72
+ if ActiveSupport.use_standard_json_time_format
73
+ Time.iso8601(expires_at)
74
+ else
75
+ Time.parse(expires_at)
76
+ end
68
77
  end
69
78
  end
70
79
  end
@@ -9,7 +9,8 @@ module ActiveSupport
9
9
  @signed, @encrypted = [], []
10
10
  end
11
11
 
12
- def rotate(kind, *args)
12
+ def rotate(kind, *args, **options)
13
+ args << options unless options.empty?
13
14
  case kind
14
15
  when :signed
15
16
  @signed << args
@@ -3,11 +3,12 @@
3
3
  module ActiveSupport
4
4
  module Messages
5
5
  module Rotator # :nodoc:
6
- def initialize(*, **options)
7
- super
6
+ def initialize(*secrets, on_rotation: nil, **options)
7
+ super(*secrets, **options)
8
8
 
9
9
  @options = options
10
10
  @rotations = []
11
+ @on_rotation = on_rotation
11
12
  end
12
13
 
13
14
  def rotate(*secrets, **options)
@@ -17,28 +18,28 @@ module ActiveSupport
17
18
  module Encryptor
18
19
  include Rotator
19
20
 
20
- def decrypt_and_verify(*args, on_rotation: nil, **options)
21
+ def decrypt_and_verify(*args, on_rotation: @on_rotation, **options)
21
22
  super
22
23
  rescue MessageEncryptor::InvalidMessage, MessageVerifier::InvalidSignature
23
- run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, options) } || raise
24
+ run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, **options) } || raise
24
25
  end
25
26
 
26
27
  private
27
28
  def build_rotation(secret = @secret, sign_secret = @sign_secret, options)
28
- self.class.new(secret, sign_secret, options)
29
+ self.class.new(secret, sign_secret, **options)
29
30
  end
30
31
  end
31
32
 
32
33
  module Verifier
33
34
  include Rotator
34
35
 
35
- def verified(*args, on_rotation: nil, **options)
36
- super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, options) }
36
+ def verified(*args, on_rotation: @on_rotation, **options)
37
+ super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, **options) }
37
38
  end
38
39
 
39
40
  private
40
41
  def build_rotation(secret = @secret, options)
41
- self.class.new(secret, options)
42
+ self.class.new(secret, **options)
42
43
  end
43
44
  end
44
45
 
@@ -46,7 +47,7 @@ module ActiveSupport
46
47
  def run_rotations(on_rotation)
47
48
  @rotations.find do |rotation|
48
49
  if message = yield(rotation) rescue next
49
- on_rotation.call if on_rotation
50
+ on_rotation&.call
50
51
  return message
51
52
  end
52
53
  end
@@ -3,8 +3,8 @@
3
3
  require "active_support/json"
4
4
  require "active_support/core_ext/string/access"
5
5
  require "active_support/core_ext/string/behavior"
6
+ require "active_support/core_ext/symbol/starts_ends_with"
6
7
  require "active_support/core_ext/module/delegation"
7
- require "active_support/core_ext/regexp"
8
8
 
9
9
  module ActiveSupport #:nodoc:
10
10
  module Multibyte #:nodoc:
@@ -18,7 +18,7 @@ module ActiveSupport #:nodoc:
18
18
  # through the +mb_chars+ method. Methods which would normally return a
19
19
  # String object now return a Chars object so methods can be chained.
20
20
  #
21
- # 'The Perfect String '.mb_chars.downcase.strip.normalize
21
+ # 'The Perfect String '.mb_chars.downcase.strip
22
22
  # # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
23
23
  #
24
24
  # Chars objects are perfectly interchangeable with String objects as long as
@@ -49,7 +49,7 @@ module ActiveSupport #:nodoc:
49
49
  alias to_s wrapped_string
50
50
  alias to_str wrapped_string
51
51
 
52
- delegate :<=>, :=~, :acts_like_string?, to: :wrapped_string
52
+ delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
53
53
 
54
54
  # Creates a new Chars instance by wrapping _string_.
55
55
  def initialize(string)
@@ -60,7 +60,7 @@ module ActiveSupport #:nodoc:
60
60
  # Forward all undefined methods to the wrapped string.
61
61
  def method_missing(method, *args, &block)
62
62
  result = @wrapped_string.__send__(method, *args, &block)
63
- if /!$/.match?(method)
63
+ if method.end_with?("!")
64
64
  self if result
65
65
  else
66
66
  result.kind_of?(String) ? chars(result) : result
@@ -74,12 +74,6 @@ module ActiveSupport #:nodoc:
74
74
  @wrapped_string.respond_to?(method, include_private)
75
75
  end
76
76
 
77
- # Returns +true+ when the proxy class can handle the string. Returns
78
- # +false+ otherwise.
79
- def self.consumes?(string)
80
- string.encoding == Encoding::UTF_8
81
- end
82
-
83
77
  # Works just like <tt>String#split</tt>, with the exception that the items
84
78
  # in the resulting list are Chars instances instead of String. This makes
85
79
  # chaining methods easier.
@@ -109,7 +103,7 @@ module ActiveSupport #:nodoc:
109
103
  #
110
104
  # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
111
105
  def reverse
112
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack("U*"))
106
+ chars(@wrapped_string.scan(/\X/).reverse.join)
113
107
  end
114
108
 
115
109
  # Limits the byte size of the string to a number of bytes without breaking
@@ -118,35 +112,7 @@ module ActiveSupport #:nodoc:
118
112
  #
119
113
  # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
120
114
  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
115
+ chars(@wrapped_string.truncate_bytes(limit, omission: nil))
150
116
  end
151
117
 
152
118
  # Capitalizes the first letter of every word, when possible.
@@ -154,21 +120,10 @@ module ActiveSupport #:nodoc:
154
120
  # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
155
121
  # "日本語".mb_chars.titleize.to_s # => "日本語"
156
122
  def titleize
157
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1) })
123
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
158
124
  end
159
125
  alias_method :titlecase, :titleize
160
126
 
161
- # Returns the KC normalization of the string by default. NFKC is
162
- # considered the best normalization form for passing strings to databases
163
- # and validations.
164
- #
165
- # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
166
- # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
167
- # ActiveSupport::Multibyte::Unicode.default_normalization_form
168
- def normalize(form = nil)
169
- chars(Unicode.normalize(@wrapped_string, form))
170
- end
171
-
172
127
  # Performs canonical decomposition on all the characters.
173
128
  #
174
129
  # 'é'.length # => 2
@@ -190,7 +145,7 @@ module ActiveSupport #:nodoc:
190
145
  # 'क्षि'.mb_chars.length # => 4
191
146
  # 'क्षि'.mb_chars.grapheme_length # => 3
192
147
  def grapheme_length
193
- Unicode.unpack_graphemes(@wrapped_string).length
148
+ @wrapped_string.scan(/\X/).length
194
149
  end
195
150
 
196
151
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -206,27 +161,14 @@ module ActiveSupport #:nodoc:
206
161
  to_s.as_json(options)
207
162
  end
208
163
 
209
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
164
+ %w(reverse tidy_bytes).each do |method|
210
165
  define_method("#{method}!") do |*args|
211
- @wrapped_string = send(method, *args).to_s
166
+ @wrapped_string = public_send(method, *args).to_s
212
167
  self
213
168
  end
214
169
  end
215
170
 
216
171
  private
217
-
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
172
  def chars(string)
231
173
  self.class.new(string)
232
174
  end