activesupport 6.1.4.2 → 7.0.2.3

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

Potentially problematic release.


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

Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +226 -462
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +15 -9
  8. data/lib/active_support/cache/mem_cache_store.rb +127 -32
  9. data/lib/active_support/cache/memory_store.rb +23 -15
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +42 -67
  12. data/lib/active_support/cache/strategy/local_cache.rb +35 -61
  13. data/lib/active_support/cache.rb +189 -45
  14. data/lib/active_support/callbacks.rb +180 -81
  15. data/lib/active_support/code_generator.rb +65 -0
  16. data/lib/active_support/concern.rb +5 -5
  17. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  18. data/lib/active_support/concurrency/share_lock.rb +2 -2
  19. data/lib/active_support/configurable.rb +6 -3
  20. data/lib/active_support/configuration_file.rb +1 -1
  21. data/lib/active_support/core_ext/array/access.rb +1 -5
  22. data/lib/active_support/core_ext/array/conversions.rb +13 -11
  23. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  24. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  25. data/lib/active_support/core_ext/array.rb +1 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  28. data/lib/active_support/core_ext/date/blank.rb +1 -1
  29. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  30. data/lib/active_support/core_ext/date/conversions.rb +11 -11
  31. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  32. data/lib/active_support/core_ext/date.rb +1 -0
  33. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  34. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  36. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  37. data/lib/active_support/core_ext/date_time.rb +1 -0
  38. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  39. data/lib/active_support/core_ext/enumerable.rb +78 -26
  40. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  41. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  42. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  43. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  44. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  45. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  46. data/lib/active_support/core_ext/name_error.rb +2 -8
  47. data/lib/active_support/core_ext/numeric/conversions.rb +79 -76
  48. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  49. data/lib/active_support/core_ext/numeric.rb +1 -0
  50. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  51. data/lib/active_support/core_ext/object/blank.rb +2 -2
  52. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  53. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  54. data/lib/active_support/core_ext/object/json.rb +29 -24
  55. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  56. data/lib/active_support/core_ext/object/try.rb +20 -20
  57. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  58. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  59. data/lib/active_support/core_ext/pathname.rb +3 -0
  60. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  61. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  62. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  63. data/lib/active_support/core_ext/range/each.rb +1 -1
  64. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
  65. data/lib/active_support/core_ext/range.rb +1 -1
  66. data/lib/active_support/core_ext/string/filters.rb +1 -1
  67. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  68. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  69. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  70. data/lib/active_support/core_ext/time/calculations.rb +7 -6
  71. data/lib/active_support/core_ext/time/conversions.rb +13 -12
  72. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  73. data/lib/active_support/core_ext/time/zones.rb +4 -19
  74. data/lib/active_support/core_ext/time.rb +1 -0
  75. data/lib/active_support/core_ext/uri.rb +3 -27
  76. data/lib/active_support/core_ext.rb +1 -0
  77. data/lib/active_support/current_attributes.rb +31 -14
  78. data/lib/active_support/dependencies/interlock.rb +10 -18
  79. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  80. data/lib/active_support/dependencies.rb +58 -788
  81. data/lib/active_support/deprecation/behaviors.rb +4 -1
  82. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  83. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  84. data/lib/active_support/deprecation.rb +1 -1
  85. data/lib/active_support/descendants_tracker.rb +174 -68
  86. data/lib/active_support/digest.rb +5 -3
  87. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  88. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  89. data/lib/active_support/duration.rb +81 -51
  90. data/lib/active_support/encrypted_configuration.rb +13 -2
  91. data/lib/active_support/encrypted_file.rb +1 -1
  92. data/lib/active_support/environment_inquirer.rb +1 -1
  93. data/lib/active_support/error_reporter.rb +117 -0
  94. data/lib/active_support/evented_file_update_checker.rb +1 -1
  95. data/lib/active_support/execution_context/test_helper.rb +13 -0
  96. data/lib/active_support/execution_context.rb +53 -0
  97. data/lib/active_support/execution_wrapper.rb +43 -21
  98. data/lib/active_support/executor/test_helper.rb +7 -0
  99. data/lib/active_support/fork_tracker.rb +19 -12
  100. data/lib/active_support/gem_version.rb +4 -4
  101. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  102. data/lib/active_support/html_safe_translation.rb +43 -0
  103. data/lib/active_support/i18n.rb +1 -0
  104. data/lib/active_support/i18n_railtie.rb +1 -1
  105. data/lib/active_support/inflector/inflections.rb +23 -7
  106. data/lib/active_support/inflector/methods.rb +24 -48
  107. data/lib/active_support/isolated_execution_state.rb +64 -0
  108. data/lib/active_support/json/encoding.rb +3 -3
  109. data/lib/active_support/key_generator.rb +18 -1
  110. data/lib/active_support/locale/en.yml +1 -1
  111. data/lib/active_support/log_subscriber.rb +13 -3
  112. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  113. data/lib/active_support/message_encryptor.rb +8 -3
  114. data/lib/active_support/message_verifier.rb +46 -14
  115. data/lib/active_support/messages/metadata.rb +2 -2
  116. data/lib/active_support/multibyte/chars.rb +10 -11
  117. data/lib/active_support/multibyte/unicode.rb +0 -12
  118. data/lib/active_support/multibyte.rb +1 -1
  119. data/lib/active_support/notifications/fanout.rb +91 -65
  120. data/lib/active_support/notifications/instrumenter.rb +32 -15
  121. data/lib/active_support/notifications.rb +15 -21
  122. data/lib/active_support/number_helper/number_converter.rb +1 -3
  123. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  124. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  125. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  126. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  127. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  128. data/lib/active_support/number_helper.rb +0 -2
  129. data/lib/active_support/option_merger.rb +8 -16
  130. data/lib/active_support/ordered_hash.rb +1 -1
  131. data/lib/active_support/parameter_filter.rb +5 -0
  132. data/lib/active_support/per_thread_registry.rb +5 -0
  133. data/lib/active_support/railtie.rb +69 -19
  134. data/lib/active_support/reloader.rb +1 -1
  135. data/lib/active_support/rescuable.rb +2 -2
  136. data/lib/active_support/ruby_features.rb +7 -0
  137. data/lib/active_support/secure_compare_rotator.rb +1 -1
  138. data/lib/active_support/string_inquirer.rb +0 -2
  139. data/lib/active_support/subscriber.rb +7 -18
  140. data/lib/active_support/tagged_logging.rb +2 -2
  141. data/lib/active_support/test_case.rb +9 -21
  142. data/lib/active_support/testing/assertions.rb +35 -5
  143. data/lib/active_support/testing/deprecation.rb +52 -1
  144. data/lib/active_support/testing/isolation.rb +2 -2
  145. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  146. data/lib/active_support/testing/parallelization/server.rb +4 -0
  147. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  148. data/lib/active_support/testing/parallelization.rb +4 -0
  149. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  150. data/lib/active_support/testing/stream.rb +3 -5
  151. data/lib/active_support/testing/tagged_logging.rb +1 -1
  152. data/lib/active_support/testing/time_helpers.rb +13 -2
  153. data/lib/active_support/time_with_zone.rb +54 -13
  154. data/lib/active_support/values/time_zone.rb +30 -9
  155. data/lib/active_support/xml_mini/jdom.rb +1 -1
  156. data/lib/active_support/xml_mini/libxml.rb +5 -5
  157. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  158. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  159. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  160. data/lib/active_support/xml_mini/rexml.rb +1 -1
  161. data/lib/active_support/xml_mini.rb +5 -4
  162. data/lib/active_support.rb +17 -1
  163. metadata +26 -23
  164. data/lib/active_support/core_ext/marshal.rb +0 -26
  165. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -2,8 +2,18 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module ForkTracker # :nodoc:
5
+ module ModernCoreExt
6
+ def _fork
7
+ pid = super
8
+ if pid == 0
9
+ ForkTracker.check!
10
+ end
11
+ pid
12
+ end
13
+ end
14
+
5
15
  module CoreExt
6
- def fork(*)
16
+ def fork(...)
7
17
  if block_given?
8
18
  super do
9
19
  ForkTracker.check!
@@ -16,17 +26,11 @@ module ActiveSupport
16
26
  pid
17
27
  end
18
28
  end
19
- ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
20
29
  end
21
30
 
22
31
  module CoreExtPrivate
23
32
  include CoreExt
24
-
25
- private
26
- def fork(*)
27
- super
28
- end
29
- ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
33
+ private :fork
30
34
  end
31
35
 
32
36
  @pid = Process.pid
@@ -34,15 +38,18 @@ module ActiveSupport
34
38
 
35
39
  class << self
36
40
  def check!
37
- if @pid != Process.pid
41
+ new_pid = Process.pid
42
+ if @pid != new_pid
38
43
  @callbacks.each(&:call)
39
- @pid = Process.pid
44
+ @pid = new_pid
40
45
  end
41
46
  end
42
47
 
43
48
  def hook!
44
- if Process.respond_to?(:fork)
45
- ::Object.prepend(CoreExtPrivate)
49
+ if Process.respond_to?(:_fork) # Ruby 3.1+
50
+ ::Process.singleton_class.prepend(ModernCoreExt)
51
+ elsif Process.respond_to?(:fork)
52
+ ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
46
53
  ::Kernel.prepend(CoreExtPrivate)
