activesupport 6.0.6 → 7.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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +224 -608
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +143 -38
  10. data/lib/active_support/cache/memory_store.rb +56 -28
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +62 -87
  13. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  14. data/lib/active_support/cache.rb +268 -77
  15. data/lib/active_support/callbacks.rb +226 -118
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array.rb +1 -0
  27. data/lib/active_support/core_ext/benchmark.rb +2 -2
  28. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  29. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -24
  31. data/lib/active_support/core_ext/date/blank.rb +1 -1
  32. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  33. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  34. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  35. data/lib/active_support/core_ext/date.rb +1 -0
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  38. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  40. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  41. data/lib/active_support/core_ext/date_time.rb +1 -0
  42. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  43. data/lib/active_support/core_ext/enumerable.rb +139 -15
  44. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  45. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  46. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  48. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  49. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  50. data/lib/active_support/core_ext/load_error.rb +1 -1
  51. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  52. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  53. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  54. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  55. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  56. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  57. data/lib/active_support/core_ext/name_error.rb +23 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  59. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  60. data/lib/active_support/core_ext/numeric.rb +1 -0
  61. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  62. data/lib/active_support/core_ext/object/blank.rb +2 -2
  63. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  64. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  65. data/lib/active_support/core_ext/object/json.rb +41 -25
  66. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  67. data/lib/active_support/core_ext/object/try.rb +20 -20
  68. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  69. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  70. data/lib/active_support/core_ext/pathname.rb +3 -0
  71. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  72. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  73. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  74. data/lib/active_support/core_ext/range/each.rb +1 -1
  75. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  76. data/lib/active_support/core_ext/range.rb +1 -1
  77. data/lib/active_support/core_ext/regexp.rb +8 -1
  78. data/lib/active_support/core_ext/string/access.rb +5 -24
  79. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  80. data/lib/active_support/core_ext/string/filters.rb +1 -1
  81. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  82. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  83. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  84. data/lib/active_support/core_ext/string/output_safety.rb +62 -67
  85. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  86. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  87. data/lib/active_support/core_ext/symbol.rb +3 -0
  88. data/lib/active_support/core_ext/time/calculations.rb +23 -5
  89. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  90. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  91. data/lib/active_support/core_ext/time/zones.rb +4 -19
  92. data/lib/active_support/core_ext/time.rb +1 -0
  93. data/lib/active_support/core_ext/uri.rb +3 -23
  94. data/lib/active_support/core_ext.rb +2 -1
  95. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  96. data/lib/active_support/current_attributes.rb +39 -16
  97. data/lib/active_support/dependencies/interlock.rb +10 -18
  98. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  99. data/lib/active_support/dependencies.rb +58 -769
  100. data/lib/active_support/deprecation/behaviors.rb +19 -3
  101. data/lib/active_support/deprecation/disallowed.rb +56 -0
  102. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  103. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  104. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  105. data/lib/active_support/deprecation/reporting.rb +50 -7
  106. data/lib/active_support/deprecation.rb +6 -1
  107. data/lib/active_support/descendants_tracker.rb +174 -64
  108. data/lib/active_support/digest.rb +5 -3
  109. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  110. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  111. data/lib/active_support/duration.rb +134 -55
  112. data/lib/active_support/encrypted_configuration.rb +11 -1
  113. data/lib/active_support/encrypted_file.rb +20 -3
  114. data/lib/active_support/environment_inquirer.rb +20 -0
  115. data/lib/active_support/error_reporter.rb +117 -0
  116. data/lib/active_support/evented_file_update_checker.rb +70 -134
  117. data/lib/active_support/execution_context/test_helper.rb +13 -0
  118. data/lib/active_support/execution_context.rb +53 -0
  119. data/lib/active_support/execution_wrapper.rb +41 -18
  120. data/lib/active_support/executor/test_helper.rb +7 -0
  121. data/lib/active_support/fork_tracker.rb +71 -0
  122. data/lib/active_support/gem_version.rb +2 -2
  123. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  124. data/lib/active_support/html_safe_translation.rb +43 -0
  125. data/lib/active_support/i18n.rb +1 -0
  126. data/lib/active_support/i18n_railtie.rb +14 -19
  127. data/lib/active_support/inflector/inflections.rb +24 -9
  128. data/lib/active_support/inflector/methods.rb +29 -49
  129. data/lib/active_support/inflector/transliterate.rb +4 -4
  130. data/lib/active_support/isolated_execution_state.rb +56 -0
  131. data/lib/active_support/json/decoding.rb +4 -4
  132. data/lib/active_support/json/encoding.rb +8 -4
  133. data/lib/active_support/key_generator.rb +19 -2
  134. data/lib/active_support/locale/en.yml +8 -4
  135. data/lib/active_support/log_subscriber.rb +21 -3
  136. data/lib/active_support/logger.rb +1 -1
  137. data/lib/active_support/logger_silence.rb +2 -26
  138. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  139. data/lib/active_support/message_encryptor.rb +12 -10
  140. data/lib/active_support/message_verifier.rb +50 -18
  141. data/lib/active_support/messages/metadata.rb +2 -2
  142. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  143. data/lib/active_support/messages/rotator.rb +6 -5
  144. data/lib/active_support/multibyte/chars.rb +13 -52
  145. data/lib/active_support/multibyte/unicode.rb +1 -87
  146. data/lib/active_support/multibyte.rb +1 -1
  147. data/lib/active_support/notifications/fanout.rb +110 -69
  148. data/lib/active_support/notifications/instrumenter.rb +37 -29
  149. data/lib/active_support/notifications.rb +47 -26
  150. data/lib/active_support/number_helper/number_converter.rb +2 -4
  151. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  152. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  153. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  155. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  156. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  157. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  158. data/lib/active_support/number_helper.rb +29 -16
  159. data/lib/active_support/option_merger.rb +9 -16
  160. data/lib/active_support/ordered_hash.rb +1 -1
  161. data/lib/active_support/ordered_options.rb +8 -2
  162. data/lib/active_support/parameter_filter.rb +21 -11
  163. data/lib/active_support/per_thread_registry.rb +6 -1
  164. data/lib/active_support/rails.rb +1 -4
  165. data/lib/active_support/railtie.rb +77 -5
  166. data/lib/active_support/reloader.rb +1 -1
  167. data/lib/active_support/rescuable.rb +6 -6
  168. data/lib/active_support/ruby_features.rb +7 -0
  169. data/lib/active_support/secure_compare_rotator.rb +51 -0
  170. data/lib/active_support/security_utils.rb +19 -12
  171. data/lib/active_support/string_inquirer.rb +2 -2
  172. data/lib/active_support/subscriber.rb +19 -25
  173. data/lib/active_support/tagged_logging.rb +31 -6
  174. data/lib/active_support/test_case.rb +9 -21
  175. data/lib/active_support/testing/assertions.rb +49 -12
  176. data/lib/active_support/testing/deprecation.rb +52 -1
  177. data/lib/active_support/testing/isolation.rb +2 -2
  178. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  179. data/lib/active_support/testing/parallelization/server.rb +82 -0
  180. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  181. data/lib/active_support/testing/parallelization.rb +16 -95
  182. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  183. data/lib/active_support/testing/stream.rb +3 -5
  184. data/lib/active_support/testing/tagged_logging.rb +1 -1
  185. data/lib/active_support/testing/time_helpers.rb +53 -5
  186. data/lib/active_support/time_with_zone.rb +120 -55
  187. data/lib/active_support/values/time_zone.rb +49 -18
  188. data/lib/active_support/xml_mini/jdom.rb +1 -1
  189. data/lib/active_support/xml_mini/libxml.rb +5 -5
  190. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  191. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  192. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  193. data/lib/active_support/xml_mini/rexml.rb +9 -2
  194. data/lib/active_support/xml_mini.rb +5 -4
  195. data/lib/active_support.rb +29 -1
  196. metadata +45 -45
  197. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  198. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  199. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  200. data/lib/active_support/core_ext/marshal.rb +0 -24
  201. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  202. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  203. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  204. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -9,18 +9,35 @@ module ActiveSupport
