activesupport 7.0.8.7 → 7.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +722 -314
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +25 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/builder.rb +1 -1
  10. data/lib/active_support/cache/coder.rb +153 -0
  11. data/lib/active_support/cache/entry.rb +128 -0
  12. data/lib/active_support/cache/file_store.rb +36 -9
  13. data/lib/active_support/cache/mem_cache_store.rb +84 -68
  14. data/lib/active_support/cache/memory_store.rb +76 -24
  15. data/lib/active_support/cache/null_store.rb +6 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +126 -131
  17. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +20 -8
  19. data/lib/active_support/cache.rb +304 -246
  20. data/lib/active_support/callbacks.rb +38 -18
  21. data/lib/active_support/concern.rb +4 -2
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  23. data/lib/active_support/concurrency/null_lock.rb +13 -0
  24. data/lib/active_support/configurable.rb +10 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  26. data/lib/active_support/core_ext/array.rb +0 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  28. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  29. data/lib/active_support/core_ext/date.rb +0 -1
  30. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  31. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  32. data/lib/active_support/core_ext/date_time.rb +0 -1
  33. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  34. data/lib/active_support/core_ext/enumerable.rb +3 -75
  35. data/lib/active_support/core_ext/erb/util.rb +196 -0
  36. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  39. data/lib/active_support/core_ext/module/delegation.rb +40 -11
  40. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  41. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  42. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  43. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  44. data/lib/active_support/core_ext/numeric.rb +0 -1
  45. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  46. data/lib/active_support/core_ext/object/duplicable.rb +15 -24
  47. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  48. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  49. data/lib/active_support/core_ext/object/json.rb +10 -2
  50. data/lib/active_support/core_ext/object/with.rb +44 -0
  51. data/lib/active_support/core_ext/object/with_options.rb +3 -3
  52. data/lib/active_support/core_ext/object.rb +1 -0
  53. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  54. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  55. data/lib/active_support/core_ext/pathname.rb +1 -0
  56. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  57. data/lib/active_support/core_ext/range/{overlaps.rb → overlap.rb} +5 -3
  58. data/lib/active_support/core_ext/range.rb +1 -2
  59. data/lib/active_support/core_ext/securerandom.rb +24 -12
  60. data/lib/active_support/core_ext/string/filters.rb +20 -14
  61. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  62. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  63. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  64. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  65. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  66. data/lib/active_support/core_ext/time/zones.rb +4 -4
  67. data/lib/active_support/core_ext/time.rb +0 -1
  68. data/lib/active_support/current_attributes.rb +15 -6
  69. data/lib/active_support/dependencies/autoload.rb +17 -12
  70. data/lib/active_support/deprecation/behaviors.rb +53 -32
  71. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  72. data/lib/active_support/deprecation/deprecators.rb +104 -0
  73. data/lib/active_support/deprecation/disallowed.rb +3 -5
  74. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  75. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  76. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  77. data/lib/active_support/deprecation/reporting.rb +35 -21
  78. data/lib/active_support/deprecation.rb +32 -5
  79. data/lib/active_support/deprecator.rb +7 -0
  80. data/lib/active_support/descendants_tracker.rb +104 -132
  81. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  82. data/lib/active_support/duration.rb +2 -1
  83. data/lib/active_support/encrypted_configuration.rb +30 -9
  84. data/lib/active_support/encrypted_file.rb +8 -3
  85. data/lib/active_support/environment_inquirer.rb +22 -2
  86. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  87. data/lib/active_support/error_reporter.rb +121 -35
  88. data/lib/active_support/execution_wrapper.rb +4 -4
  89. data/lib/active_support/file_update_checker.rb +4 -2
  90. data/lib/active_support/fork_tracker.rb +10 -2
  91. data/lib/active_support/gem_version.rb +4 -4
  92. data/lib/active_support/gzip.rb +2 -0
  93. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  94. data/lib/active_support/i18n.rb +1 -1
  95. data/lib/active_support/i18n_railtie.rb +20 -13
  96. data/lib/active_support/inflector/inflections.rb +2 -0
  97. data/lib/active_support/inflector/methods.rb +22 -10
  98. data/lib/active_support/inflector/transliterate.rb +3 -1
  99. data/lib/active_support/isolated_execution_state.rb +26 -22
  100. data/lib/active_support/json/decoding.rb +2 -1
  101. data/lib/active_support/json/encoding.rb +25 -43
  102. data/lib/active_support/key_generator.rb +9 -1
  103. data/lib/active_support/lazy_load_hooks.rb +6 -4
  104. data/lib/active_support/locale/en.yml +2 -0
  105. data/lib/active_support/log_subscriber.rb +78 -33
  106. data/lib/active_support/logger.rb +1 -1
  107. data/lib/active_support/logger_thread_safe_level.rb +9 -21
  108. data/lib/active_support/message_encryptor.rb +197 -53
  109. data/lib/active_support/message_encryptors.rb +140 -0
  110. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  111. data/lib/active_support/message_pack/extensions.rb +292 -0
  112. data/lib/active_support/message_pack/serializer.rb +63 -0
  113. data/lib/active_support/message_pack.rb +50 -0
  114. data/lib/active_support/message_verifier.rb +212 -93
  115. data/lib/active_support/message_verifiers.rb +134 -0
  116. data/lib/active_support/messages/codec.rb +65 -0
  117. data/lib/active_support/messages/metadata.rb +111 -45
  118. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  119. data/lib/active_support/messages/rotator.rb +34 -32
  120. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  121. data/lib/active_support/multibyte/chars.rb +2 -0
  122. data/lib/active_support/multibyte/unicode.rb +9 -37
  123. data/lib/active_support/notifications/fanout.rb +239 -81
  124. data/lib/active_support/notifications/instrumenter.rb +71 -14
  125. data/lib/active_support/notifications.rb +1 -1
  126. data/lib/active_support/number_helper/number_converter.rb +2 -2
  127. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  128. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  129. data/lib/active_support/ordered_hash.rb +3 -3
  130. data/lib/active_support/ordered_options.rb +14 -0
  131. data/lib/active_support/parameter_filter.rb +84 -69
  132. data/lib/active_support/proxy_object.rb +2 -0
  133. data/lib/active_support/railtie.rb +33 -21
  134. data/lib/active_support/reloader.rb +12 -4
  135. data/lib/active_support/rescuable.rb +2 -0
  136. data/lib/active_support/secure_compare_rotator.rb +16 -9
  137. data/lib/active_support/string_inquirer.rb +3 -1
  138. data/lib/active_support/subscriber.rb +9 -27
  139. data/lib/active_support/syntax_error_proxy.rb +49 -0
  140. data/lib/active_support/tagged_logging.rb +60 -24
  141. data/lib/active_support/test_case.rb +153 -6
  142. data/lib/active_support/testing/assertions.rb +25 -9
  143. data/lib/active_support/testing/autorun.rb +0 -2
  144. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  145. data/lib/active_support/testing/deprecation.rb +25 -25
  146. data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
  147. data/lib/active_support/testing/isolation.rb +1 -1
  148. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  149. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  150. data/lib/active_support/testing/stream.rb +1 -1
  151. data/lib/active_support/testing/strict_warnings.rb +38 -0
  152. data/lib/active_support/testing/time_helpers.rb +32 -14
  153. data/lib/active_support/time_with_zone.rb +4 -14
  154. data/lib/active_support/values/time_zone.rb +9 -7
  155. data/lib/active_support/version.rb +1 -1
  156. data/lib/active_support/xml_mini/jdom.rb +3 -10
  157. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  158. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  159. data/lib/active_support/xml_mini/rexml.rb +1 -1
  160. data/lib/active_support/xml_mini.rb +2 -2
  161. data/lib/active_support.rb +13 -3
  162. metadata +106 -21
  163. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  164. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  165. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  166. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  167. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  168. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  169. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  170. data/lib/active_support/core_ext/uri.rb +0 -5
  171. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -4,25 +4,30 @@ require "fiber"