47
54
  ::Kernel.singleton_class.prepend(CoreExt)
48
55
  ::Process.singleton_class.prepend(CoreExt)
@@ -7,10 +7,10 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
11
- MINOR = 1
12
- TINY = 4
13
- PRE = "2"
10
+ MAJOR = 7
11
+ MINOR = 0
12
+ TINY = 2
13
+ PRE = "3"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -65,7 +65,7 @@ module ActiveSupport
65
65
  self
66
66
  end
67
67
 
68
- def initialize(constructor = {})
68
+ def initialize(constructor = nil)
69
69
  if constructor.respond_to?(:to_hash)
70
70
  super()
71
71
  update(constructor)
@@ -73,6 +73,8 @@ module ActiveSupport
73
73
  hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
74
74
  self.default = hash.default if hash.default
75
75
  self.default_proc = hash.default_proc if hash.default_proc
76
+ elsif constructor.nil?
77
+ super()
76
78
  else
77
79
  super(constructor)
78
80
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module HtmlSafeTranslation # :nodoc:
5
+ extend self
6
+
7
+ def translate(key, **options)
8
+ if html_safe_translation_key?(key)
9
+ html_safe_options = html_escape_translation_options(options)
10
+ translation = I18n.translate(key, **html_safe_options)
11
+ html_safe_translation(translation)
12
+ else
13
+ I18n.translate(key, **options)
14
+ end
15
+ end
16
+
17
+ private
18
+ def html_safe_translation_key?(key)
19
+ /(?:_|\b)html\z/.match?(key)
20
+ end
21
+
22
+ def html_escape_translation_options(options)
23
+ options.each do |name, value|
24
+ unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
25
+ options[name] = ERB::Util.html_escape(value.to_s)
26
+ end
27
+ end
28
+ end
29
+
30
+ def i18n_option?(name)
31
+ (@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
32
+ end
33
+
34
+
35
+ def html_safe_translation(translation)
36
+ if translation.respond_to?(:map)
37
+ translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
38
+ else
39
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,6 +5,7 @@ require "active_support/core_ext/hash/except"
5
5
  require "active_support/core_ext/hash/slice"
6
6
  begin
7
7
  require "i18n"
8
+ require "i18n/backend/fallbacks"
8
9
  rescue LoadError => e
9
10
  $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
11
  raise e
@@ -77,7 +77,7 @@ module I18n
77
77
 
78
78
  def self.forward_raise_on_missing_translations_config(app)
79
79
  ActiveSupport.on_load(:action_view) do
80
- self.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
80
+ ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
81
  end
82
82
 
83
83
  ActiveSupport.on_load(:action_controller) do
@@ -16,13 +16,13 @@ module ActiveSupport
16
16
  # inflect.plural /^(ox)$/i, '\1\2en'
17
17
  # inflect.singular /^(ox)en/i, '\1'
18
18
  #
19
- # inflect.irregular 'octopus', 'octopi'
19
+ # inflect.irregular 'cactus', 'cacti'
20
20
  #
21
21
  # inflect.uncountable 'equipment'
22
22
  # end
23
23
  #
24
24
  # New rules are added at the top. So in the example above, the irregular
25
- # rule for octopus will now be the first of the pluralization and
25
+ # rule for cactus will now be the first of the pluralization and
26
26
  # singularization rules that is runs. This guarantees that your rules run
27
27
  # before any of the rules that may already have been loaded.
28
28
  class Inflections
@@ -64,6 +64,13 @@ module ActiveSupport
64
64
  @__instance__[locale] ||= new
65
65
  end
66
66
 
67
+ def self.instance_or_fallback(locale)
68
+ I18n.fallbacks[locale].each do |k|
69
+ return @__instance__[k] if @__instance__.key?(k)
70
+ end
71
+ instance(locale)
72
+ end
73
+
67
74
  attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
68
75
 
69
76
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
@@ -160,7 +167,7 @@ module ActiveSupport
160
167
  # regular expressions. You simply pass the irregular in singular and
161
168
  # plural form.
162
169
  #
163
- # irregular 'octopus', 'octopi'
170
+ # irregular 'cactus', 'cacti'
164
171
  # irregular 'person', 'people'
165
172
  def irregular(singular, plural)
166
173
  @uncountables.delete(singular)
@@ -215,15 +222,24 @@ module ActiveSupport
215
222
  # Clears the loaded inflections within a given scope (default is
216
223
  # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
217
224
  # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
218
- # <tt>:humans</tt>.
225
+ # <tt>:humans</tt>, <tt>:acronyms</tt>.
219
226
  #
220
227
  # clear :all
221
228
  # clear :plurals
222
229
  def clear(scope = :all)
223
230
  case scope
224
231
  when :all
225
- @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
226
- else
232
+ clear(:acronyms)
233
+ clear(:plurals)
234
+ clear(:singulars)
235
+ clear(:uncountables)
236
+ clear(:humans)
237
+ when :acronyms
238
+ @acronyms = {}
239
+ define_acronym_regex_patterns
240
+ when :uncountables
241
+ @uncountables = Uncountables.new
242
+ when :plurals, :singulars, :humans
227
243
  instance_variable_set "@#{scope}", []
228
244
  end
229
245
  end
@@ -248,7 +264,7 @@ module ActiveSupport
248
264
  if block_given?
249
265
  yield Inflections.instance(locale)
250
266
  else
251
- Inflections.instance(locale)
267
+ Inflections.instance_or_fallback(locale)
252
268
  end
253
269
  end
254
270
  end
@@ -68,13 +68,17 @@ module ActiveSupport
68
68
  # camelize(underscore('SSLError')) # => "SslError"
69
69
  def camelize(term, uppercase_first_letter = true)
70
70
  string = term.to_s
71
- if uppercase_first_letter
72
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
71
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
72
+ if !uppercase_first_letter || uppercase_first_letter == :lower
73
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
73
74
  else
74
- string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
75
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
76
+ end
77
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
78
+ word = $2
79
+ substituted = inflections.acronyms[word] || word.capitalize! || word
80
+ $1 ? "::#{substituted}" : substituted
75
81
  end
76
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/", "::")
78
82
  string
79
83
  end
80
84
 
@@ -90,11 +94,10 @@ module ActiveSupport
90
94
  #
91
95
  # camelize(underscore('SSLError')) # => "SslError"
92
96
  def underscore(camel_cased_word)
93
- return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
97
+ return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
94
98
  word = camel_cased_word.to_s.gsub("::", "/")
95
99
  word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
96
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
97
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
100
+ word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
98
101
  word.tr!("-", "_")
99
102
  word.downcase!
100
103
  word
@@ -106,7 +109,7 @@ module ActiveSupport
106
109
  #
107
110
  # * Applies human inflection rules to the argument.
108
111
  # * Deletes leading underscores, if any.
109
- # * Removes a "_id" suffix if present.
112
+ # * Removes an "_id" suffix if present.
110
113
  # * Replaces underscores with spaces, if any.
111
114
  # * Downcases all words except acronyms.
112
115
  # * Capitalizes the first word.
@@ -120,7 +123,7 @@ module ActiveSupport
120
123
  # humanize('author_id') # => "Author"
121
124
  # humanize('author_id', capitalize: false) # => "author"
122
125
  # humanize('_id') # => "Id"
123
- # humanize('author_id', keep_id_suffix: true) # => "Author Id"
126
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
124
127
  #
125
128
  # If "SSL" was defined to be an acronym:
126
129
  #
@@ -131,18 +134,22 @@ module ActiveSupport
131
134
 
132
135
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
133
136
 
134
- result.sub!(/\A_+/, "")
137
+ result.tr!("_", " ")
138
+ result.lstrip!
135
139
  unless keep_id_suffix
136
- result.delete_suffix!("_id")
140
+ result.delete_suffix!(" id")
137
141
  end
138
- result.tr!("_", " ")
139
142
 
140
- result.gsub!(/([a-z\d]*)/i) do |match|
141
- "#{inflections.acronyms[match.downcase] || match.downcase}"
143
+ result.gsub!(/([a-z\d]+)/i) do |match|
144
+ match.downcase!
145
+ inflections.acronyms[match] || match
142
146
  end
143
147
 
144
148
  if capitalize
145
- result.sub!(/\A\w/) { |match| match.upcase }
149
+ result.sub!(/\A\w/) do |match|
150
+ match.upcase!
151
+ match
152
+ end
146
153
  end
147
154
 
148
155
  result
@@ -270,38 +277,7 @@ module ActiveSupport
270
277
  # NameError is raised when the name is not in CamelCase or the constant is
271
278
  # unknown.
272
279
  def constantize(camel_cased_word)
273
- if camel_cased_word.blank? || !camel_cased_word.include?("::")
274
- Object.const_get(camel_cased_word)
275
- else
276
- names = camel_cased_word.split("::")
277
-
278
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
279
- Object.const_get(camel_cased_word) if names.empty?
280
-
281
- # Remove the first blank element in case of '::ClassName' notation.
282
- names.shift if names.size > 1 && names.first.empty?
283
-
284
- names.inject(Object) do |constant, name|
285
- if constant == Object
286
- constant.const_get(name)
287
- else
288
- candidate = constant.const_get(name)
289
- next candidate if constant.const_defined?(name, false)
290
- next candidate unless Object.const_defined?(name)
291
-
292
- # Go down the ancestors to check if it is owned directly. The check
293
- # stops when we reach Object or the end of ancestors tree.
294
- constant = constant.ancestors.inject(constant) do |const, ancestor|
295
- break const if ancestor == Object
296
- break ancestor if ancestor.const_defined?(name, false)
297
- const
298
- end
299
-
300
- # owner is in Object, so raise
301
- constant.const_get(name, false)
302
- end
303
- end
304
- end
280
+ Object.const_get(camel_cased_word)
305
281
  end
306
282
 
307
283
  # Tries to find a constant with the name specified in the argument string.
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
4
+
5
+ module ActiveSupport
6
+ module IsolatedExecutionState # :nodoc:
7
+ @isolation_level = :thread
8
+
9
+ Thread.attr_accessor :active_support_execution_state
10
+ Fiber.attr_accessor :active_support_execution_state
11
+
12
+ class << self
13
+ attr_reader :isolation_level
14
+
15
+ def isolation_level=(level)
16
+ unless %i(thread fiber).include?(level)
17
+ raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
18
+ end
19
+
20
+ if level != isolation_level
21
+ clear
22
+ singleton_class.alias_method(:current, "current_#{level}")
23
+ singleton_class.send(:private, :current)
24
+ @isolation_level = level
25
+ end
26
+ end
27
+
28
+ def unique_id
29
+ self[:__id__] ||= Object.new
30
+ end
31
+
32
+ def [](key)
33
+ current[key]
34
+ end
35
+
36
+ def []=(key, value)
37
+ current[key] = value
38
+ end
39
+
40
+ def key?(key)
41
+ current.key?(key)
42
+ end
43
+
44
+ def delete(key)
45
+ current.delete(key)
46
+ end
47
+
48
+ def clear
49
+ current.clear
50
+ end
51
+
52
+ private
53
+ def current_thread
54
+ Thread.current.active_support_execution_state ||= {}
55
+ end
56
+
57
+ def current_fiber
58
+ Fiber.current.active_support_execution_state ||= {}
59
+ end
60
+
61
+ alias_method :current, :current_thread
62
+ end
63
+ end
64
+ end
@@ -22,8 +22,8 @@ module ActiveSupport
22
22
  Encoding.json_encoder.new(options).encode(value)
23
23
  end
24
24
 
25
- module Encoding #:nodoc:
26
- class JSONGemEncoder #:nodoc:
25
+ module Encoding # :nodoc:
26
+ class JSONGemEncoder # :nodoc:
27
27
  attr_reader :options
28
28
 
29
29
  def initialize(options = nil)
@@ -51,7 +51,7 @@ module ActiveSupport
51
51
  ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
52
52
 
53
53
  # This class wraps all the strings we see and does the extra escaping
54
- class EscapedString < String #:nodoc:
54
+ class EscapedString < String # :nodoc:
55
55
  def to_json(*)
56
56
  if Encoding.escape_html_entities_in_json
57
57
  s = super
@@ -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
 
@@ -55,7 +55,7 @@ en:
55
55
  # Used in NumberHelper.number_to_currency()
56
56
  currency:
57
57
  format:
58
- # 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)
59
59
  format: "%u%n"
60
60
  unit: "$"
61
61
  # These six are to override number.format and are optional
@@ -114,9 +114,13 @@ module ActiveSupport
114
114
  def finish(name, id, payload)
115
115
  super if logger
116
116
  rescue => e
117
- if logger
118
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
119
- 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)
120
124
  end