9
9
  # This lets Rails applications have a single secure secret, but avoid reusing that
10
10
  # key in multiple incompatible contexts.
11
11
  class KeyGenerator
12
+ class << self
13
+ def hash_digest_class=(klass)
14
+ if klass.kind_of?(Class) && klass < OpenSSL::Digest
15
+ @hash_digest_class = klass
16
+ else
17
+ raise ArgumentError, "#{klass} is expected to be an OpenSSL::Digest subclass"
18
+ end
19
+ end
20
+
21
+ def hash_digest_class
22
+ @hash_digest_class ||= OpenSSL::Digest::SHA1
23
+ end
24
+ end
25
+
12
26
  def initialize(secret, options = {})
13
27
  @secret = secret
14
28
  # The default iterations are higher than required for our key derivation uses
15
29
  # on the off chance someone uses this for password storage
16
30
  @iterations = options[:iterations] || 2**16
31
+ # Also allow configuration here so people can use this to build a rotation
32
+ # scheme when switching the digest class.
33
+ @hash_digest_class = options[:hash_digest_class] || self.class.hash_digest_class
17
34
  end
18
35
 
19
36
  # Returns a derived key suitable for use. The default key_size is chosen
20
37
  # to be compatible with the default settings of ActiveSupport::MessageVerifier.
