activesupport 7.0.0 → 7.2.2.1

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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +156 -255
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +3 -1
  7. data/lib/active_support/backtrace_cleaner.rb +41 -9
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +49 -17
  14. data/lib/active_support/cache/mem_cache_store.rb +111 -129
  15. data/lib/active_support/cache/memory_store.rb +81 -26
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +175 -154
  18. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +31 -13
  20. data/lib/active_support/cache.rb +457 -377
  21. data/lib/active_support/callbacks.rb +123 -139
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +12 -2
  27. data/lib/active_support/core_ext/array/conversions.rb +7 -9
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +0 -1
  30. data/lib/active_support/core_ext/class/subclasses.rb +4 -15
  31. data/lib/active_support/core_ext/date/blank.rb +4 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +20 -5
  33. data/lib/active_support/core_ext/date/conversions.rb +15 -16
  34. data/lib/active_support/core_ext/date.rb +0 -1
  35. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  36. data/lib/active_support/core_ext/date_and_time/compatibility.rb +29 -2
  37. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  38. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +15 -15
  40. data/lib/active_support/core_ext/date_time.rb +0 -1
  41. data/lib/active_support/core_ext/digest/uuid.rb +7 -10
  42. data/lib/active_support/core_ext/enumerable.rb +51 -101
  43. data/lib/active_support/core_ext/erb/util.rb +201 -0
  44. data/lib/active_support/core_ext/file/atomic.rb +2 -0
  45. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  46. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +7 -7
  50. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  51. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  52. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  53. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  54. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +38 -20
  55. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  56. data/lib/active_support/core_ext/module/delegation.rb +20 -119
  57. data/lib/active_support/core_ext/module/deprecation.rb +12 -12
  58. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  59. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  60. data/lib/active_support/core_ext/numeric/conversions.rb +77 -75
  61. data/lib/active_support/core_ext/numeric.rb +0 -1
  62. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  63. data/lib/active_support/core_ext/object/blank.rb +45 -1
  64. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  65. data/lib/active_support/core_ext/object/duplicable.rb +25 -16
  66. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  67. data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
  68. data/lib/active_support/core_ext/object/json.rb +17 -7
  69. data/lib/active_support/core_ext/object/to_query.rb +0 -2
  70. data/lib/active_support/core_ext/object/with.rb +46 -0
  71. data/lib/active_support/core_ext/object/with_options.rb +9 -9
  72. data/lib/active_support/core_ext/object.rb +1 -0
  73. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  74. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  75. data/lib/active_support/core_ext/pathname.rb +1 -0
  76. data/lib/active_support/core_ext/range/conversions.rb +32 -11
  77. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  78. data/lib/active_support/core_ext/range.rb +1 -2
  79. data/lib/active_support/core_ext/securerandom.rb +2 -6
  80. data/lib/active_support/core_ext/string/conversions.rb +3 -3
  81. data/lib/active_support/core_ext/string/filters.rb +21 -15
  82. data/lib/active_support/core_ext/string/indent.rb +1 -1
  83. data/lib/active_support/core_ext/string/inflections.rb +16 -9
  84. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  85. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  86. data/lib/active_support/core_ext/string/output_safety.rb +39 -150
  87. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  88. data/lib/active_support/core_ext/time/calculations.rb +42 -32
  89. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  90. data/lib/active_support/core_ext/time/conversions.rb +13 -15
  91. data/lib/active_support/core_ext/time/zones.rb +8 -9
  92. data/lib/active_support/core_ext/time.rb +0 -1
  93. data/lib/active_support/core_ext.rb +0 -1
  94. data/lib/active_support/current_attributes.rb +53 -46
  95. data/lib/active_support/deep_mergeable.rb +53 -0
  96. data/lib/active_support/delegation.rb +202 -0
  97. data/lib/active_support/dependencies/autoload.rb +9 -16
  98. data/lib/active_support/deprecation/behaviors.rb +65 -42
  99. data/lib/active_support/deprecation/constant_accessor.rb +47 -25
  100. data/lib/active_support/deprecation/deprecators.rb +104 -0
  101. data/lib/active_support/deprecation/disallowed.rb +6 -8
  102. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  103. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
  104. data/lib/active_support/deprecation/reporting.rb +49 -27
  105. data/lib/active_support/deprecation.rb +39 -9
  106. data/lib/active_support/deprecator.rb +7 -0
  107. data/lib/active_support/descendants_tracker.rb +66 -175
  108. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  109. data/lib/active_support/duration/iso8601_serializer.rb +1 -4
  110. data/lib/active_support/duration.rb +13 -7
  111. data/lib/active_support/encrypted_configuration.rb +63 -10
  112. data/lib/active_support/encrypted_file.rb +29 -13
  113. data/lib/active_support/environment_inquirer.rb +22 -2
  114. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  115. data/lib/active_support/error_reporter.rb +160 -36
  116. data/lib/active_support/evented_file_update_checker.rb +19 -7
  117. data/lib/active_support/execution_wrapper.rb +23 -28
  118. data/lib/active_support/file_update_checker.rb +5 -3
  119. data/lib/active_support/fork_tracker.rb +4 -32
  120. data/lib/active_support/gem_version.rb +4 -4
  121. data/lib/active_support/gzip.rb +2 -0
  122. data/lib/active_support/hash_with_indifferent_access.rb +41 -25
  123. data/lib/active_support/html_safe_translation.rb +19 -6
  124. data/lib/active_support/i18n.rb +1 -1
  125. data/lib/active_support/i18n_railtie.rb +20 -13
  126. data/lib/active_support/inflector/inflections.rb +2 -0
  127. data/lib/active_support/inflector/methods.rb +28 -18
  128. data/lib/active_support/inflector/transliterate.rb +4 -2
  129. data/lib/active_support/isolated_execution_state.rb +39 -19
  130. data/lib/active_support/json/decoding.rb +2 -1
  131. data/lib/active_support/json/encoding.rb +25 -43
  132. data/lib/active_support/key_generator.rb +13 -5
  133. data/lib/active_support/lazy_load_hooks.rb +33 -7
  134. data/lib/active_support/locale/en.yml +2 -0
  135. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  136. data/lib/active_support/log_subscriber.rb +76 -36
  137. data/lib/active_support/logger.rb +22 -60
  138. data/lib/active_support/logger_thread_safe_level.rb +10 -32
  139. data/lib/active_support/message_encryptor.rb +200 -55
  140. data/lib/active_support/message_encryptors.rb +141 -0
  141. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  142. data/lib/active_support/message_pack/extensions.rb +305 -0
  143. data/lib/active_support/message_pack/serializer.rb +63 -0
  144. data/lib/active_support/message_pack.rb +50 -0
  145. data/lib/active_support/message_verifier.rb +220 -89
  146. data/lib/active_support/message_verifiers.rb +135 -0
  147. data/lib/active_support/messages/codec.rb +65 -0
  148. data/lib/active_support/messages/metadata.rb +111 -45
  149. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  150. data/lib/active_support/messages/rotator.rb +34 -32
  151. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  152. data/lib/active_support/multibyte/chars.rb +4 -2
  153. data/lib/active_support/multibyte/unicode.rb +9 -37
  154. data/lib/active_support/notifications/fanout.rb +248 -87
  155. data/lib/active_support/notifications/instrumenter.rb +93 -25
  156. data/lib/active_support/notifications.rb +38 -31
  157. data/lib/active_support/number_helper/number_converter.rb +16 -7
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  159. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  160. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  161. data/lib/active_support/number_helper.rb +379 -317
  162. data/lib/active_support/option_merger.rb +4 -4
  163. data/lib/active_support/ordered_hash.rb +3 -3
  164. data/lib/active_support/ordered_options.rb +68 -16
  165. data/lib/active_support/parameter_filter.rb +103 -84
  166. data/lib/active_support/proxy_object.rb +8 -3
  167. data/lib/active_support/railtie.rb +30 -25
  168. data/lib/active_support/reloader.rb +13 -5
  169. data/lib/active_support/rescuable.rb +12 -10
  170. data/lib/active_support/secure_compare_rotator.rb +17 -10
  171. data/lib/active_support/string_inquirer.rb +4 -2
  172. data/lib/active_support/subscriber.rb +10 -27
  173. data/lib/active_support/syntax_error_proxy.rb +60 -0
  174. data/lib/active_support/tagged_logging.rb +64 -25
  175. data/lib/active_support/test_case.rb +160 -7
  176. data/lib/active_support/testing/assertions.rb +29 -13
  177. data/lib/active_support/testing/autorun.rb +0 -2
  178. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  179. data/lib/active_support/testing/deprecation.rb +20 -27
  180. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  181. data/lib/active_support/testing/isolation.rb +46 -33
  182. data/lib/active_support/testing/method_call_assertions.rb +7 -8
  183. data/lib/active_support/testing/parallelization/server.rb +3 -0
  184. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  185. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  186. data/lib/active_support/testing/stream.rb +1 -1
  187. data/lib/active_support/testing/strict_warnings.rb +43 -0
  188. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  189. data/lib/active_support/testing/time_helpers.rb +38 -16
  190. data/lib/active_support/time_with_zone.rb +28 -54
  191. data/lib/active_support/values/time_zone.rb +26 -15
  192. data/lib/active_support/version.rb +1 -1
  193. data/lib/active_support/xml_mini/jdom.rb +3 -10
  194. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  195. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  196. data/lib/active_support/xml_mini/rexml.rb +1 -1
  197. data/lib/active_support/xml_mini.rb +13 -4
  198. data/lib/active_support.rb +15 -3
  199. metadata +142 -21
  200. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  201. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
  202. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
  203. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  204. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
  205. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
  206. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  207. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
  208. data/lib/active_support/core_ext/uri.rb +0 -5
  209. data/lib/active_support/deprecation/instance_delegator.rb +0 -38
  210. data/lib/active_support/per_thread_registry.rb +0 -65
  211. data/lib/active_support/ruby_features.rb +0 -7
