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
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
+ # = Backtrace Cleaner
5
+ #
4
6
  # Backtraces often include many lines that are not relevant for the context
5
7
  # under review. This makes it hard to find the signal amongst the backtrace
6
8
  # noise, and adds debugging time. With a BacktraceCleaner, filters and
@@ -15,22 +17,24 @@ module ActiveSupport
15
17
  # can focus on the rest.
16
18
  #
17
19
  # bc = ActiveSupport::BacktraceCleaner.new
18
- # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
20
+ # root = "#{Rails.root}/"
21
+ # bc.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line } # strip the Rails.root prefix
19
22
  # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
20
23
  # bc.clean(exception.backtrace) # perform the cleanup
21
24
  #
22
- # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
25
+ # To reconfigure an existing BacktraceCleaner (like the default one in \Rails)
23
26
  # and show as much data as possible, you can always call
24
- # <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
27
+ # BacktraceCleaner#remove_silencers!, which will restore the
25
28
  # backtrace to a pristine state. If you need to reconfigure an existing
26
29
  # BacktraceCleaner so that it does not filter or modify the paths of any lines
27
- # of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
30
+ # of the backtrace, you can call BacktraceCleaner#remove_filters!
28
31
  # These two methods will give you a completely untouched backtrace.
29
32
  #
30
33
  # Inspired by the Quiet Backtrace gem by thoughtbot.
31
34
  class BacktraceCleaner
32
35
  def initialize
33
36
  @filters, @silencers = [], []
37
+ add_core_silencer
34
38
  add_gem_filter
35
39
  add_gem_silencer
36
40
  add_stdlib_silencer
@@ -52,11 +56,30 @@ module ActiveSupport
52
56
  end
53
57
  alias :filter :clean
54
58
 
59
+ # Returns the frame with all filters applied.
60
+ # returns +nil+ if the frame was silenced.
61
+ def clean_frame(frame, kind = :silent)
62
+ frame = frame.to_s
63
+ @filters.each do |f|
64
+ frame = f.call(frame.to_s)
65
+ end
66
+
67
+ case kind
68
+ when :silent
69
+ frame unless @silencers.any? { |s| s.call(frame) }
70
+ when :noise
71
+ frame if @silencers.any? { |s| s.call(frame) }
72
+ else
73
+ frame
74
+ end
75
+ end
76
+
55
77
  # Adds a filter from the block provided. Each line in the backtrace will be
56
78
  # mapped against this filter.
57
79
  #
58
- # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
59
- # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
80
+ # # Will turn "/my/rails/root/app/models/person.rb" into "app/models/person.rb"
81
+ # root = "#{Rails.root}/"
82
+ # backtrace_cleaner.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line }
60
83
  def add_filter(&block)
61
84
  @filters << block
62
85
  end
@@ -87,6 +110,11 @@ module ActiveSupport
87
110
  private
88
111
  FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
89
112
 
113
+ def initialize_copy(_other)
114
+ @filters = @filters.dup
115
+ @silencers = @silencers.dup
116
+ end
117
+
90
118
  def add_gem_filter
91
119
  gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
92
120
  return if gems_paths.empty?
@@ -96,6 +124,10 @@ module ActiveSupport
96
124
  add_filter { |line| line.sub(gems_regexp, gems_result) }
97
125
  end
98
126
 
127
+ def add_core_silencer
128
+ add_silencer { |line| line.include?("<internal:") }
129
+ end
130
+
99
131
  def add_gem_silencer
100
132
  add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
101
133
  end
@@ -106,7 +138,7 @@ module ActiveSupport
106
138
 
107
139
  def filter_backtrace(backtrace)
108
140
  @filters.each do |f|
109
- backtrace = backtrace.map { |line| f.call(line) }
141
+ backtrace = backtrace.map { |line| f.call(line.to_s) }
110
142
  end
111
143
 
112
144
  backtrace
@@ -114,7 +146,7 @@ module ActiveSupport
114
146
 
115
147
  def silence(backtrace)
116
148
  @silencers.each do |s|