21
38
  # i.e. OpenSSL::Digest::SHA1#block_length
22
39
  def generate_key(salt, key_size = 64)
23
- OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
40
+ OpenSSL::PKCS5.pbkdf2_hmac(@secret, salt, @iterations, key_size, @hash_digest_class.new)
24
41
  end
25
42
  end
26
43
 
@@ -35,7 +52,7 @@ module ActiveSupport
35
52
 
36
53
  # Returns a derived key suitable for use.
37
54
  def generate_key(*args)
38
- @cache_keys[args.join] ||= @key_generator.generate_key(*args)
55
+ @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args)
39
56
  end
40
57
  end
41
58
  end
@@ -44,22 +44,25 @@ en:
44
44
  delimiter: ","
45
45
  # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
46
46
  precision: 3
47
+ # Determine how rounding is performed (see BigDecimal::mode)
48
+ round_mode: default
47
49
  # If set to true, precision will mean the number of significant digits instead
48
50
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
49
51
  significant: false
50
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
52
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
51
53
  strip_insignificant_zeros: false
52
54
 
53
55
  # Used in NumberHelper.number_to_currency()
54
56
  currency:
55
57
  format:
56
- # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
58
+ # Where is the currency sign? %u is the currency unit, %n is the number (default: $5.00)
57
59
  format: "%u%n"
58
60
  unit: "$"
59
- # These five are to override number.format and are optional
61
+ # These six are to override number.format and are optional
60
62
  separator: "."
61
63
  delimiter: ","
62
64
  precision: 2
65
+ # round_mode:
63
66
  significant: false
64
67
  strip_insignificant_zeros: false
65
68
 
@@ -87,10 +90,11 @@ en:
87
90
  # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
88
91
  human:
89
92
  format:
90
- # These five are to override number.format and are optional
93
+ # These six are to override number.format and are optional
91
94
  # separator:
92
95
  delimiter: ""
93
96
  precision: 3
97
+ # round_mode:
94
98
  significant: true
95
99
  strip_insignificant_zeros: true
96
100
  # Used in number_to_human_size()
@@ -29,6 +29,9 @@ module ActiveSupport
29
29
  # subscriber, the line above should be called after your
30
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
31
  #
32
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
33
+ # This is assigned automatically in a Rails environment.
34
+ #
32
35
  # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
33
36
  # it will properly dispatch the event
34
37
  # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
@@ -93,6 +96,11 @@ module ActiveSupport
93
96
  def flush_all!
94
97
  logger.flush if logger.respond_to?(:flush)
95
98
  end
99
+
100
+ private
101
+ def fetch_public_methods(subscriber, inherit_all)
102
+ subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
+ end
96
104
  end
97
105
 
98
106
  def logger
@@ -106,9 +114,13 @@ module ActiveSupport
106
114
  def finish(name, id, payload)
107
115
  super if logger
108
116
  rescue => e
109
- if logger
110
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
111
- end
117
+ log_exception(name, e)
118
+ end
119
+
120
+ def publish_event(event)
121
+ super if logger
122
+ rescue => e
123
+ log_exception(event.name, e)
112
124
  end
113
125
 
114
126
  private
@@ -130,5 +142,11 @@ module ActiveSupport
130
142
  bold = bold ? BOLD : ""
131
143
  "#{bold}#{color}#{text}#{CLEAR}"
132
144
  end
145
+
146
+ def log_exception(name, e)
147
+ if logger
148
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
149
+ end
150
+ end
133
151
  end
134
152
  end
@@ -14,7 +14,7 @@ module ActiveSupport
14
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
15
  # # => true
16
16
  def self.logger_outputs_to?(logger, *sources)