@@ -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,27 +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
43
+ end
44
+
45
+ def key?(key)
46
+ state.key?(key)
47
+ end
48
+
49
+ def delete(key)
50
+ state.delete(key)
38
51
  end
39
52
 
40
53
  def clear
41
- current.clear
54
+ state.clear
42
55
  end
43
56
 
44
- private
45
- def current_thread
46
- Thread.current.active_support_execution_state ||= {}
47
- end
57
+ def context
58
+ scope.current
59
+ end
48
60
 
49
- def current_fiber
50
- Fiber.current.active_support_execution_state ||= {}
51
- end
61
+ def share_with(other)
62
+ # Action Controller streaming spawns a new thread and copy thread locals.
63
+ # We do the same here for backward compatibility, but this is very much a hack
64
+ # and streaming should be rethought.
65
+ context.active_support_execution_state = other.active_support_execution_state.dup
66
+ end
52
67
 
53
- alias_method :current, :current_thread
68
+ private
69
+ def state
70
+ context.active_support_execution_state ||= {}
71
+ end
54
72
  end
73
+
74
+ self.isolation_level = :thread
55
75
  end
56
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
@@ -33,17 +35,23 @@ module ActiveSupport
33
35
  @hash_digest_class = options[:hash_digest_class] || self.class.hash_digest_class