121
125
 
122
126
  private
@@ -138,5 +142,11 @@ module ActiveSupport
138
142
  bold = bold ? BOLD : ""
139
143
  "#{bold}#{color}#{text}#{CLEAR}"
140
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
141
151
  end
142
152
  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,25 +17,20 @@ module ActiveSupport
21
17
  EOT
22
18
  end
23
19
 
24
- def local_log_id
25
- Fiber.current.__id__
26
- end
27
-
28
20
  def local_level
29
- self.class.local_levels[local_log_id]
21
+ IsolatedExecutionState[:logger_thread_safe_level]
30
22
  end
31
23
 
32
24
  def local_level=(level)
33
25
  case level
34
26
  when Integer
35
- self.class.local_levels[local_log_id] = level
36
27
  when Symbol
37
- self.class.local_levels[local_log_id] = Logger::Severity.const_get(level.to_s.upcase)
28
+ level = Logger::Severity.const_get(level.to_s.upcase)
38
29
  when nil
39
- self.class.local_levels.delete(local_log_id)
40
30
  else
41
31
  raise ArgumentError, "Invalid log level: #{level.inspect}"
42
32
  end
33
+ IsolatedExecutionState[:logger_thread_safe_level] = level
43
34
  end
44
35
 
45
36
  def level