4
4
 
5
5
  module ActiveSupport
6
6
  module IsolatedExecutionState # :nodoc:
7
- @isolation_level = :thread
7
+ @isolation_level = nil
8
8
 
9
9
  Thread.attr_accessor :active_support_execution_state
10
10
  Fiber.attr_accessor :active_support_execution_state
11
11
 
12
12
  class << self
13
- attr_reader :isolation_level
13
+ attr_reader :isolation_level, :scope
14
14
 
15
15
  def isolation_level=(level)
16
+ return if level == @isolation_level
17
+
16
18
  unless %i(thread fiber).include?(level)
17
19
  raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
18
20
  end
19
21
 
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
22
+ clear if @isolation_level
23
+
24
+ @scope =
25
+ case level
26
+ when :thread; Thread
27
+ when :fiber; Fiber
28
+ end
29
+
30
+ @isolation_level = level
26
31
  end
27
32
 
28
33
  def unique_id
@@ -30,43 +35,42 @@ module ActiveSupport
30
35
  end
31
36
 
32
37
  def [](key)
33
- current[key]
38
+ state[key]
34
39
  end
35
40
 
36
41
  def []=(key, value)
37
- current[key] = value
42
+ state[key] = value
38
43
  end
39
44
 
40
45
  def key?(key)