17
- logdev = logger.instance_variable_get("@logdev")
17
+ logdev = logger.instance_variable_get(:@logdev)
18
18
  logger_source = logdev.dev if logdev.respond_to?(:dev)
19
19
  sources.any? { |source| source == logger_source }
20
20
  end
@@ -4,19 +4,6 @@ require "active_support/concern"
4
4
  require "active_support/core_ext/module/attribute_accessors"
5
5
  require "active_support/logger_thread_safe_level"
6
6
 
7
- module LoggerSilence
8
- extend ActiveSupport::Concern
9
-
10
- included do
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
17
- end
18
- end
19
-
20
7
  module ActiveSupport
21
8
  module LoggerSilence
22
9
  extend ActiveSupport::Concern
@@ -27,19 +14,8 @@ module ActiveSupport
27
14
  end
28
15
 
29
16
  # 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
35
-
36
- yield self
37
- ensure
38
- self.local_level = old_local_level
39
- end
40
- else
41
- yield self
42
- end
17
+ def silence(severity = Logger::ERROR)
18
+ silencer ? log_at(severity) { yield self } : yield(self)
43
19
  end
44
20
  end
45
21
  end
@@ -9,10 +9,6 @@ module ActiveSupport
9
9
  module LoggerThreadSafeLevel # :nodoc:
10
10
  extend ActiveSupport::Concern
11
11
 
12
- included do
13
- cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false
14
- end
15
-
16
12
  Logger::Severity.constants.each do |severity|
17
13
  class_eval(<<-EOT, __FILE__, __LINE__ + 1)
18
14
  def #{severity.downcase}? # def debug?
@@ -21,36 +17,53 @@ module ActiveSupport
21
17
  EOT
22
18
  end
23
19
 
24
- def after_initialize
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
- )
29
- end
30
-
31
- def local_log_id
32
- Fiber.current.__id__
33
- end
34
-
35
20
  def local_level
36
- self.class.local_levels[local_log_id]
21
+ IsolatedExecutionState[:logger_thread_safe_level]
37
22
  end
38
23
 
39
24
  def local_level=(level)
40
- if level
41
- self.class.local_levels[local_log_id] = level
25
+ case level
26
+ when Integer
27
+ when Symbol
28
+ level = Logger::Severity.const_get(level.to_s.upcase)
29
+ when nil
42
30
  else
43
- self.class.local_levels.delete(local_log_id)
31
+ raise ArgumentError, "Invalid log level: #{level.inspect}"
44
32
  end
33
+ IsolatedExecutionState[:logger_thread_safe_level] = level
45
34
  end
46
35
 
47
36
  def level
48
37
  local_level || super
49
38
  end
50
39
 
40
+ # Change the thread-local level for the duration of the given block.
41
+ def log_at(level)
42
+ old_local_level, self.local_level = local_level, level
43
+ yield
44
+ ensure
45
+ self.local_level = old_local_level
46
+ end
47
+
48
+ # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
49
+ # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
51
50
  def add(severity, message = nil, progname = nil, &block) # :nodoc:
52
- return true if @logdev.nil? || (severity || UNKNOWN) < level
53
- super
51
+ severity ||= UNKNOWN
52
+ progname ||= @progname
53
+
54
+ return true if @logdev.nil? || severity < level
55
+
56
+ if message.nil?
57
+ if block_given?
58
+ message = yield
59
+ else
60
+ message = progname
61
+ progname = @progname
62
+ end
63
+ end
64
+
65
+ @logdev.write \
66
+ format_message(format_severity(severity), Time.now, progname, message)
54
67
  end
55
68
  end
56
69
  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"
@@ -23,6 +22,11 @@ module ActiveSupport
23
22
  # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
24
23
  # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
25
24
  # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
25
+ # The +decrypt_and_verify+ method will raise an
26
+ # <tt>ActiveSupport::MessageEncryptor::InvalidMessage</tt> exception if the data
27
+ # provided cannot be decrypted or verified.
28
+ #
29
+ # crypt.decrypt_and_verify('not encrypted data') # => ActiveSupport::MessageEncryptor::InvalidMessage
26
30
  #
27
31
  # === Confining messages to a specific purpose
28
32
  #
@@ -85,7 +89,7 @@ module ActiveSupport
85
89
  cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
86
90
 
87
91
  class << self