@@ -56,7 +47,7 @@ module ActiveSupport
56
47
 
57
48
  # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
58
49
  # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
59
- def add(severity, message = nil, progname = nil, &block) #:nodoc:
50
+ def add(severity, message = nil, progname = nil, &block) # :nodoc:
60
51
  severity ||= UNKNOWN
61
52
  progname ||= @progname
62
53
 
@@ -22,6 +22,11 @@ module ActiveSupport
22
22
  # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
23
23
  # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
24
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
25
30
  #
26
31
  # === Confining messages to a specific purpose
27
32
  #
@@ -84,7 +89,7 @@ module ActiveSupport
84
89
  cattr_accessor :use_authenticated_message_encryption, instance_accessor: false, default: false
85
90
 
86
91
  class << self
87
- def default_cipher #:nodoc:
92
+ def default_cipher # :nodoc:
88
93
  if use_authenticated_message_encryption
89
94
  "aes-256-gcm"
90
95
  else
@@ -93,7 +98,7 @@ module ActiveSupport
93
98
  end
94
99
  end
95
100
 
96
- module NullSerializer #:nodoc:
101
+ module NullSerializer # :nodoc:
97
102
  def self.load(value)
98
103
  value
99
104
  end
@@ -103,7 +108,7 @@ module ActiveSupport
103
108
  end
104
109
  end
105
110
 
106
- module NullVerifier #:nodoc:
111
+ module NullVerifier # :nodoc:
107
112
  def self.verify(value)
108
113
  value
109
114
  end