41
- current.key?(key)
46
+ state.key?(key)
42
47
  end
43
48
 
44
49
  def delete(key)
45
- current.delete(key)
50
+ state.delete(key)
46
51
  end
47
52
 
48
53
  def clear
49
- current.clear
54
+ state.clear
55
+ end
56
+
57
+ def context
58
+ scope.current
50
59
  end
51
60
 
52
61
  def share_with(other)
53
62
  # Action Controller streaming spawns a new thread and copy thread locals.
54
63
  # We do the same here for backward compatibility, but this is very much a hack
55
64
  # and streaming should be rethought.
56
- context = @isolation_level == :thread ? Thread.current : Fiber.current
57
65
  context.active_support_execution_state = other.active_support_execution_state.dup
58
66
  end
59
67
 
60
68
  private
61
- def current_thread
62
- Thread.current.active_support_execution_state ||= {}
63
- end
64
-
65
- def current_fiber
66
- Fiber.current.active_support_execution_state ||= {}
69
+ def state
70
+ context.active_support_execution_state ||= {}
67
71
  end
68
-
69
- alias_method :current, :current_thread
70
72
  end
73
+
74
+ self.isolation_level = :thread
71
75
  end
72
76
  end
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/delegation"
5
5
  require "json"
6
6
 
7
7
  module ActiveSupport
8
- # Look for and parse json strings that look like ISO 8601 times.
8
+ # Look for and parse JSON strings that look like ISO 8601 times.
9
9
  mattr_accessor :parse_json_times
10
10
 
11
11
  module JSON
@@ -28,6 +28,7 @@ module ActiveSupport
28
28
  data
29
29
  end
30
30
  end
31
+ alias_method :load, :decode
31
32
 
32
33
  # Returns the class of the error that will be raised when there is an
33
34
  # error in decoding JSON. Using this method means you won't directly
@@ -18,8 +18,11 @@ module ActiveSupport
18
18
  #
19
19
  # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
20
20
  # # => "{\"team\":\"rails\",\"players\":\"36\"}"
21
- def self.encode(value, options = nil)
22
- Encoding.json_encoder.new(options).encode(value)
21
+ class << self
22
+ def encode(value, options = nil)
23
+ Encoding.json_encoder.new(options).encode(value)
24
+ end
25
+ alias_method :dump, :encode
23
26
  end
24
27
 
25
28
  module Encoding # :nodoc:
@@ -32,49 +35,27 @@ module ActiveSupport
32
35
 
33
36
  # Encode the given object into a JSON string
34
37
  def encode(value)
35
- stringify jsonify value.as_json(options.dup)
36
- end
38
+ unless options.empty?
39
+ value = value.as_json(options.dup)
40
+ end
41
+ json = stringify(jsonify(value))
37
42
 
38
- private
39
43
  # Rails does more escaping than the JSON gem natively does (we
40
44
  # escape \u2028 and \u2029 and optionally >, <, & to work around
41
45
  # certain browser problems).
42
- ESCAPED_CHARS = {
43
- "\u2028" => '\u2028',
44
- "\u2029" => '\u2029',
45
- ">" => '\u003e',
46
- "<" => '\u003c',
47
- "&" => '\u0026',
48
- }
49
-
50
- ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
51
- ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
52
-
53
- # This class wraps all the strings we see and does the extra escaping
54
- class EscapedString < String # :nodoc:
55
- def to_json(*)
56
- if Encoding.escape_html_entities_in_json
57
- s = super
58
- s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
- s
60
- else
61
- s = super
62
- s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
- s
64
- end
65
- end
66
-
67
- def to_s
68
- self
69
- end
46
+ if Encoding.escape_html_entities_in_json
47
+ json.gsub!(">", '\u003e')
48
+ json.gsub!("<", '\u003c')
49
+ json.gsub!("&", '\u0026')
70
50
  end
51
+ json.gsub!("\u2028", '\u2028')
52
+ json.gsub!("\u2029", '\u2029')
53
+ json
54
+ end
71
55
 