88
- def default_cipher #:nodoc:
92
+ def default_cipher # :nodoc:
89
93
  if use_authenticated_message_encryption
90
94
  "aes-256-gcm"
91
95
  else
@@ -94,7 +98,7 @@ module ActiveSupport
94
98
  end
95
99
  end
96
100
 
97
- module NullSerializer #:nodoc:
101
+ module NullSerializer # :nodoc:
98
102
  def self.load(value)
99
103
  value
100
104
  end
@@ -104,7 +108,7 @@ module ActiveSupport
104
108
  end
105
109
  end
106
110
 
107
- module NullVerifier #:nodoc:
111
+ module NullVerifier # :nodoc:
108
112
  def self.verify(value)
109
113
  value
110
114
  end
@@ -134,15 +138,13 @@ module ActiveSupport
134
138
  # * <tt>:digest</tt> - String of digest to use for signing. Default is
135
139
  # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
136
140
  # * <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
141
+ def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer: nil)
140
142
  @secret = secret
141
143
  @sign_secret = sign_secret
142
- @cipher = options[:cipher] || self.class.default_cipher
143
- @digest = options[:digest] || "SHA1" unless aead_mode?
144
+ @cipher = cipher || self.class.default_cipher
145
+ @digest = digest || "SHA1" unless aead_mode?
144
146
  @verifier = resolve_verifier
145
- @serializer = options[:serializer] || Marshal
147
+ @serializer = serializer || Marshal
146
148
  end
147
149
 
148
150
  # Encrypt and sign a message. We need to sign the message in order to avoid
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "openssl"
3
4
  require "base64"
4
5
  require "active_support/core_ext/object/blank"
5
6
  require "active_support/security_utils"
@@ -68,8 +69,8 @@ module ActiveSupport
68
69
  # return the original value. But messages can be set to expire at a given
69
70
  # time with +:expires_in+ or +:expires_at+.
70
71
  #
71
- # @verifier.generate(parcel, expires_in: 1.month)
72
- # @verifier.generate(doowad, expires_at: Time.now.end_of_year)
72
+ # @verifier.generate("parcel", expires_in: 1.month)
73
+ # @verifier.generate("doowad", expires_at: Time.now.end_of_year)
73
74
  #
74
75
  # Then the messages can be verified and returned up to the expire time.
75
76
  # Thereafter, the +verified+ method returns +nil+ while +verify+ raises
@@ -78,8 +79,8 @@ module ActiveSupport
78
79
  # === Rotating keys
79
80
  #
80
81
  # MessageVerifier also supports rotating out old configurations by falling
81
- # back to a stack of verifiers. Call +rotate+ to build and add a verifier to
82
- # so either +verified+ or +verify+ will also try verifying with the fallback.
82
+ # back to a stack of verifiers. Call +rotate+ to build and add a verifier so
83
+ # either +verified+ or +verify+ will also try verifying with the fallback.
83
84
  #
84
85
  # By default any rotated verifiers use the values of the primary
85
86
  # verifier unless specified otherwise.
@@ -103,11 +104,14 @@ module ActiveSupport
103
104
 
104
105
  class InvalidSignature < StandardError; end
105
106
 
106
- def initialize(secret, options = {})
107
+ SEPARATOR = "--" # :nodoc:
108
+ SEPARATOR_LENGTH = SEPARATOR.length # :nodoc:
109
+
110
+ def initialize(secret, digest: nil, serializer: nil)
107
111
  raise ArgumentError, "Secret should not be nil." unless secret
108
112
  @secret = secret
109
- @digest = options[:digest] || "SHA1"
110
- @serializer = options[:serializer] || Marshal
113
+ @digest = digest&.to_s || "SHA1"
114
+ @serializer = serializer || Marshal
111
115
  end
112
116
 
113
117
  # Checks if a signed message could have been generated by signing an object
@@ -120,10 +124,8 @@ module ActiveSupport
120
124
  # tampered_message = signed_message.chop # editing the message invalidates the signature
121
125
  # verifier.valid_message?(tampered_message) # => false
122
126
  def valid_message?(signed_message)
123
- return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
124
-
125
- data, digest = signed_message.split("--")
126
- data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
127
+ data, digest = get_data_and_digest_from(signed_message)
128
+ digest_matches_data?(digest, data)
127
129
  end
128
130
 
129
131
  # Decodes the signed message using the +MessageVerifier+'s secret.
