activesupport 6.0.6.1 → 6.1.0.rc1

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +337 -573
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/array_inquirer.rb +4 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +2 -2
  9. data/lib/active_support/cache/mem_cache_store.rb +20 -14
  10. data/lib/active_support/cache/memory_store.rb +38 -26
  11. data/lib/active_support/cache/redis_cache_store.rb +25 -25
  12. data/lib/active_support/cache/strategy/local_cache.rb +13 -4
  13. data/lib/active_support/cache.rb +75 -34
  14. data/lib/active_support/callbacks.rb +65 -56
  15. data/lib/active_support/concern.rb +46 -2
  16. data/lib/active_support/configurable.rb +3 -3
  17. data/lib/active_support/configuration_file.rb +46 -0
  18. data/lib/active_support/core_ext/benchmark.rb +2 -2
  19. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  20. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  21. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  22. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  23. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  24. data/lib/active_support/core_ext/enumerable.rb +76 -4
  25. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  26. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  27. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  28. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  29. data/lib/active_support/core_ext/load_error.rb +1 -1
  30. data/lib/active_support/core_ext/marshal.rb +2 -0
  31. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  32. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  33. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  34. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  35. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  36. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  37. data/lib/active_support/core_ext/name_error.rb +29 -2
  38. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  39. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  40. data/lib/active_support/core_ext/object/json.rb +5 -1
  41. data/lib/active_support/core_ext/object/try.rb +2 -2
  42. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  44. data/lib/active_support/core_ext/string/access.rb +5 -24
  45. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  46. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  47. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  48. data/lib/active_support/core_ext/string/output_safety.rb +8 -38
  49. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  50. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  51. data/lib/active_support/core_ext/symbol.rb +3 -0
  52. data/lib/active_support/core_ext/time/calculations.rb +16 -0
  53. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  54. data/lib/active_support/core_ext/uri.rb +5 -1
  55. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  56. data/lib/active_support/current_attributes.rb +7 -2
  57. data/lib/active_support/dependencies.rb +38 -24
  58. data/lib/active_support/deprecation/behaviors.rb +15 -2
  59. data/lib/active_support/deprecation/disallowed.rb +56 -0
  60. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  61. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  62. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  63. data/lib/active_support/deprecation/reporting.rb +50 -7
  64. data/lib/active_support/deprecation.rb +6 -1
  65. data/lib/active_support/descendants_tracker.rb +6 -2
  66. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  67. data/lib/active_support/duration.rb +71 -22
  68. data/lib/active_support/encrypted_file.rb +19 -2
  69. data/lib/active_support/environment_inquirer.rb +20 -0
  70. data/lib/active_support/evented_file_update_checker.rb +69 -133
  71. data/lib/active_support/execution_wrapper.rb +13 -16
  72. data/lib/active_support/fork_tracker.rb +58 -0
  73. data/lib/active_support/gem_version.rb +3 -3
  74. data/lib/active_support/hash_with_indifferent_access.rb +35 -22
  75. data/lib/active_support/i18n_railtie.rb +14 -19
  76. data/lib/active_support/inflector/inflections.rb +1 -2
  77. data/lib/active_support/inflector/methods.rb +35 -31
  78. data/lib/active_support/inflector/transliterate.rb +4 -4
  79. data/lib/active_support/json/decoding.rb +4 -4
  80. data/lib/active_support/json/encoding.rb +5 -1
  81. data/lib/active_support/key_generator.rb +1 -1
  82. data/lib/active_support/locale/en.yml +7 -3
  83. data/lib/active_support/log_subscriber.rb +8 -0
  84. data/lib/active_support/logger.rb +1 -1
  85. data/lib/active_support/logger_silence.rb +2 -26
  86. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  87. data/lib/active_support/message_encryptor.rb +4 -7
  88. data/lib/active_support/message_verifier.rb +5 -5
  89. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  90. data/lib/active_support/messages/rotator.rb +6 -5
  91. data/lib/active_support/multibyte/chars.rb +4 -42
  92. data/lib/active_support/multibyte/unicode.rb +9 -83
  93. data/lib/active_support/notifications/fanout.rb +23 -8
  94. data/lib/active_support/notifications/instrumenter.rb +6 -15
  95. data/lib/active_support/notifications.rb +31 -4
  96. data/lib/active_support/number_helper/number_converter.rb +1 -1
  97. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  98. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  99. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  100. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  101. data/lib/active_support/number_helper.rb +29 -14
  102. data/lib/active_support/option_merger.rb +2 -1
  103. data/lib/active_support/ordered_options.rb +8 -2
  104. data/lib/active_support/parameter_filter.rb +15 -10
  105. data/lib/active_support/per_thread_registry.rb +1 -1
  106. data/lib/active_support/rails.rb +1 -4
  107. data/lib/active_support/railtie.rb +23 -1
  108. data/lib/active_support/reloader.rb +1 -1
  109. data/lib/active_support/secure_compare_rotator.rb +51 -0
  110. data/lib/active_support/security_utils.rb +19 -12
  111. data/lib/active_support/string_inquirer.rb +4 -2
  112. data/lib/active_support/subscriber.rb +12 -7
  113. data/lib/active_support/tagged_logging.rb +29 -4
  114. data/lib/active_support/testing/assertions.rb +18 -11
  115. data/lib/active_support/testing/parallelization/server.rb +78 -0
  116. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  117. data/lib/active_support/testing/parallelization.rb +12 -95
  118. data/lib/active_support/testing/time_helpers.rb +40 -3
  119. data/lib/active_support/time_with_zone.rb +66 -42
  120. data/lib/active_support/values/time_zone.rb +20 -10
  121. data/lib/active_support/xml_mini/rexml.rb +8 -1
  122. data/lib/active_support.rb +13 -1
  123. metadata +39 -42
  124. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  125. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  126. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  127. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  128. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  129. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/dependencies/autoload"