72
- # Mark these as private so we don't leak encoding-specific constructs
73
- private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
74
- :ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
75
-
56
+ private
76
57
  # Convert an object into a "JSON-ready" representation composed of
77
- # primitives like Hash, Array, String, Numeric,
58
+ # primitives like Hash, Array, String, Symbol, Numeric,
78
59
  # and +true+/+false+/+nil+.
79
60
  # Recursively calls #as_json to the object to recursively build a
80
61
  # fully JSON-ready object.
@@ -88,14 +69,15 @@ module ActiveSupport
88
69
  # calls.
89
70
  def jsonify(value)
90
71
  case value
91
- when String
92
- EscapedString.new(value)
93
- when Numeric, NilClass, TrueClass, FalseClass
72
+ when String, Integer, Symbol, nil, true, false
73
+ value
74
+ when Numeric
94
75
  value.as_json
95
76
  when Hash
96
77
  result = {}
97
78
  value.each do |k, v|
98
- result[jsonify(k)] = jsonify(v)
79
+ k = k.to_s unless Symbol === k || String === k
80
+ result[k] = jsonify(v)
99
81
  end
100
82
  result
101
83
  when Array
@@ -124,7 +106,7 @@ module ActiveSupport
124
106
  # Defaults to 3 (equivalent to millisecond precision)
125
107
  attr_accessor :time_precision
126
108
 
127
- # Sets the encoder used by Rails to encode Ruby objects into JSON strings
109
+ # Sets the encoder used by \Rails to encode Ruby objects into JSON strings
128
110
  # in +Object#to_json+ and +ActiveSupport::JSON.encode+.
129
111
  attr_accessor :json_encoder
130
112
  end
@@ -4,9 +4,11 @@ require "concurrent/map"
4
4
  require "openssl"
5
5
 
6
6
  module ActiveSupport
7
+ # = Key Generator
8
+ #
7
9
  # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2.
8
10
  # It can be used to derive a number of keys for various purposes from a given secret.
9
- # This lets Rails applications have a single secure secret, but avoid reusing that
11
+ # This lets \Rails applications have a single secure secret, but avoid reusing that
10
12
  # key in multiple incompatible contexts.
11
13
  class KeyGenerator
12
14
  class << self
@@ -39,8 +41,14 @@ module ActiveSupport
39
41
  def generate_key(salt, key_size = 64)
40
42
  OpenSSL::PKCS5.pbkdf2_hmac(@secret, salt, @iterations, key_size, @hash_digest_class.new)
41
43
  end
44
+
45
+ def inspect # :nodoc:
46
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
47
+ end
42
48
  end
43
49
 
50
+ # = Caching Key Generator
51
+ #
44
52
  # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
45
53
  # re-executing the key generation process when it's called using the same +salt+ and
46
54
  # +key_size+.
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # LazyLoadHooks allows Rails to lazily load a lot of components and thus
4
+ # = Lazy Load Hooks
5
+ #
6
+ # LazyLoadHooks allows \Rails to lazily load a lot of components and thus
5
7
  # making the app boot faster. Because of this feature now there is no need to
6
- # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
8
+ # require +ActiveRecord::Base+ at boot time purely to apply
7
9
  # configuration. Instead a hook is registered that applies configuration once
8
- # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
10
+ # +ActiveRecord::Base+ is loaded. Here +ActiveRecord::Base+ is
9
11
  # used as example but this feature can be applied elsewhere too.
10
12
  #
11
13
  # Here is an example where on_load method is called to register a hook.
@@ -47,7 +49,7 @@ module ActiveSupport
47
49
  end
48
50
  end
49
51
 
50
- # Declares a block that will be executed when a Rails component is fully
52
+ # Declares a block that will be executed when a \Rails component is fully
51
53
  # loaded. If the component has already loaded, the block is executed
52
54
  # immediately.
53
55
  #
@@ -57,6 +57,7 @@ en:
57
57
  format:
58
58
  # Where is the currency sign? %u is the currency unit, %n is the number (default: $5.00)
59
59
  format: "%u%n"
60
+ negative_format: "-%u%n"
60
61
  unit: "$"
61
62
  # These six are to override number.format and are optional
62
63
  separator: "."
@@ -112,6 +113,7 @@ en:
112
113
  tb: "TB"
113
114
  pb: "PB"
114
115
  eb: "EB"
116
+ zb: "ZB"
115
117
  # Used in NumberHelper.number_to_human()
116
118
  decimal_units:
117
119
  format: "%n %u"
@@ -3,9 +3,12 @@
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/core_ext/class/attribute"
5
5
  require "active_support/subscriber"
6
+ require "active_support/deprecation/proxy_wrappers"
6
7
 
7
8
  module ActiveSupport
8
- # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
+ # = Active Support Log \Subscriber
10
+ #
11
+ # +ActiveSupport::LogSubscriber+ is an object set to consume
9
12
  # ActiveSupport::Notifications with the sole purpose of logging them.
10
13
  # The log subscriber dispatches notifications to a registered object based
11
14
  # on its given namespace.
@@ -15,29 +18,23 @@ module ActiveSupport
15
18
  #
16
19
  # module ActiveRecord
17
20
  # class LogSubscriber < ActiveSupport::LogSubscriber
21
+ # attach_to :active_record
22
+ #
18
23
  # def sql(event)
19
24
  # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
25
  # end
21
26
  # end
22
27
  # end
23
28
  #
24
- # And it's finally registered as:
25
- #
26
- # ActiveRecord::LogSubscriber.attach_to :active_record
29
+ # ActiveRecord::LogSubscriber.logger must be set as well, but it is assigned
30
+ # automatically in a \Rails environment.
27
31
  #
28
- # Since we need to know all instance methods before attaching the log
29
- # subscriber, the line above should be called after your
30
- # <tt>ActiveRecord::LogSubscriber</tt> definition.
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
- #
35
- # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
36
- # it will properly dispatch the event
37
- # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
32
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is
33
+ # published, it will properly dispatch the event
34
+ # (ActiveSupport::Notifications::Event) to the +sql+ method.
38
35
  #
39
36
  # Being an ActiveSupport::Notifications consumer,
40
- # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
37
+ # +ActiveSupport::LogSubscriber+ exposes a simple interface to check if
41
38
  # instrumented code raises an exception. It is common to log a different
42
39
  # message in case of an error, and this can be achieved by extending
43
40
  # the previous example:
@@ -59,15 +56,24 @@ module ActiveSupport
59
56
  # end
60
57
  # end
61
58
  #
62
- # Log subscriber also has some helpers to deal with logging and automatically
63
- # flushes all logs when the request finishes
64
- # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
59
+ # +ActiveSupport::LogSubscriber+ also has some helpers to deal with
60
+ # logging. For example, ActiveSupport::LogSubscriber.flush_all! will ensure
61
+ # that all logs are flushed, and it is called in Rails::Rack::Logger after a
62
+ # request finishes.
65
63
  class LogSubscriber < Subscriber
66
64
  # Embed in a String to clear all previous ANSI sequences.