117
- backtrace = backtrace.reject { |line| s.call(line) }
149
+ backtrace = backtrace.reject { |line| s.call(line.to_s) }
118
150
  end
119
151
 
120
152
  backtrace
@@ -123,7 +155,7 @@ module ActiveSupport
123
155
  def noise(backtrace)
124
156
  backtrace.select do |line|
125
157
  @silencers.any? do |s|
126
- s.call(line)
158
+ s.call(line.to_s)
127
159
  end
128
160
  end
129
161
  end
@@ -4,6 +4,7 @@ require "active_support/core_ext/benchmark"
4
4
  require "active_support/core_ext/hash/keys"
5
5
 
6
6
  module ActiveSupport
7
+ # = \Benchmarkable
7
8
  module Benchmarkable
8
9
  # Allows you to measure the execution time of a block in a template and
9
10
  # records the result to the log. Wrap this block around expensive operations
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # = Active Support Broadcast Logger
5
+ #
6
+ # The Broadcast logger is a logger used to write messages to multiple IO. It is commonly used
7
+ # in development to display messages on STDOUT and also write them to a file (development.log).
8
+ # With the Broadcast logger, you can broadcast your logs to a unlimited number of sinks.
9
+ #
10
+ # The BroadcastLogger acts as a standard logger and all methods you are used to are available.
11
+ # However, all the methods on this logger will propagate and be delegated to the other loggers
12
+ # that are part of the broadcast.
13
+ #
14
+ # Broadcasting your logs.
15
+ #
16
+ # stdout_logger = Logger.new(STDOUT)
17
+ # file_logger = Logger.new("development.log")
18
+ # broadcast = BroadcastLogger.new(stdout_logger, file_logger)
19
+ #
20
+ # broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
21
+ #
22
+ # Add a logger to the broadcast.
23
+ #
24
+ # stdout_logger = Logger.new(STDOUT)
25
+ # broadcast = BroadcastLogger.new(stdout_logger)
26
+ # file_logger = Logger.new("development.log")
27
+ # broadcast.broadcast_to(file_logger)
28
+ #
29
+ # broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
30
+ #
31
+ # Modifying the log level for all broadcasted loggers.
32
+ #
33
+ # stdout_logger = Logger.new(STDOUT)
34
+ # file_logger = Logger.new("development.log")
35
+ # broadcast = BroadcastLogger.new(stdout_logger, file_logger)
36
+ #
37
+ # broadcast.level = Logger::FATAL # Modify the log level for the whole broadcast.
38
+ #
39
+ # Stop broadcasting log to a sink.
40
+ #
41
+ # stdout_logger = Logger.new(STDOUT)
42
+ # file_logger = Logger.new("development.log")
43
+ # broadcast = BroadcastLogger.new(stdout_logger, file_logger)
44
+ # broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
45
+ #
46
+ # broadcast.stop_broadcasting_to(file_logger)
47
+ # broadcast.info("Hello world!") # Writes the log *only* to STDOUT.
48
+ #
49
+ # At least one sink has to be part of the broadcast. Otherwise, your logs will not
50
+ # be written anywhere. For instance:
51
+ #
52
+ # broadcast = BroadcastLogger.new
53
+ # broadcast.info("Hello world") # The log message will appear nowhere.
54
+ #
55
+ # If you are adding a custom logger with custom methods to the broadcast,
56
+ # the `BroadcastLogger` will proxy them and return the raw value, or an array
57
+ # of raw values, depending on how many loggers in the broadcasts responded to
58
+ # the method:
59
+ #
60
+ # class MyLogger < ::Logger
61
+ # def loggable?
62
+ # true
63
+ # end
64
+ # end
65
+ #
66
+ # logger = BroadcastLogger.new
67
+ # logger.loggable? # => A NoMethodError exception is raised because no loggers in the broadcasts could respond.
68
+ #
69
+ # logger.broadcast_to(MyLogger.new(STDOUT))
70
+ # logger.loggable? # => true
71
+ # logger.broadcast_to(MyLogger.new(STDOUT))
72
+ # puts logger.broadcasts # => [MyLogger, MyLogger]
73
+ # logger.loggable? # [true, true]
74
+ class BroadcastLogger
75
+ include ActiveSupport::LoggerSilence
76
+
77
+ # Returns all the logger that are part of this broadcast.
78
+ attr_reader :broadcasts
79
+ attr_reader :formatter
80
+ attr_accessor :progname
81
+
82
+ def initialize(*loggers)
83
+ @broadcasts = []
84
+ @progname = "Broadcast"
85
+
86
+ broadcast_to(*loggers)
87
+ end
88
+
89
+ # Add logger(s) to the broadcast.
90
+ #
91
+ # broadcast_logger = ActiveSupport::BroadcastLogger.new
92
+ # broadcast_logger.broadcast_to(Logger.new(STDOUT), Logger.new(STDERR))
93
+ def broadcast_to(*loggers)
94
+ @broadcasts.concat(loggers)
95
+ end
96
+
97
+ # Remove a logger from the broadcast. When a logger is removed, messages sent to
98
+ # the broadcast will no longer be written to its sink.
99
+ #
100
+ # sink = Logger.new(STDOUT)
101
+ # broadcast_logger = ActiveSupport::BroadcastLogger.new
102
+ #
103
+ # broadcast_logger.stop_broadcasting_to(sink)
104
+ def stop_broadcasting_to(logger)
105
+ @broadcasts.delete(logger)
106
+ end
107
+
108
+ def level
109
+ @broadcasts.map(&:level).min
110
+ end
111
+
112
+ def <<(message)
113
+ dispatch { |logger| logger.<<(message) }
114
+ end
115
+
116
+ def add(...)
117
+ dispatch { |logger| logger.add(...) }
118
+ end
119
+ alias_method :log, :add
120
+
121
+ def debug(...)
122
+ dispatch { |logger| logger.debug(...) }
123
+ end
124
+
125
+ def info(...)
126
+ dispatch { |logger| logger.info(...) }
127
+ end
128
+
129
+ def warn(...)
130
+ dispatch { |logger| logger.warn(...) }
131
+ end
132
+
133
+ def error(...)
134
+ dispatch { |logger| logger.error(...) }
135
+ end
136
+
137
+ def fatal(...)
138
+ dispatch { |logger| logger.fatal(...) }
139
+ end
140
+
141
+ def unknown(...)
142
+ dispatch { |logger| logger.unknown(...) }
143
+ end
144
+
145
+ def formatter=(formatter)
146
+ dispatch { |logger| logger.formatter = formatter }
147
+
148
+ @formatter = formatter
149
+ end
150
+
151
+ def level=(level)
152
+ dispatch { |logger| logger.level = level }
153
+ end
154
+ alias_method :sev_threshold=, :level=
155
+
156
+ def local_level=(level)
157
+ dispatch do |logger|
158
+ logger.local_level = level if logger.respond_to?(:local_level=)
159
+ end
160
+ end
161
+
162
+ def close
163
+ dispatch { |logger| logger.close }
164
+ end
165
+
166
+ # +True+ if the log level allows entries with severity Logger::DEBUG to be written
167
+ # to at least one broadcast. +False+ otherwise.
168
+ def debug?
169
+ @broadcasts.any? { |logger| logger.debug? }
170
+ end
171
+
172
+ # Sets the log level to Logger::DEBUG for the whole broadcast.
173
+ def debug!
174
+ dispatch { |logger| logger.debug! }
175
+ end
176
+
177
+ # +True+ if the log level allows entries with severity Logger::INFO to be written
178
+ # to at least one broadcast. +False+ otherwise.
179
+ def info?
180
+ @broadcasts.any? { |logger| logger.info? }
181
+ end
182
+
183
+ # Sets the log level to Logger::INFO for the whole broadcast.
184
+ def info!
185
+ dispatch { |logger| logger.info! }
186
+ end
187
+
188
+ # +True+ if the log level allows entries with severity Logger::WARN to be written
189
+ # to at least one broadcast. +False+ otherwise.
190
+ def warn?
191
+ @broadcasts.any? { |logger| logger.warn? }
192
+ end
193
+
194
+ # Sets the log level to Logger::WARN for the whole broadcast.
195
+ def warn!
196
+ dispatch { |logger| logger.warn! }
197
+ end
198
+
199
+ # +True+ if the log level allows entries with severity Logger::ERROR to be written
200
+ # to at least one broadcast. +False+ otherwise.
201
+ def error?
202
+ @broadcasts.any? { |logger| logger.error? }
203
+ end
204
+
205
+ # Sets the log level to Logger::ERROR for the whole broadcast.
206
+ def error!
207
+ dispatch { |logger| logger.error! }
208
+ end
209
+
210
+ # +True+ if the log level allows entries with severity Logger::FATAL to be written
211
+ # to at least one broadcast. +False+ otherwise.
212
+ def fatal?
213
+ @broadcasts.any? { |logger| logger.fatal? }
214
+ end
215
+
216
+ # Sets the log level to Logger::FATAL for the whole broadcast.
217
+ def fatal!
218
+ dispatch { |logger| logger.fatal! }
219
+ end
220
+
221
+ def initialize_copy(other)
222
+ @broadcasts = []
223
+ @progname = other.progname.dup
224
+ @formatter = other.formatter.dup
225
+
226
+ broadcast_to(*other.broadcasts.map(&:dup))
227
+ end
228
+
229
+ private
230
+ def dispatch(&block)
231
+ @broadcasts.each { |logger| block.call(logger) }
232
+ true
233
+ end
234
+
235
+ def method_missing(name, ...)
236
+ loggers = @broadcasts.select { |logger| logger.respond_to?(name) }
237
+
238
+ if loggers.none?
239
+ super
240
+ elsif loggers.one?
241
+ loggers.first.send(name, ...)
242
+ else
243
+ loggers.map { |logger| logger.send(name, ...) }
244
+ end
245
+ end
246
+
247
+ def respond_to_missing?(method, include_all)
248
+ @broadcasts.any? { |logger| logger.respond_to?(method, include_all) }
249
+ end
250
+ end
251
+ end
@@ -3,6 +3,6 @@
3
3
  begin