34
36
  end
35
37
 
36
- # Returns a derived key suitable for use. The default key_size is chosen
38
+ # Returns a derived key suitable for use. The default +key_size+ is chosen
37
39
  # to be compatible with the default settings of ActiveSupport::MessageVerifier.
38
- # i.e. OpenSSL::Digest::SHA1#block_length
40
+ # i.e. <tt>OpenSSL::Digest::SHA1#block_length</tt>
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
- # re-executing the key generation process when it's called using the same salt and
46
- # key_size.
53
+ # re-executing the key generation process when it's called using the same +salt+ and
54
+ # +key_size+.
47
55
  class CachingKeyGenerator
48
56
  def initialize(key_generator)
49
57
  @key_generator = key_generator
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # lazy_load_hooks 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
- # Here is an example where +on_load+ method is called to register a hook.
13
+ # Here is an example where on_load method is called to register a hook.
12
14
  #
13
15
  # initializer 'active_record.initialize_timezone' do
14
16
  # ActiveSupport.on_load(:active_record) do
@@ -18,10 +20,26 @@ module ActiveSupport
18
20
  # end
19
21
  #
20
22
  # When the entirety of +ActiveRecord::Base+ has been
21
- # evaluated then +run_load_hooks+ is invoked. The very last line of
23
+ # evaluated then run_load_hooks is invoked. The very last line of
22
24
  # +ActiveRecord::Base+ is:
23
25
  #
24
26
  # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
27
+ #
28
+ # run_load_hooks will then execute all the hooks that were registered
29
+ # with the on_load method. In the case of the above example, it will
30
+ # execute the block of code that is in the +initializer+.
31
+ #
32
+ # Registering a hook that has already run results in that hook executing
33
+ # immediately. This allows hooks to be nested for code that relies on
34
+ # multiple lazily loaded components:
35
+ #
36
+ # initializer "action_text.renderer" do
37
+ # ActiveSupport.on_load(:action_controller_base) do
38
+ # ActiveSupport.on_load(:action_text_content) do
39
+ # self.default_renderer = Class.new(ActionController::Base).renderer
40
+ # end
41
+ # end
42
+ # end
25
43
  module LazyLoadHooks
26
44
  def self.extended(base) # :nodoc:
27
45
  base.class_eval do
@@ -31,8 +49,9 @@ module ActiveSupport
31
49
  end
32
50
  end
33
51
 
34
- # Declares a block that will be executed when a Rails component is fully
35
- # loaded.
52
+ # Declares a block that will be executed when a \Rails component is fully
53
+ # loaded. If the component has already loaded, the block is executed
54
+ # immediately.
36
55
  #
37
56
  # Options:
38
57
  #
@@ -46,6 +65,13 @@ module ActiveSupport
46
65
  @load_hooks[name] << [block, options]
47
66
  end
48
67
 
68
+ # Executes all blocks registered to +name+ via on_load, using +base+ as the
69
+ # evaluation context.
70
+ #
71
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
72
+ #
73
+ # In the case of the above example, it will execute all hooks registered
74
+ # for +:active_record+ within the class +ActiveRecord::Base+.
49
75
  def run_load_hooks(name, base = Object)
50
76
  @loaded[name] << base
51
77
  @load_hooks[name].each do |hook, options|
@@ -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"
@@ -27,13 +27,13 @@ module ActiveSupport
27
27
  #
28
28
  # All you need to do is to ensure that your log subscriber is added to
29
29
  # Rails::Subscriber, as in the second line of the code above. The test
30
- # helpers are responsible for setting up the queue, subscriptions and
30
+ # helpers are responsible for setting up the queue and subscriptions, and
31
31
  # turning colors in logs off.
32
32
  #
33
33
  # The messages are available in the @logger instance, which is a logger with
34
34
  # limited powers (it actually does not send anything to your output), and
35
35
  # you can collect them doing @logger.logged(level), where level is the level
36
- # used in logging, like info, debug, warn and so on.
36
+ # used in logging, like info, debug, warn, and so on.
37
37
  module TestHelper
38
38
  def setup # :nodoc:
39
39
  @logger = MockLogger.new
@@ -2,11 +2,15 @@
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/core_ext/class/attribute"
5
+ require "active_support/core_ext/enumerable"
5
6
  require "active_support/subscriber"
7
+ require "active_support/deprecation/proxy_wrappers"
6
8
 
7
9
  module ActiveSupport
8
- # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
- # <tt>ActiveSupport::Notifications</tt> with the sole purpose of logging them.
10
+ # = Active Support Log \Subscriber
11
+ #
12
+ # +ActiveSupport::LogSubscriber+ is an object set to consume
13
+ # ActiveSupport::Notifications with the sole purpose of logging them.
10
14
  # The log subscriber dispatches notifications to a registered object based
11
15
  # on its given namespace.
12
16
  #
@@ -15,29 +19,23 @@ module ActiveSupport
15
19
  #
16
20
  # module ActiveRecord
17
21
  # class LogSubscriber < ActiveSupport::LogSubscriber
22
+ # attach_to :active_record
23
+ #
18
24
  # def sql(event)
19
25
  # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
26
  # end
21
27
  # end
22
28
  # end
23
29
  #