4
-
5
3
  module ActiveSupport
6
4
  module NumberHelper
7
5
  extend ActiveSupport::Autoload
@@ -73,6 +71,8 @@ module ActiveSupport
73
71
  # (defaults to current locale).
74
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
75
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
76
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
77
77
  # (defaults to "$").
78
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -111,6 +111,8 @@ module ActiveSupport
111
111
  # # => "1234567890,50 &pound;"
112
112
  # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
113
113
  # # => "$1,234,567,890.5"
114
+ # number_to_currency(1234567890.50, precision: 0, round_mode: :up)
115
+ # # => "$1,234,567,891"
114
116
  def number_to_currency(number, options = {})
115
117
  NumberToCurrencyConverter.convert(number, options)
116
118
  end
@@ -124,6 +126,8 @@ module ActiveSupport
124
126
  # (defaults to current locale).
125
127
  # * <tt>:precision</tt> - Sets the precision of the number
126
128
  # (defaults to 3). Keeps the number's precision if +nil+.
129
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
130
+ # (defaults to :default. See BigDecimal::mode)
127
131
  # * <tt>:significant</tt> - If +true+, precision will be the number
128
132
  # of significant_digits. If +false+, the number of fractional
129
133
  # digits (defaults to +false+).
@@ -139,15 +143,16 @@ module ActiveSupport
139
143
  #
140
144
  # ==== Examples
141
145
  #
142
- # number_to_percentage(100) # => "100.000%"
143
- # number_to_percentage('98') # => "98.000%"
144
- # number_to_percentage(100, precision: 0) # => "100%"
145
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
146
- # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
147
- # number_to_percentage(1000, locale: :fr) # => "1000,000%"
148
- # number_to_percentage(1000, precision: nil) # => "1000%"
149
- # number_to_percentage('98a') # => "98a%"
150
- # number_to_percentage(100, format: '%n %') # => "100.000 %"
146
+ # number_to_percentage(100) # => "100.000%"
147
+ # number_to_percentage('98') # => "98.000%"
148
+ # number_to_percentage(100, precision: 0) # => "100%"
149
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
150
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
151
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
152
+ # number_to_percentage(1000, precision: nil) # => "1000%"
153
+ # number_to_percentage('98a') # => "98a%"
154
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
155
+ # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
151
156
  def number_to_percentage(number, options = {})