4
4
  require "builder"
5
5
  rescue LoadError => e
6
- $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
6
+ warn "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
7
7
  raise e
8
8
  end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "entry"
4
+
5
+ module ActiveSupport
6
+ module Cache
7
+ class Coder # :nodoc:
8
+ def initialize(serializer, compressor, legacy_serializer: false)
9
+ @serializer = serializer
10
+ @compressor = compressor
11
+ @legacy_serializer = legacy_serializer
12
+ end
13
+
14
+ def dump(entry)
15
+ return @serializer.dump(entry) if @legacy_serializer
16
+
17
+ dump_compressed(entry, Float::INFINITY)
18
+ end
19
+
20
+ def dump_compressed(entry, threshold)
21
+ return @serializer.dump_compressed(entry, threshold) if @legacy_serializer
22
+
23
+ # If value is a string with a supported encoding, use it as the payload
24
+ # instead of passing it through the serializer.
25
+ if type = type_for_string(entry.value)
26
+ payload = entry.value.b
27
+ else
28
+ type = OBJECT_DUMP_TYPE
29
+ payload = @serializer.dump(entry.value)
30
+ end
31
+
32
+ if compressed = try_compress(payload, threshold)
33
+ payload = compressed
34
+ type = type | COMPRESSED_FLAG
35
+ end
36
+
37
+ expires_at = entry.expires_at || -1.0
38
+
39
+ version = dump_version(entry.version) if entry.version
40
+ version_length = version&.bytesize || -1
41
+
42
+ packed = SIGNATURE.b
43
+ packed << [type, expires_at, version_length].pack(PACKED_TEMPLATE)
44
+ packed << version if version
45
+ packed << payload
46
+ end
47
+
48
+ def load(dumped)
49
+ return @serializer.load(dumped) if !signature?(dumped)
50
+
51
+ type = dumped.unpack1(PACKED_TYPE_TEMPLATE)
52
+ expires_at = dumped.unpack1(PACKED_EXPIRES_AT_TEMPLATE)
53
+ version_length = dumped.unpack1(PACKED_VERSION_LENGTH_TEMPLATE)
54
+
55
+ expires_at = nil if expires_at < 0
56
+ version = load_version(dumped.byteslice(PACKED_VERSION_INDEX, version_length)) if version_length >= 0
57
+ payload = dumped.byteslice((PACKED_VERSION_INDEX + [version_length, 0].max)..)
58
+
59
+ compressor = @compressor if type & COMPRESSED_FLAG > 0
60
+ serializer = STRING_DESERIALIZERS[type & ~COMPRESSED_FLAG] || @serializer
61
+
62
+ LazyEntry.new(serializer, compressor, payload, version: version, expires_at: expires_at)
63
+ end
64
+
65
+ private
66
+ SIGNATURE = "\x00\x11".b.freeze
67
+
68
+ OBJECT_DUMP_TYPE = 0x01
69
+
70
+ STRING_ENCODINGS = {
71
+ 0x02 => Encoding::UTF_8,
72
+ 0x03 => Encoding::BINARY,
73
+ 0x04 => Encoding::US_ASCII,
74
+ }
75
+
76
+ COMPRESSED_FLAG = 0x80
77
+
78
+ PACKED_TEMPLATE = "CEl<"
79
+ PACKED_TYPE_TEMPLATE = "@#{SIGNATURE.bytesize}C"
80
+ PACKED_EXPIRES_AT_TEMPLATE = "@#{[0].pack(PACKED_TYPE_TEMPLATE).bytesize}E"
81
+ PACKED_VERSION_LENGTH_TEMPLATE = "@#{[0].pack(PACKED_EXPIRES_AT_TEMPLATE).bytesize}l<"
82
+ PACKED_VERSION_INDEX = [0].pack(PACKED_VERSION_LENGTH_TEMPLATE).bytesize
83
+
84
+ MARSHAL_SIGNATURE = "\x04\x08".b.freeze
85
+
86
+ class StringDeserializer
87
+ def initialize(encoding)
88
+ @encoding = encoding
89
+ end
90
+
91
+ def load(payload)
92
+ payload.force_encoding(@encoding)
93
+ end
94
+ end
95
+
96
+ STRING_DESERIALIZERS = STRING_ENCODINGS.transform_values { |encoding| StringDeserializer.new(encoding) }
97
+
98
+ class LazyEntry < Cache::Entry
99
+ def initialize(serializer, compressor, payload, **options)
100
+ super(payload, **options)
101
+ @serializer = serializer
102
+ @compressor = compressor
103
+ @resolved = false
104
+ end
105
+
106
+ def value
107
+ if !@resolved
108
+ @value = @serializer.load(@compressor ? @compressor.inflate(@value) : @value)
109
+ @resolved = true
110
+ end
111
+ @value
112
+ end
113
+
114
+ def mismatched?(version)
115
+ super.tap { |mismatched| value if !mismatched }
116
+ rescue Cache::DeserializationError
117
+ true
118
+ end
119
+ end
120
+
121
+ def signature?(dumped)
122
+ dumped.is_a?(String) && dumped.start_with?(SIGNATURE)
123
+ end
124
+
125
+ def type_for_string(value)
126
+ STRING_ENCODINGS.key(value.encoding) if value.instance_of?(String)
127
+ end
128
+
129
+ def try_compress(string, threshold)
130
+ if @compressor && string.bytesize >= threshold
131
+ compressed = @compressor.deflate(string)
132
+ compressed if compressed.bytesize < string.bytesize
133
+ end
134
+ end
135
+
136
+ def dump_version(version)
137
+ if version.encoding != Encoding::UTF_8 || version.start_with?(MARSHAL_SIGNATURE)
138
+ Marshal.dump(version)
139
+ else
140
+ version.b
141
+ end
142
+ end
143
+
144
+ def load_version(dumped_version)
145
+ if dumped_version.start_with?(MARSHAL_SIGNATURE)
146
+ Marshal.load(dumped_version)
147
+ else
148
+ dumped_version.force_encoding(Encoding::UTF_8)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+
5
+ module ActiveSupport
6
+ module Cache
7
+ # This class is used to represent cache entries. Cache entries have a value, an optional
8
+ # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
9
+ # on the cache. The version is used to support the :version option on the cache for rejecting
10
+ # mismatches.
11
+ #
12
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
13
+ # using short instance variable names that are lazily defined.
14
+ class Entry # :nodoc:
15
+ class << self
16
+ def unpack(members)
17
+ new(members[0], expires_at: members[1], version: members[2])
18
+ end
19
+ end
20
+
21
+ attr_reader :version
22
+
23
+ # Creates a new cache entry for the specified value. Options supported are
24
+ # +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
25
+ def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
26
+ @value = value
27
+ @version = version
28
+ @created_at = 0.0
29
+ @expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
30
+ @compressed = true if compressed
31
+ end
32
+
33
+ def value
34
+ compressed? ? uncompress(@value) : @value
35
+ end
36
+
37
+ def mismatched?(version)
38
+ @version && version && @version != version
39
+ end
40
+
41
+ # Checks if the entry is expired. The +expires_in+ parameter can override
42
+ # the value set when the entry was created.
43
+ def expired?
44
+ @expires_in && @created_at + @expires_in <= Time.now.to_f
45
+ end
46
+
47
+ def expires_at
48
+ @expires_in ? @created_at + @expires_in : nil
49
+ end
50
+
51
+ def expires_at=(value)
52
+ if value
53
+ @expires_in = value.to_f - @created_at
54
+ else
55
+ @expires_in = nil
56
+ end
57
+ end
58
+
59
+ # Returns the size of the cached value. This could be less than
60
+ # <tt>value.bytesize</tt> if the data is compressed.
61
+ def bytesize
62
+ case value
63
+ when NilClass
64
+ 0
65
+ when String
66
+ @value.bytesize
67
+ else
68
+ @s ||= Marshal.dump(@value).bytesize
69
+ end
70
+ end
71
+
72
+ def compressed? # :nodoc:
73
+ defined?(@compressed)
74
+ end
75
+
76
+ def compressed(compress_threshold)
77
+ return self if compressed?
78
+
79
+ case @value
80
+ when nil, true, false, Numeric
81
+ uncompressed_size = 0
82
+ when String
83
+ uncompressed_size = @value.bytesize
84
+ else
85
+ serialized = Marshal.dump(@value)
86
+ uncompressed_size = serialized.bytesize
87
+ end
88
+
89
+ if uncompressed_size >= compress_threshold
90
+ serialized ||= Marshal.dump(@value)
91
+ compressed = Zlib::Deflate.deflate(serialized)
92
+
93
+ if compressed.bytesize < uncompressed_size
94
+ return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
95
+ end
96
+ end
97
+ self
98
+ end
99
+
100
+ def local?
101
+ false
102
+ end
103
+
104
+ # Duplicates the value in a class. This is used by cache implementations that don't natively
105
+ # serialize entries to protect against accidental cache modifications.
106
+ def dup_value!
107
+ if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
108
+ if @value.is_a?(String)
109
+ @value = @value.dup
110
+ else
111
+ @value = Marshal.load(Marshal.dump(@value))
112
+ end
113
+ end
114
+ end
115
+
116
+ def pack
117
+ members = [value, expires_at, version]
118
+ members.pop while !members.empty? && members.last.nil?
119
+ members
120
+ end
121
+
122
+ private
123
+ def uncompress(value)
124
+ marshal_load(Zlib::Inflate.inflate(value))
125
+ end
126
+
127
+ def marshal_load(payload)
128
+ Marshal.load(payload)
129
+ rescue ArgumentError => error
130
+ raise Cache::DeserializationError, error.message
131
+ end
132
+ end
133
+ end
134
+ end