24
- # And it's finally registered as:
25
- #
26
- # ActiveRecord::LogSubscriber.attach_to :active_record
30
+ # ActiveRecord::LogSubscriber.logger must be set as well, but it is assigned
31
+ # automatically in a \Rails environment.
27
32
  #
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.
33
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is
34
+ # published, it will properly dispatch the event
35
+ # (ActiveSupport::Notifications::Event) to the +sql+ method.
31
36
  #
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.
38
- #
39
- # Being an <tt>ActiveSupport::Notifications</tt> consumer,
40
- # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
37
+ # Being an ActiveSupport::Notifications consumer,
38
+ # +ActiveSupport::LogSubscriber+ exposes a simple interface to check if
41
39
  # instrumented code raises an exception. It is common to log a different
42
40
  # message in case of an error, and this can be achieved by extending
43
41
  # the previous example:
@@ -59,15 +57,20 @@ module ActiveSupport
59
57
  # end
60
58
  # end
61
59
  #
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.
60
+ # +ActiveSupport::LogSubscriber+ also has some helpers to deal with
61
+ # logging. For example, ActiveSupport::LogSubscriber.flush_all! will ensure
62
+ # that all logs are flushed, and it is called in Rails::Rack::Logger after a
63
+ # request finishes.
65
64
  class LogSubscriber < Subscriber
66
- # Embed in a String to clear all previous ANSI sequences.
67
- CLEAR = "\e[0m"
68
- BOLD = "\e[1m"
69
-
70
- # Colors
65
+ # ANSI sequence modes
66
+ MODES = {
67
+ clear: 0,
68
+ bold: 1,
69
+ italic: 3,
70
+ underline: 4,
71
+ }
72
+
73
+ # ANSI sequence colors
71
74
  BLACK = "\e[30m"
72
75
  RED = "\e[31m"
73
76
  GREEN = "\e[32m"
@@ -78,6 +81,13 @@ module ActiveSupport
78
81
  WHITE = "\e[37m"
79
82
 
80
83
  mattr_accessor :colorize_logging, default: true
84
+ class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:
85
+
86
+ LEVEL_CHECKS = {
87
+ debug: -> (logger) { !logger.debug? },
88
+ info: -> (logger) { !logger.info? },
89
+ error: -> (logger) { !logger.error? },
90
+ }
81
91
 
82
92
  class << self
83
93
  def logger
@@ -86,6 +96,12 @@ module ActiveSupport
86
96
  end
87
97
  end
88
98
 
99
+ def attach_to(...) # :nodoc:
100
+ result = super
101
+ set_event_levels
102
+ result
103
+ end
104
+
89
105
  attr_writer :logger
90
106
 
91
107
  def log_subscribers
@@ -101,20 +117,36 @@ module ActiveSupport
101
117
  def fetch_public_methods(subscriber, inherit_all)
102
118
  subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
119
  end
120
+
121
+ def set_event_levels
122
+ if subscriber
123
+ subscriber.event_levels = log_levels.transform_keys { |k| "#{k}.#{namespace}" }
124
+ end
125
+ end
126
+
127
+ def subscribe_log_level(method, level)
128
+ self.log_levels = log_levels.merge(method => LEVEL_CHECKS.fetch(level))
129
+ set_event_levels
130
+ end
131
+ end
132
+
133
+ def initialize
134
+ super
135
+ @event_levels = {}
104
136
  end
105
137
 
106
138
  def logger
107
139
  LogSubscriber.logger
108
140
  end
109
141
 
110
- def start(name, id, payload)
111
- super if logger
142
+ def silenced?(event)
143
+ logger.nil? || @event_levels[event]&.call(logger)
112
144
  end
113
145
 
114
- def finish(name, id, payload)
146
+ def call(event)
115
147
  super if logger
116
148
  rescue => e
117
- log_exception(name, e)
149
+ log_exception(event.name, e)
118
150
  end
119
151
 
120
152
  def publish_event(event)
@@ -123,6 +155,8 @@ module ActiveSupport
123
155
  log_exception(event.name, e)
124
156
  end
125
157
 
158
+ attr_writer :event_levels # :nodoc:
159
+
126
160
  private
127
161
  %w(info debug warn error fatal unknown).each do |level|
128
162
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -132,15 +166,21 @@ module ActiveSupport
132
166
  METHOD
133
167
  end