152
157
  NumberToPercentageConverter.convert(number, options)
153
158
  end
@@ -198,6 +203,8 @@ module ActiveSupport
198
203
  # (defaults to current locale).
199
204
  # * <tt>:precision</tt> - Sets the precision of the number
200
205
  # (defaults to 3). Keeps the number's precision if +nil+.
206
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
207
+ # (defaults to :default. See BigDecimal::mode)
201
208
  # * <tt>:significant</tt> - If +true+, precision will be the number
202
209
  # of significant_digits. If +false+, the number of fractional
203
210
  # digits (defaults to +false+).
@@ -219,6 +226,7 @@ module ActiveSupport
219
226
  # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
220
227
  # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
221
228
  # number_to_rounded(13, precision: nil) # => "13"
229
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
222
230
  # number_to_rounded(111.234, locale: :fr) # => "111,234"
223
231
  #
224
232
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
@@ -232,7 +240,7 @@ module ActiveSupport
232
240
  end
233
241
 
234
242
  # Formats the bytes in +number+ into a more understandable
235
- # representation (e.g., giving it 1500 yields 1.5 KB). This
243
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
236
244
  # method is useful for reporting file sizes to users. You can
237
245
  # customize the format in the +options+ hash.
238
246
  #
@@ -245,6 +253,8 @@ module ActiveSupport
245
253
  # (defaults to current locale).
246
254
  # * <tt>:precision</tt> - Sets the precision of the number
247
255
  # (defaults to 3).
256
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
257
+ # (defaults to :default. See BigDecimal::mode)
248
258
  # * <tt>:significant</tt> - If +true+, precision will be the number
249
259
  # of significant_digits. If +false+, the number of fractional
250
260
  # digits (defaults to +true+)
@@ -268,6 +278,7 @@ module ActiveSupport
268
278
  # number_to_human_size(1234567890123456789) # => "1.07 EB"
269
279
  # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
270
280
  # number_to_human_size(483989, precision: 2) # => "470 KB"
281
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
271
282
  # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
272
283
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
273
284
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
@@ -276,7 +287,7 @@ module ActiveSupport
276
287
  end
277
288
 
278
289
  # Pretty prints (formats and approximates) a number in a way it
279
- # is more readable by humans (eg.: 1200000000 becomes "1.2
290
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
280
291
  # Billion"). This is useful for numbers that can get very large
281
292
  # (and too hard to read).
282
293
  #
@@ -284,7 +295,7 @@ module ActiveSupport
284
295
  # size.
285
296
  #
286
297
  # You can also define your own unit-quantifier names if you want
287
- # to use other decimal units (eg.: 1500 becomes "1.5
298
+ # to use other decimal units (e.g.: 1500 becomes "1.5
288
299
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
289
300
  # define a wide range of unit quantifiers, even fractional ones
290
301
  # (centi, deci, mili, etc).
@@ -295,6 +306,8 @@ module ActiveSupport
295
306
  # (defaults to current locale).
296
307
  # * <tt>:precision</tt> - Sets the precision of the number
297
308
  # (defaults to 3).
309
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
310
+ # (defaults to :default. See BigDecimal::mode)
298
311
  # * <tt>:significant</tt> - If +true+, precision will be the number
299
312
  # of significant_digits. If +false+, the number of fractional
300
313
  # digits (defaults to +true+)
@@ -332,6 +345,8 @@ module ActiveSupport
332
345
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
333
346
  # number_to_human(489939, precision: 2) # => "490 Thousand"
334
347
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
348
+ # number_to_human(489939, precision: 2
349
+ # , round_mode: :down) # => "480 Thousand"
335
350
  # number_to_human(1234567, precision: 4,
336
351
  # significant: false) # => "1.2346 Million"