67
- CLEAR = "\e[0m"
68
- BOLD = "\e[1m"
69
-
70
- # Colors
65
+ CLEAR = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[0m", "CLEAR is deprecated! Use MODES[:clear] instead.", ActiveSupport.deprecator)
66
+ BOLD = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[1m", "BOLD is deprecated! Use MODES[:bold] instead.", ActiveSupport.deprecator)
67
+
68
+ # ANSI sequence modes
69
+ MODES = {
70
+ clear: 0,
71
+ bold: 1,
72
+ italic: 3,
73
+ underline: 4,
74
+ }
75
+
76
+ # ANSI sequence colors
71
77
  BLACK = "\e[30m"
72
78
  RED = "\e[31m"
73
79
  GREEN = "\e[32m"
@@ -78,6 +84,7 @@ module ActiveSupport
78
84
  WHITE = "\e[37m"
79
85
 
80
86
  mattr_accessor :colorize_logging, default: true
87
+ class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:
81
88
 
82
89
  class << self
83
90
  def logger
@@ -86,6 +93,12 @@ module ActiveSupport
86
93
  end
87
94
  end
88
95
 
96
+ def attach_to(...) # :nodoc:
97
+ result = super
98
+ set_event_levels
99
+ result
100
+ end
101
+
89
102
  attr_writer :logger
90
103
 
91
104
  def log_subscribers
@@ -101,20 +114,36 @@ module ActiveSupport
101
114
  def fetch_public_methods(subscriber, inherit_all)
102
115
  subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
116
  end
117
+
118
+ def set_event_levels
119
+ if subscriber
120
+ subscriber.event_levels = log_levels.transform_keys { |k| "#{k}.#{namespace}" }
121
+ end
122
+ end
123
+
124
+ def subscribe_log_level(method, level)
125
+ self.log_levels = log_levels.merge(method => ::Logger.const_get(level.upcase))
126
+ set_event_levels
127
+ end
128
+ end
129
+
130
+ def initialize
131
+ super
132
+ @event_levels = {}
104
133
  end
105
134
 
106
135
  def logger
107
136
  LogSubscriber.logger
108
137
  end
109
138
 
110
- def start(name, id, payload)
111
- super if logger
139
+ def silenced?(event)
140
+ logger.nil? || logger.level > @event_levels.fetch(event, Float::INFINITY)
112
141
  end
113
142
 
114
- def finish(name, id, payload)
143
+ def call(event)
115
144
  super if logger
116
145
  rescue => e
117
- log_exception(name, e)
146
+ log_exception(event.name, e)
118
147
  end
119
148
 
120
149
  def publish_event(event)
@@ -123,6 +152,8 @@ module ActiveSupport
123
152
  log_exception(event.name, e)
124
153
  end
125
154
 
155
+ attr_writer :event_levels # :nodoc:
156
+
126
157
  private
127
158
  %w(info debug warn error fatal unknown).each do |level|
128
159
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -132,15 +163,29 @@ module ActiveSupport
132
163
  METHOD
133
164
  end
134
165
 
135
- # Set color by using a symbol or one of the defined constants. If a third
136
- # option is set to +true+, it also adds bold to the string. This is based
137
- # on the Highline implementation and will automatically append CLEAR to the
138
- # end of the returned String.
139
- def color(text, color, bold = false) # :doc:
166
+ # Set color by using a symbol or one of the defined constants. Set modes
167
+ # by specifying bold, italic, or underline options. Inspired by Highline,
168
+ # this method will automatically clear formatting at the end of the returned String.
169
+ def color(text, color, mode_options = {}) # :doc:
140
170
  return text unless colorize_logging
141
171
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
142
- bold = bold ? BOLD : ""
143
- "#{bold}#{color}#{text}#{CLEAR}"
172
+ mode = mode_from(mode_options)
173
+ clear = "\e[#{MODES[:clear]}m"
174
+ "#{mode}#{color}#{text}#{clear}"
175
+ end
176
+
177
+ def mode_from(options)
178
+ if options.is_a?(TrueClass) || options.is_a?(FalseClass)
179
+ ActiveSupport.deprecator.warn(<<~MSG.squish)
180
+ Bolding log text with a positional boolean is deprecated and will be removed
181
+ in Rails 7.2. Use an option hash instead (eg. `color("my text", :red, bold: true)`).
182
+ MSG
183
+ options = { bold: options }
184
+ end
185
+
186
+ modes = MODES.values_at(*options.compact_blank.keys)
187
+
188
+ "\e[#{modes.join(";")}m" if modes.any?
144
189
  end
145
190
 
146
191
  def log_exception(name, e)
@@ -79,7 +79,7 @@ module ActiveSupport
79
79
 
80
80
  def initialize(*args, **kwargs)
81
81
  super
82
- @formatter = SimpleFormatter.new
82
+ @formatter ||= SimpleFormatter.new
83
83
  end
84
84
 
85
85
  # Simple formatter which only displays the message.
@@ -18,7 +18,7 @@ module ActiveSupport
18
18
  end
19
19
 
20
20
  def local_level
21
- IsolatedExecutionState[:logger_thread_safe_level]
21
+ IsolatedExecutionState[local_level_key]
22
22
  end
23
23
 
24
24
  def local_level=(level)
@@ -30,7 +30,11 @@ module ActiveSupport
30
30
  else
31
31
  raise ArgumentError, "Invalid log level: #{level.inspect}"
32
32
  end
33
- IsolatedExecutionState[:logger_thread_safe_level] = level
33
+ if level.nil?
34
+ IsolatedExecutionState.delete(local_level_key)
35
+ else
36
+ IsolatedExecutionState[local_level_key] = level
37
+ end
34
38
  end
35
39
 
36
40
  def level
@@ -45,25 +49,9 @@ module ActiveSupport
45
49
  self.local_level = old_local_level
46
50
  end
47
51
 
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.
50
- def add(severity, message = nil, progname = nil, &block) # :nodoc:
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
52
+ private
53
+ def local_level_key
54
+ @local_level_key ||= :"logger_thread_safe_level_#{object_id}"
63
55
  end
64
-
65
- @logdev.write \
66
- format_message(format_severity(severity), Time.now, progname, message)
67
- end
68
56
  end
69
57
  end