@@ -148,9 +150,9 @@ module ActiveSupport
148
150
  # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
149
151
  # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
150
152
  def verified(signed_message, purpose: nil, **)
151
- if valid_message?(signed_message)
153
+ data, digest = get_data_and_digest_from(signed_message)
154
+ if digest_matches_data?(digest, data)
152
155
  begin
153
- data = signed_message.split("--")[0]
154
156
  message = Messages::Metadata.verify(decode(data), purpose)
155
157
  @serializer.load(message) if message
156
158
  rescue ArgumentError => argument_error
@@ -178,14 +180,14 @@ module ActiveSupport
178
180
 
179
181
  # Generates a signed message for the provided value.
180
182
  #
181
- # The message is signed with the +MessageVerifier+'s secret. Without knowing
182
- # the secret, the original value cannot be extracted from the message.
183
+ # The message is signed with the +MessageVerifier+'s secret.
184
+ # Returns Base64-encoded message joined with the generated signature.
183
185
  #
184
186
  # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
185
187
  # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
186
188
  def generate(value, expires_at: nil, expires_in: nil, purpose: nil)
187
189
  data = encode(Messages::Metadata.wrap(@serializer.dump(value), expires_at: expires_at, expires_in: expires_in, purpose: purpose))
188
- "#{data}--#{generate_digest(data)}"
190
+ "#{data}#{SEPARATOR}#{generate_digest(data)}"
189
191
  end
190
192
 
191
193
  private
@@ -198,8 +200,38 @@ module ActiveSupport
198
200
  end
199
201
 
200
202
  def generate_digest(data)
201
- require "openssl" unless defined?(OpenSSL)
202
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
203
+ OpenSSL::HMAC.hexdigest(@digest, @secret, data)
204
+ end
205
+
206
+ def digest_length_in_hex
207
+ # In hexadecimal (AKA base16) it takes 4 bits to represent a character,
208
+ # hence we multiply the digest's length (in bytes) by 8 to get it in
209
+ # bits and divide by 4 to get its number of characters it hex. Well, 8
210
+ # divided by 4 is 2.
211
+ @digest_length_in_hex ||= OpenSSL::Digest.new(@digest).digest_length * 2
212
+ end
213
+
214
+ def separator_index_for(signed_message)
215
+ index = signed_message.length - digest_length_in_hex - SEPARATOR_LENGTH
216
+ return if index.negative? || signed_message[index, SEPARATOR_LENGTH] != SEPARATOR
217
+
218
+ index
219
+ end
220
+
221
+ def get_data_and_digest_from(signed_message)
222
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.empty?
223
+
224
+ separator_index = separator_index_for(signed_message)
225
+ return if separator_index.nil?
226
+
227
+ data = signed_message[0...separator_index]
228
+ digest = signed_message[separator_index + SEPARATOR_LENGTH..-1]
229
+
230
+ [data, digest]
231
+ end
232
+
233
+ def digest_matches_data?(digest, data)
234
+ data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
203
235
  end
204
236
  end
205
237
  end
@@ -3,8 +3,8 @@
3
3
  require "time"
4
4
 
5
5
  module ActiveSupport
6
- module Messages #:nodoc:
7
- class Metadata #:nodoc:
6
+ module Messages # :nodoc:
7
+ class Metadata # :nodoc:
8
8
  def initialize(message, expires_at = nil, purpose = nil)
9
9
  @message, @purpose = message, purpose
10
10
  @expires_at = expires_at.is_a?(String) ? parse_expires_at(expires_at) : expires_at
@@ -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,7 +18,7 @@ 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
24
  run_rotations(on_rotation) { |encryptor| encryptor.decrypt_and_verify(*args, **options) } || raise
@@ -32,7 +33,7 @@ module ActiveSupport
32
33
  module Verifier
33
34
  include Rotator
34
35
 
35
- def verified(*args, on_rotation: nil, **options)
36
+ def verified(*args, on_rotation: @on_rotation, **options)
36
37
  super || run_rotations(on_rotation) { |verifier| verifier.verified(*args, **options) }
37
38
  end
38
39
 
@@ -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
@@ -5,8 +5,8 @@ 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
7
 
8
- module ActiveSupport #:nodoc:
9
- module Multibyte #:nodoc:
8
+ module ActiveSupport # :nodoc:
9
+ module Multibyte # :nodoc:
10
10
  # Chars enables you to work transparently with UTF-8 encoding in the Ruby