337
352
  # number_to_human(1234567, precision: 1,
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/hash/deep_merge"
4
+ require "active_support/core_ext/symbol/starts_ends_with"
4
5
 
5
6
  module ActiveSupport
6
7
  class OptionMerger #:nodoc:
7
8
  instance_methods.each do |method|
8
- undef_method(method) if !/^(__|instance_eval|class|object_id)/.match?(method)
9
+ undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
9
10
  end
10
11
 
11
12
  def initialize(context, options)
@@ -3,7 +3,9 @@
3
3
  require "active_support/core_ext/object/blank"
4
4
 
5
5
  module ActiveSupport
6
- # Usually key value pairs are handled something like this:
6
+ # +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods.
7
+ #
8
+ # With a +Hash+, key-value pairs are typically managed like this:
7
9
  #
8
10
  # h = {}
9
11
  # h[:boy] = 'John'
@@ -12,7 +14,7 @@ module ActiveSupport
12
14
  # h[:girl] # => 'Mary'
13
15
  # h[:dog] # => nil
14
16
  #
15
- # Using +OrderedOptions+, the above code could be reduced to:
17
+ # Using +OrderedOptions+, the above code can be written as:
16
18
  #
17
19
  # h = ActiveSupport::OrderedOptions.new
18
20
  # h.boy = 'John'
@@ -60,6 +62,10 @@ module ActiveSupport
60
62
  def extractable_options?
61
63
  true
62
64
  end
65
+
66
+ def inspect
67
+ "#<#{self.class.name} #{super}>"
68
+ end
63
69
  end
64
70
 
65
71
  # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/object/duplicable"
4
- require "active_support/core_ext/array/extract"
5
4
 
6
5
  module ActiveSupport
7
6
  # +ParameterFilter+ allows you to specify keys for sensitive data from
@@ -22,7 +21,7 @@ module ActiveSupport
22
21
  # change { file: { code: "xxxx"} }
23
22
  #
24
23
  # ActiveSupport::ParameterFilter.new([-> (k, v) do
25
- # v.reverse! if k =~ /secret/i
24
+ # v.reverse! if /secret/i.match?(k)
26
25
  # end])
27
26
  # => reverses the value to all keys matching /secret/i
28
27
  class ParameterFilter
@@ -59,24 +58,30 @@ module ActiveSupport
59
58
  def self.compile(filters, mask:)
60
59
  return lambda { |params| params.dup } if filters.empty?
61
60
 
62
- strings, regexps, blocks = [], [], []
61
+ strings, regexps, blocks, deep_regexps, deep_strings = [], [], [], nil, nil
63
62
 
64
63
  filters.each do |item|
65
64
  case item
66
65
  when Proc
67
66
  blocks << item
68
67
  when Regexp
69
- regexps << item
68
+ if item.to_s.include?("\\.")
69
+ (deep_regexps ||= []) << item
70
+ else
71
+ regexps << item
72
+ end
70
73
  else
71
- strings << Regexp.escape(item.to_s)
74
+ s = Regexp.escape(item.to_s)
75
+ if s.include?("\\.")
76
+ (deep_strings ||= []) << s
77
+ else
78
+ strings << s
79
+ end
72
80
  end
73
81
  end
74
82
 
75
- deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
76
- deep_strings = strings.extract! { |s| s.include?("\\.") }
77
-
78
83
  regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
79
- deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
84
+ (deep_regexps ||= []) << Regexp.new(deep_strings.join("|"), true) if deep_strings&.any?
80
85
 
81
86
  new regexps, deep_regexps, blocks, mask: mask
82
87
  end
@@ -85,7 +90,7 @@ module ActiveSupport
85
90
 
86
91
  def initialize(regexps, deep_regexps, blocks, mask:)
87
92
  @regexps = regexps
88
- @deep_regexps = deep_regexps.any? ? deep_regexps : nil
93
+ @deep_regexps = deep_regexps&.any? ? deep_regexps : nil
89
94
  @blocks = blocks