134
168
 
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:
169
+ # Set color by using a symbol or one of the defined constants. Set modes
170
+ # by specifying bold, italic, or underline options. Inspired by Highline,
171
+ # this method will automatically clear formatting at the end of the returned String.
172
+ def color(text, color, mode_options = {}) # :doc:
140
173
  return text unless colorize_logging
141
174
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
142
- bold = bold ? BOLD : ""
143
- "#{bold}#{color}#{text}#{CLEAR}"
175
+ mode = mode_from(mode_options)
176
+ clear = "\e[#{MODES[:clear]}m"
177
+ "#{mode}#{color}#{text}#{clear}"
178
+ end
179
+
180
+ def mode_from(options)
181
+ modes = MODES.values_at(*options.compact_blank.keys)
182
+
183
+ "\e[#{modes.join(";")}m" if modes.any?
144
184
  end
145
185
 
146
186
  def log_exception(name, e)
@@ -13,73 +13,26 @@ module ActiveSupport
13
13
  # logger = Logger.new(STDOUT)
14
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
15
  # # => true
16
+ #
17
+ # logger = Logger.new('/var/log/rails.log')
18
+ # ActiveSupport::Logger.logger_outputs_to?(logger, '/var/log/rails.log')
19
+ # # => true
16
20
  def self.logger_outputs_to?(logger, *sources)
17
- logdev = logger.instance_variable_get(:@logdev)
18
- logger_source = logdev.dev if logdev.respond_to?(:dev)
19
- sources.any? { |source| source == logger_source }
20
- end
21
-
22
- # Broadcasts logs to multiple loggers.
23
- def self.broadcast(logger) # :nodoc:
24
- Module.new do
25
- define_method(:add) do |*args, &block|
26
- logger.add(*args, &block)
27
- super(*args, &block)
28
- end
29
-
30
- define_method(:<<) do |x|
31
- logger << x
32
- super(x)
33
- end
34
-
35
- define_method(:close) do
36
- logger.close
37
- super()
38
- end
39
-
40
- define_method(:progname=) do |name|
41
- logger.progname = name
42
- super(name)
43
- end
44
-
45
- define_method(:formatter=) do |formatter|
46
- logger.formatter = formatter
47
- super(formatter)
48
- end
49
-
50
- define_method(:level=) do |level|
51
- logger.level = level
52
- super(level)
53
- end
21
+ loggers = if logger.is_a?(BroadcastLogger)
22
+ logger.broadcasts
23
+ else
24
+ [logger]
25
+ end
54
26
 
55
- define_method(:local_level=) do |level|
56
- logger.local_level = level if logger.respond_to?(:local_level=)
57
- super(level) if respond_to?(:local_level=)
58
- end
27
+ logdevs = loggers.map { |logger| logger.instance_variable_get(:@logdev) }
28
+ logger_sources = logdevs.filter_map { |logdev| logdev.try(:filename) || logdev.try(:dev) }
59
29
 
60
- define_method(:silence) do |level = Logger::ERROR, &block|
61
- if logger.respond_to?(:silence)
62
- logger.silence(level) do
63
- if defined?(super)
64
- super(level, &block)
65
- else
66
- block.call(self)
67
- end
68
- end
69
- else
70
- if defined?(super)
71
- super(level, &block)
72
- else
73
- block.call(self)
74
- end
75
- end
76
- end
77
- end
30
+ normalize_sources(sources).intersect?(normalize_sources(logger_sources))
78
31
  end
79
32
 
80
33
  def initialize(*args, **kwargs)
81
34
  super
82
- @formatter = SimpleFormatter.new
35
+ @formatter ||= SimpleFormatter.new
83
36
  end
84
37
 
85
38
  # Simple formatter which only displays the message.
@@ -89,5 +42,14 @@ module ActiveSupport
89
42
  "#{String === msg ? msg : msg.inspect}\n"
90
43
  end
91
44
  end
45
+
46
+ private
47
+ def self.normalize_sources(sources)
48
+ sources.map do |source|
49
+ source = source.path if source.respond_to?(:path)
50
+ source = File.realpath(source) if source.is_a?(String) && File.exist?(source)
51
+ source
52
+ end
53
+ end
92
54
  end
93
55
  end