11
11
  # String class without having extensive knowledge about the encoding. A
12
12
  # Chars object accepts a string upon initialization and proxies String
@@ -48,7 +48,7 @@ module ActiveSupport #:nodoc:
48
48
  alias to_s wrapped_string
49
49
  alias to_str wrapped_string
50
50
 
51
- delegate :<=>, :=~, :acts_like_string?, to: :wrapped_string
51
+ delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
52
52
 
53
53
  # Creates a new Chars instance by wrapping _string_.
54
54
  def initialize(string)
@@ -59,7 +59,7 @@ module ActiveSupport #:nodoc:
59
59
  # Forward all undefined methods to the wrapped string.
60
60
  def method_missing(method, *args, &block)
61
61
  result = @wrapped_string.__send__(method, *args, &block)
62
- if /!$/.match?(method)
62
+ if method.end_with?("!")
63
63
  self if result
64
64
  else
65
65
  result.kind_of?(String) ? chars(result) : result
@@ -73,17 +73,6 @@ module ActiveSupport #:nodoc:
73
73
  @wrapped_string.respond_to?(method, include_private)
74
74
  end
75
75
 
76
- # Returns +true+ when the proxy class can handle the string. Returns
77
- # +false+ otherwise.
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
-
84
- string.encoding == Encoding::UTF_8
85
- end
86
-
87
76
  # Works just like <tt>String#split</tt>, with the exception that the items
88
77
  # in the resulting list are Chars instances instead of String. This makes
89
78
  # chaining methods easier.
@@ -113,7 +102,7 @@ module ActiveSupport #:nodoc:
113
102
  #
114
103
  # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
115
104
  def reverse
116
- chars(@wrapped_string.scan(/\X/).reverse.join)
105
+ chars(@wrapped_string.grapheme_clusters.reverse.join)
117
106
  end
118
107
 
119
108
  # Limits the byte size of the string to a number of bytes without breaking
@@ -134,46 +123,18 @@ module ActiveSupport #:nodoc:
134
123
  end
135
124
  alias_method :titlecase, :titleize
136
125
 
137
- # Returns the KC normalization of the string by default. NFKC is
138
- # considered the best normalization form for passing strings to databases
139
- # and validations.
140
- #
141
- # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
142
- # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
143
- # ActiveSupport::Multibyte::Unicode.default_normalization_form
144
- def normalize(form = nil)
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
163
- end
164
-
165
126
  # Performs canonical decomposition on all the characters.
166
127
  #
167
- # 'é'.length # => 2
168
- # 'é'.mb_chars.decompose.to_s.length # => 3
128
+ # 'é'.length # => 1
129
+ # 'é'.mb_chars.decompose.to_s.length # => 2
169
130
  def decompose
170
131
  chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
171
132
  end
172
133
 
173
134
  # Performs composition on all the characters.
174
135
  #
175
- # 'é'.length # => 3
176
- # 'é'.mb_chars.compose.to_s.length # => 2
136
+ # 'é'.length # => 1
137
+ # 'é'.mb_chars.compose.to_s.length # => 1
177
138
  def compose
178
139
  chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
179
140
  end
@@ -181,9 +142,9 @@ module ActiveSupport #:nodoc:
181
142
  # Returns the number of grapheme clusters in the string.
182
143
  #
183
144
  # 'क्षि'.mb_chars.length # => 4
184
- # 'क्षि'.mb_chars.grapheme_length # => 3
145
+ # 'क्षि'.mb_chars.grapheme_length # => 2
185
146
  def grapheme_length
186
- @wrapped_string.scan(/\X/).length
147
+ @wrapped_string.grapheme_clusters.length
187
148
  end
188
149
 
189
150
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -195,13 +156,13 @@ module ActiveSupport #:nodoc:
195
156
  chars(Unicode.tidy_bytes(@wrapped_string, force))
196
157
  end
197
158
 
198
- def as_json(options = nil) #:nodoc:
159
+ def as_json(options = nil) # :nodoc:
199
160
  to_s.as_json(options)
200
161
  end
201
162
 
202
163
  %w(reverse tidy_bytes).each do |method|
203
164
  define_method("#{method}!") do |*args|
204
- @wrapped_string = send(method, *args).to_s
165
+ @wrapped_string = public_send(method, *args).to_s
205
166
  self
206
167
  end
207
168
  end