90
95
  @mask = mask
91
96
  end
@@ -40,7 +40,7 @@ module ActiveSupport
40
40
  # If the class has an initializer, it must accept no arguments.
41
41
  module PerThreadRegistry
42
42
  def self.extended(object)
43
- object.instance_variable_set "@per_thread_registry_key", object.name.freeze
43
+ object.instance_variable_set :@per_thread_registry_key, object.name.freeze
44
44
  end
45
45
 
46
46
  def instance
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This is private interface.
3
+ # This is a private interface.
4
4
  #
5
5
  # Rails components cherry pick from Active Support as needed, but there are a
6
6
  # few features that are used for sure in some way or another and it is not worth
@@ -13,9 +13,6 @@
13
13
  # Defines Object#blank? and Object#present?.
14
14
  require "active_support/core_ext/object/blank"
15
15
 
16
- # Rails own autoload, eager_load, etc.
17
- require "active_support/dependencies/autoload"
18
-
19
16
  # Support for ClassMethods and the included macro.
20
17
  require "active_support/concern"
21
18
 
@@ -22,12 +22,25 @@ module ActiveSupport
22
22
  app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
23
23
  app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
24
24
  app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
25
+
26
+ ActiveSupport.on_load(:active_support_test_case) do
27
+ require "active_support/current_attributes/test_helper"
28
+ include ActiveSupport::CurrentAttributes::TestHelper
29
+ end
25
30
  end
26
31
 
27
32
  initializer "active_support.deprecation_behavior" do |app|
28
33
  if deprecation = app.config.active_support.deprecation
29
34
  ActiveSupport::Deprecation.behavior = deprecation
30
35
  end
36
+
37
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
38
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
39
+ end
40
+
41
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
42
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
43
+ end
31
44
  end
32
45
 
33
46
  # Sets the default value for Time.zone
@@ -65,15 +78,24 @@ module ActiveSupport
65
78
  initializer "active_support.set_configs" do |app|
66
79
  app.config.active_support.each do |k, v|
67
80
  k = "#{k}="
68
- ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
81
+ ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
69
82
  end
70
83
  end
71
84
 
72
85
  initializer "active_support.set_hash_digest_class" do |app|
73
86
  config.after_initialize do
74
87
  if app.config.active_support.use_sha1_digests
88
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
+ config.active_support.use_sha1_digests is deprecated and will
90
+ be removed from Rails 6.2. Use
91
+ config.active_support.hash_digest_class = ::Digest::SHA1 instead.
92
+ MSG
75
93
  ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
76
94
  end
95
+
96
+ if klass = app.config.active_support.hash_digest_class
97
+ ActiveSupport::Digest.hash_digest_class = klass
98
+ end
77
99
  end
78
100
  end
79
101
  end
@@ -58,7 +58,7 @@ module ActiveSupport
58
58
  prepare!
59
59
  end
60
60
 
61
- def self.run!(reset: false) # :nodoc:
61
+ def self.run! # :nodoc:
62
62
  if check!
63
63
  super
64
64
  else
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/security_utils"
4
+ require "active_support/messages/rotator"
5
+
6
+ module ActiveSupport
7
+ # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
8
+ # and allows you to rotate a previously defined value to a new one.
9
+ #
10
+ # It can be used as follow:
11
+ #
12
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
13
+ # rotator.rotate('previous_production_value')
14
+ # rotator.secure_compare!('previous_production_value')
15
+ #
16
+ # One real use case example would be to rotate a basic auth credentials:
17
+ #
18
+ # class MyController < ApplicationController
19
+ # def authenticate_request
20
+ # rotator = ActiveSupport::SecureComparerotator.new('new_password')
21
+ # rotator.rotate('old_password')
22
+ #
23
+ # authenticate_or_request_with_http_basic do |username, password|
24
+ # rotator.secure_compare!(password)
25
+ # rescue ActiveSupport::SecureCompareRotator::InvalidMatch
26
+ # false
27
+ # end
28
+ # end
29
+ # end
30
+ class SecureCompareRotator
31
+ include SecurityUtils
32
+ prepend Messages::Rotator
33
+
34
+ InvalidMatch = Class.new(StandardError)
35
+
36
+ def initialize(value, **_options)
37
+ @value = value
38
+ end
39
+
40
+ def secure_compare!(other_value, on_rotation: @on_rotation)
41
+ secure_compare(@value, other_value) ||
42
+ run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } ||
43
+ raise(InvalidMatch)
44
+ end
45
+
46
+ private
47
+ def build_rotation(previous_value, _options)
48
+ self.class.new(previous_value)
49
+ end
50
+ end
51
+ end
@@ -1,30 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest/sha2"
4
-
5
3
  module ActiveSupport
6
4
  module SecurityUtils
7
5
  # Constant time string comparison, for fixed length strings.
8
6
  #
9
7
  # The values compared should be of fixed length, such as strings
10
8
  # that have already been processed by HMAC. Raises in case of length mismatch.
11
- def fixed_length_secure_compare(a, b)
12
- raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
13
9
 
14
- l = a.unpack "C#{a.bytesize}"
10
+ if defined?(OpenSSL.fixed_length_secure_compare)
11
+ def fixed_length_secure_compare(a, b)
12
+ OpenSSL.fixed_length_secure_compare(a, b)
13
+ end
14
+ else
15
+ def fixed_length_secure_compare(a, b)
16
+ raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
17
+
18
+ l = a.unpack "C#{a.bytesize}"
15
19
 
16
- res = 0
17
- b.each_byte { |byte| res |= byte ^ l.shift }
18
- res == 0
20
+ res = 0
21
+ b.each_byte { |byte| res |= byte ^ l.shift }
22
+ res == 0
23
+ end
19
24
  end
20
25
  module_function :fixed_length_secure_compare
21
26
 
22
- # Constant time string comparison, for variable length strings.
27
+ # Secure string comparison for strings of variable length.
23
28
  #
24
- # The values are first processed by SHA256, so that we don't leak length info
25
- # via timing attacks.
29
+ # While a timing attack would not be able to discern the content of
30
+ # a secret compared via secure_compare, it is possible to determine
31
+ # the secret length. This should be considered when using secure_compare
32
+ # to compare weak, short secrets to user input.
26
33
  def secure_compare(a, b)
27
- fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
34
+ a.length == b.length && fixed_length_secure_compare(a, b)
28
35
  end
29
36
  module_function :secure_compare
30
37
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module ActiveSupport
4
6
  # Wrapping a string in this class gives you a prettier way to test
5
7
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
@@ -19,11 +21,11 @@ module ActiveSupport
19
21
  class StringInquirer < String
20
22
  private
21
23
  def respond_to_missing?(method_name, include_private = false)
22
- (method_name[-1] == "?") || super
24
+ method_name.end_with?("?") || super
23
25
  end
24
26
 
25
27
  def method_missing(method_name, *arguments)
26
- if method_name[-1] == "?"
28
+ if method_name.end_with?("?")
27
29
  self == method_name[0..-2]
28
30
  else
29
31
  super
@@ -31,15 +31,16 @@ module ActiveSupport
31
31
  class Subscriber
32
32
  class << self
33
33
  # Attach the subscriber to a namespace.
34
- def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
34
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
35
35
  @namespace = namespace
36
36
  @subscriber = subscriber
37
37
  @notifier = notifier
38
+ @inherit_all = inherit_all
38
39
 
39
40
  subscribers << subscriber
40
41
 
41
42
  # Add event subscribers for all existing methods on the class.
42
- subscriber.public_methods(false).each do |event|
43
+ fetch_public_methods(subscriber, inherit_all).each do |event|
43
44
  add_event_subscriber(event)
44
45
  end
45
46
  end
@@ -55,7 +56,7 @@ module ActiveSupport
55
56
  subscribers.delete(subscriber)
56
57
 
57
58
  # Remove event subscribers of all existing methods on the class.
58
- subscriber.public_methods(false).each do |event|
59
+ fetch_public_methods(subscriber, true).each do |event|
59
60
  remove_event_subscriber(event)
60
61
  end
61
62
 
@@ -81,18 +82,18 @@ module ActiveSupport
81
82
  attr_reader :subscriber, :notifier, :namespace
82
83
 
83
84
  def add_event_subscriber(event) # :doc:
84
- return if invalid_event?(event.to_s)
85
+ return if invalid_event?(event)
85
86
 
86
87
  pattern = prepare_pattern(event)
87
88
 
88
- # Don't add multiple subscribers (eg. if methods are redefined).
89
+ # Don't add multiple subscribers (e.g. if methods are redefined).
89
90
  return if pattern_subscribed?(pattern)
90
91
 
91
92
  subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
92
93
  end
93
94
 
94
95
  def remove_event_subscriber(event) # :doc:
95
- return if invalid_event?(event.to_s)
96
+ return if invalid_event?(event)
96
97
 
97
98
  pattern = prepare_pattern(event)
98
99
 
@@ -107,7 +108,7 @@ module ActiveSupport
107
108
  end
108
109
 
109
110
  def invalid_event?(event)
110
- %w{ start finish }.include?(event.to_s)
111
+ %i{ start finish }.include?(event.to_sym)
111
112
  end
112
113
 
113
114
  def prepare_pattern(event)
@@ -117,6 +118,10 @@ module ActiveSupport
117
118
  def pattern_subscribed?(pattern)
118
119
  subscriber.patterns.key?(pattern)
119
120
  end
121
+
122
+ def fetch_public_methods(subscriber, inherit_all)
123
+ subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
124
+ end
120
125
  end
121
126
 
122
127
  attr_reader :patterns # :nodoc:
@@ -8,11 +8,20 @@ require "active_support/logger"
8
8
  module ActiveSupport
9
9
  # Wraps any standard Logger object to provide tagging capabilities.
10
10
  #
11
+ # May be called with a block:
12
+ #
11
13
  # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
12
14
  # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
13
15
  # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
14
16
  # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
15
17
  #
18
+ # If called without a block, a new logger will be returned with applied tags:
19
+ #
20
+ # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
21
+ # logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff"
22
+ # logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
23
+ # logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
24
+ #
16
25
  # This is used by the default Rails.logger as configured by Railties to make
17
26
  # it easy to stamp log lines with subdomains, request ids, and anything else
18
27
  # to aid debugging of multi-user production applications.
@@ -31,9 +40,10 @@ module ActiveSupport
31
40
  end
32
41
 
33
42
  def push_tags(*tags)
34
- tags.flatten.reject(&:blank?).tap do |new_tags|
35
- current_tags.concat new_tags
36
- end
43
+ tags.flatten!
44
+ tags.reject!(&:blank?)
45
+ current_tags.concat tags
46
+ tags
37
47
  end
38
48
 
39
49
  def pop_tags(size = 1)
@@ -60,6 +70,14 @@ module ActiveSupport
60
70
  end
61
71
  end
62
72
 
73
+ module LocalTagStorage # :nodoc:
74
+ attr_accessor :current_tags
75
+
76
+ def self.extended(base)
77
+ base.current_tags = []
78
+ end
79
+ end
80
+
63
81
  def self.new(logger)
64
82
  logger = logger.dup
65
83
 
@@ -77,7 +95,14 @@ module ActiveSupport
77
95
  delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
78
96
 
79
97
  def tagged(*tags)
80
- formatter.tagged(*tags) { yield self }
98
+ if block_given?
99
+ formatter.tagged(*tags) { yield self }
100
+ else
101
+ logger = ActiveSupport::TaggedLogging.new(self)
102
+ logger.formatter.extend LocalTagStorage
103
+ logger.push_tags(*formatter.current_tags, *tags)
104
+ logger
105
+ end
81
106
  end
82
107
 
83
108
  def flush