activesupport 7.2.2.2 → 8.1.2

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +368 -152
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +73 -2
  5. data/lib/active_support/benchmark.rb +21 -0
  6. data/lib/active_support/benchmarkable.rb +3 -2
  7. data/lib/active_support/broadcast_logger.rb +61 -74
  8. data/lib/active_support/cache/file_store.rb +14 -4
  9. data/lib/active_support/cache/mem_cache_store.rb +27 -29
  10. data/lib/active_support/cache/memory_store.rb +11 -5
  11. data/lib/active_support/cache/null_store.rb +2 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +43 -34
  13. data/lib/active_support/cache/strategy/local_cache.rb +72 -27
  14. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  15. data/lib/active_support/cache.rb +88 -20
  16. data/lib/active_support/callbacks.rb +28 -13
  17. data/lib/active_support/class_attribute.rb +33 -0
  18. data/lib/active_support/code_generator.rb +9 -0
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  20. data/lib/active_support/concurrency/share_lock.rb +0 -1
  21. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  22. data/lib/active_support/configurable.rb +34 -0
  23. data/lib/active_support/configuration_file.rb +15 -6
  24. data/lib/active_support/continuous_integration.rb +145 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +3 -3
  26. data/lib/active_support/core_ext/array.rb +7 -7
  27. data/lib/active_support/core_ext/benchmark.rb +4 -14
  28. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  29. data/lib/active_support/core_ext/class/attribute.rb +26 -20
  30. data/lib/active_support/core_ext/class.rb +2 -2
  31. data/lib/active_support/core_ext/date/conversions.rb +2 -0
  32. data/lib/active_support/core_ext/date.rb +5 -5
  33. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
  34. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  35. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  36. data/lib/active_support/core_ext/date_time.rb +5 -5
  37. data/lib/active_support/core_ext/digest.rb +1 -1
  38. data/lib/active_support/core_ext/enumerable.rb +25 -8
  39. data/lib/active_support/core_ext/erb/util.rb +5 -5
  40. data/lib/active_support/core_ext/file.rb +1 -1
  41. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  42. data/lib/active_support/core_ext/hash/except.rb +0 -12
  43. data/lib/active_support/core_ext/hash.rb +8 -8
  44. data/lib/active_support/core_ext/integer.rb +3 -3
  45. data/lib/active_support/core_ext/kernel.rb +3 -3
  46. data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
  47. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  48. data/lib/active_support/core_ext/module.rb +11 -11
  49. data/lib/active_support/core_ext/numeric.rb +3 -3
  50. data/lib/active_support/core_ext/object/json.rb +24 -11
  51. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  52. data/lib/active_support/core_ext/object/try.rb +2 -2
  53. data/lib/active_support/core_ext/object.rb +13 -13
  54. data/lib/active_support/core_ext/pathname.rb +2 -2
  55. data/lib/active_support/core_ext/range/overlap.rb +3 -3
  56. data/lib/active_support/core_ext/range/sole.rb +17 -0
  57. data/lib/active_support/core_ext/range.rb +4 -4
  58. data/lib/active_support/core_ext/securerandom.rb +24 -8
  59. data/lib/active_support/core_ext/string/filters.rb +3 -3
  60. data/lib/active_support/core_ext/string/multibyte.rb +12 -3
  61. data/lib/active_support/core_ext/string/output_safety.rb +19 -12
  62. data/lib/active_support/core_ext/string.rb +13 -13
  63. data/lib/active_support/core_ext/symbol.rb +1 -1
  64. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  65. data/lib/active_support/core_ext/time/calculations.rb +7 -2
  66. data/lib/active_support/core_ext/time/compatibility.rb +2 -19
  67. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  68. data/lib/active_support/core_ext/time.rb +5 -5
  69. data/lib/active_support/core_ext.rb +1 -1
  70. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  71. data/lib/active_support/current_attributes.rb +27 -17
  72. data/lib/active_support/delegation.rb +25 -44
  73. data/lib/active_support/dependencies/interlock.rb +11 -5
  74. data/lib/active_support/dependencies.rb +6 -2
  75. data/lib/active_support/deprecation/reporting.rb +4 -21
  76. data/lib/active_support/deprecation.rb +1 -1
  77. data/lib/active_support/duration.rb +14 -10
  78. data/lib/active_support/editor.rb +70 -0
  79. data/lib/active_support/encrypted_configuration.rb +20 -2
  80. data/lib/active_support/error_reporter.rb +81 -4
  81. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  82. data/lib/active_support/event_reporter.rb +592 -0
  83. data/lib/active_support/evented_file_update_checker.rb +5 -2
  84. data/lib/active_support/execution_context.rb +64 -7
  85. data/lib/active_support/execution_wrapper.rb +1 -1
  86. data/lib/active_support/file_update_checker.rb +8 -6
  87. data/lib/active_support/gem_version.rb +3 -3
  88. data/lib/active_support/gzip.rb +1 -0
  89. data/lib/active_support/hash_with_indifferent_access.rb +61 -38
  90. data/lib/active_support/i18n_railtie.rb +19 -11
  91. data/lib/active_support/inflector/inflections.rb +32 -15
  92. data/lib/active_support/inflector/methods.rb +2 -2
  93. data/lib/active_support/inflector/transliterate.rb +6 -8
  94. data/lib/active_support/isolated_execution_state.rb +17 -17
  95. data/lib/active_support/json/decoding.rb +6 -4
  96. data/lib/active_support/json/encoding.rb +157 -21
  97. data/lib/active_support/lazy_load_hooks.rb +1 -1
  98. data/lib/active_support/log_subscriber.rb +2 -6
  99. data/lib/active_support/logger_thread_safe_level.rb +6 -3
  100. data/lib/active_support/message_encryptors.rb +54 -2
  101. data/lib/active_support/message_pack/extensions.rb +6 -1
  102. data/lib/active_support/message_verifier.rb +9 -0
  103. data/lib/active_support/message_verifiers.rb +57 -3
  104. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  105. data/lib/active_support/messages/rotator.rb +10 -0
  106. data/lib/active_support/multibyte/chars.rb +12 -2
  107. data/lib/active_support/multibyte.rb +4 -0
  108. data/lib/active_support/notifications/fanout.rb +64 -43
  109. data/lib/active_support/notifications/instrumenter.rb +1 -1
  110. data/lib/active_support/number_helper.rb +22 -0
  111. data/lib/active_support/railtie.rb +32 -9
  112. data/lib/active_support/structured_event_subscriber.rb +99 -0
  113. data/lib/active_support/subscriber.rb +0 -5
  114. data/lib/active_support/syntax_error_proxy.rb +7 -0
  115. data/lib/active_support/tagged_logging.rb +5 -0
  116. data/lib/active_support/test_case.rb +67 -6
  117. data/lib/active_support/testing/assertions.rb +115 -27
  118. data/lib/active_support/testing/autorun.rb +5 -0
  119. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  120. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  121. data/lib/active_support/testing/isolation.rb +0 -2
  122. data/lib/active_support/testing/notification_assertions.rb +92 -0
  123. data/lib/active_support/testing/parallelization/server.rb +15 -2
  124. data/lib/active_support/testing/parallelization/worker.rb +8 -4
  125. data/lib/active_support/testing/parallelization.rb +25 -1
  126. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  127. data/lib/active_support/testing/time_helpers.rb +9 -4
  128. data/lib/active_support/time_with_zone.rb +36 -23
  129. data/lib/active_support/values/time_zone.rb +19 -10
  130. data/lib/active_support/xml_mini.rb +3 -2
  131. data/lib/active_support.rb +21 -9
  132. metadata +34 -12
  133. data/lib/active_support/core_ext/range/each.rb +0 -24
  134. data/lib/active_support/proxy_object.rb +0 -20
  135. data/lib/active_support/testing/strict_warnings.rb +0 -43
data/README.rdoc CHANGED
@@ -35,6 +35,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
35
35
 
36
36
  * https://github.com/rails/rails/issues
37
37
 
38
- Feature requests should be discussed on the rails-core mailing list here:
38
+ Feature requests should be discussed on the rubyonrails-core forum here:
39
39
 
40
40
  * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -18,7 +18,7 @@ module ActiveSupport
18
18
  #
19
19
  # bc = ActiveSupport::BacktraceCleaner.new
20
20
  # root = "#{Rails.root}/"
21
- # bc.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line } # strip the Rails.root prefix
21
+ # bc.add_filter { |line| line.delete_prefix(root) } # strip the Rails.root prefix
22
22
  # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
23
23
  # bc.clean(exception.backtrace) # perform the cleanup
24
24
  #
@@ -56,6 +56,18 @@ module ActiveSupport
56
56
  end
57
57
  alias :filter :clean
58
58
 
59
+ # Given an array of Thread::Backtrace::Location objects, returns an array
60
+ # with the clean ones:
61
+ #
62
+ # clean_locations = backtrace_cleaner.clean_locations(caller_locations)
63
+ #
64
+ # Filters and silencers receive strings as usual. However, the +path+
65
+ # attributes of the locations in the returned array are the original,
66
+ # unfiltered ones, since locations are immutable.
67
+ def clean_locations(locations, kind = :silent)
68
+ locations.select { |location| clean_frame(location, kind) }
69
+ end
70
+
59
71
  # Returns the frame with all filters applied.
60
72
  # returns +nil+ if the frame was silenced.
61
73
  def clean_frame(frame, kind = :silent)
@@ -74,12 +86,71 @@ module ActiveSupport
74
86
  end
75
87
  end
76
88
 
89
+ # Thread.each_caller_location does not accept a start in Ruby < 3.4.
90
+ if Thread.method(:each_caller_location).arity == 0
91
+ # Returns the first clean frame of the caller's backtrace, or +nil+.
92
+ #
93
+ # Frames are strings.
94
+ def first_clean_frame(kind = :silent)
95
+ caller_location_skipped = false
96
+
97
+ Thread.each_caller_location do |location|
98
+ unless caller_location_skipped
99
+ caller_location_skipped = true
100
+ next
101
+ end
102
+
103
+ frame = clean_frame(location, kind)
104
+ return frame if frame
105
+ end
106
+ end
107
+
108
+ # Returns the first clean location of the caller's call stack, or +nil+.
109
+ #
110
+ # Locations are Thread::Backtrace::Location objects. Since they are
111
+ # immutable, their +path+ attributes are the original ones, but filters
112
+ # are applied internally so silencers can still rely on them.
113
+ def first_clean_location(kind = :silent)
114
+ caller_location_skipped = false
115
+
116
+ Thread.each_caller_location do |location|
117
+ unless caller_location_skipped
118
+ caller_location_skipped = true
119
+ next
120
+ end
121
+
122
+ return location if clean_frame(location, kind)
123
+ end
124
+ end
125
+ else
126
+ # Returns the first clean frame of the caller's backtrace, or +nil+.
127
+ #
128
+ # Frames are strings.
129
+ def first_clean_frame(kind = :silent)
130
+ Thread.each_caller_location(2) do |location|
131
+ frame = clean_frame(location, kind)
132
+ return frame if frame
133
+ end
134
+ end
135
+
136
+ # Returns the first clean location of the caller's call stack, or +nil+.
137
+ #
138
+ # Locations are Thread::Backtrace::Location objects. Since they are
139
+ # immutable, their +path+ attributes are the original ones, but filters
140
+ # are applied internally so silencers can still rely on them.
141
+ def first_clean_location(kind = :silent)
142
+ Thread.each_caller_location(2) do |location|
143
+ return location if clean_frame(location, kind)
144
+ end
145
+ end
146
+ end
147
+
77
148
  # Adds a filter from the block provided. Each line in the backtrace will be
78
149
  # mapped against this filter.
79
150
  #
80
151
  # # Will turn "/my/rails/root/app/models/person.rb" into "app/models/person.rb"
81
152
  # root = "#{Rails.root}/"
82
- # backtrace_cleaner.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line }
153
+ # backtrace_cleaner.add_filter { |line| line.delete_prefix(root) }
83
154
  def add_filter(&block)
84
155
  @filters << block
85
156
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Benchmark # :nodoc:
5
+ # Benchmark realtime in the specified time unit. By default,
6
+ # the returned unit is in seconds.
7
+ #
8
+ # ActiveSupport::Benchmark.realtime { sleep 0.1 }
9
+ # # => 0.10007
10
+ #
11
+ # ActiveSupport::Benchmark.realtime(:float_millisecond) { sleep 0.1 }
12
+ # # => 100.07
13
+ #
14
+ # `unit` can be any of the values accepted by Ruby's `Process.clock_gettime`.
15
+ def self.realtime(unit = :float_second, &block)
16
+ time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
17
+ yield
18
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit) - time_start
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/benchmark"
4
3
  require "active_support/core_ext/hash/keys"
5
4
 
6
5
  module ActiveSupport
@@ -41,7 +40,9 @@ module ActiveSupport
41
40
  options[:level] ||= :info
42
41
 
43
42
  result = nil
44
- ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
43
+ ms = ActiveSupport::Benchmark.realtime(:float_millisecond) do
44
+ result = options[:silence] ? logger.silence(&block) : yield
45
+ end
45
46
  logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
46
47
  result
47
48
  else
@@ -76,7 +76,6 @@ module ActiveSupport
76
76
 
77
77
  # Returns all the logger that are part of this broadcast.
78
78
  attr_reader :broadcasts
79
- attr_reader :formatter
80
79
  attr_accessor :progname
81
80
 
82
81
  def initialize(*loggers)
@@ -105,131 +104,119 @@ module ActiveSupport
105
104
  @broadcasts.delete(logger)
106
105
  end
107
106
 
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(...) }
107
+ def local_level=(level)
108
+ @broadcasts.each do |logger|
109
+ logger.local_level = level if logger.respond_to?(:local_level=)
110
+ end
143
111
  end
144
112
 
145
- def formatter=(formatter)
146
- dispatch { |logger| logger.formatter = formatter }
147
-
148
- @formatter = formatter
149
- end
113
+ def local_level
114
+ loggers = @broadcasts.select { |logger| logger.respond_to?(:local_level) }
150
115
 
151
- def level=(level)
152
- dispatch { |logger| logger.level = level }
116
+ loggers.map do |logger|
117
+ logger.local_level
118
+ end.first
153
119
  end
154
- alias_method :sev_threshold=, :level=
155
120
 
156
- def local_level=(level)
157
- dispatch do |logger|
158
- logger.local_level = level if logger.respond_to?(:local_level=)
159
- end
121
+ LOGGER_METHODS = %w[
122
+ << log add debug info warn error fatal unknown
123
+ level= sev_threshold= close
124
+ formatter formatter=
125
+ ] # :nodoc:
126
+ LOGGER_METHODS.each do |method|
127
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
128
+ def #{method}(...)
129
+ dispatch(:#{method}, ...)
130
+ end
131
+ RUBY
160
132
  end
161
133
 
162
- def close
163
- dispatch { |logger| logger.close }
134
+ # Returns the lowest level of all the loggers in the broadcast.
135
+ def level
136
+ @broadcasts.map(&:level).min
164
137
  end
165
138
 
166
- # +True+ if the log level allows entries with severity Logger::DEBUG to be written
167
- # to at least one broadcast. +False+ otherwise.
139
+ # True if the log level allows entries with severity +Logger::DEBUG+ to be written
140
+ # to at least one broadcast. False otherwise.
168
141
  def debug?
169
142
  @broadcasts.any? { |logger| logger.debug? }
170
143
  end
171
144
 
172
- # Sets the log level to Logger::DEBUG for the whole broadcast.
145
+ # Sets the log level to +Logger::DEBUG+ for the whole broadcast.
173
146
  def debug!
174
- dispatch { |logger| logger.debug! }
147
+ dispatch(:debug!)
175
148
  end
176
149
 
177
- # +True+ if the log level allows entries with severity Logger::INFO to be written
178
- # to at least one broadcast. +False+ otherwise.
150
+ # True if the log level allows entries with severity +Logger::INFO+ to be written
151
+ # to at least one broadcast. False otherwise.
179
152
  def info?
180
153
  @broadcasts.any? { |logger| logger.info? }
181
154
  end
182
155
 
183
- # Sets the log level to Logger::INFO for the whole broadcast.
156
+ # Sets the log level to +Logger::INFO+ for the whole broadcast.
184
157
  def info!
185
- dispatch { |logger| logger.info! }
158
+ dispatch(:info!)
186
159
  end
187
160
 
188
- # +True+ if the log level allows entries with severity Logger::WARN to be written
189
- # to at least one broadcast. +False+ otherwise.
161
+ # True if the log level allows entries with severity +Logger::WARN+ to be written
162
+ # to at least one broadcast. False otherwise.
190
163
  def warn?
191
164
  @broadcasts.any? { |logger| logger.warn? }
192
165
  end
193
166
 
194
- # Sets the log level to Logger::WARN for the whole broadcast.
167
+ # Sets the log level to +Logger::WARN+ for the whole broadcast.
195
168
  def warn!
196
- dispatch { |logger| logger.warn! }
169
+ dispatch(:warn!)
197
170
  end
198
171
 
199
- # +True+ if the log level allows entries with severity Logger::ERROR to be written
200
- # to at least one broadcast. +False+ otherwise.
172
+ # True if the log level allows entries with severity +Logger::ERROR+ to be written
173
+ # to at least one broadcast. False otherwise.
201
174
  def error?
202
175
  @broadcasts.any? { |logger| logger.error? }
203
176
  end
204
177
 
205
- # Sets the log level to Logger::ERROR for the whole broadcast.
178
+ # Sets the log level to +Logger::ERROR+ for the whole broadcast.
206
179
  def error!
207
- dispatch { |logger| logger.error! }
180
+ dispatch(:error!)
208
181
  end
209
182
 
210
- # +True+ if the log level allows entries with severity Logger::FATAL to be written
211
- # to at least one broadcast. +False+ otherwise.
183
+ # True if the log level allows entries with severity +Logger::FATAL+ to be written
184
+ # to at least one broadcast. False otherwise.
212
185
  def fatal?
213
186
  @broadcasts.any? { |logger| logger.fatal? }
214
187
  end
215
188
 
216
- # Sets the log level to Logger::FATAL for the whole broadcast.
189
+ # Sets the log level to +Logger::FATAL+ for the whole broadcast.
217
190
  def fatal!
218
- dispatch { |logger| logger.fatal! }
191
+ dispatch(:fatal!)
219
192
  end
220
193
 
221
194
  def initialize_copy(other)
222
195
  @broadcasts = []
223
196
  @progname = other.progname.dup
224
- @formatter = other.formatter.dup
225
197
 
226
198
  broadcast_to(*other.broadcasts.map(&:dup))
227
199
  end
228
200
 
229
201
  private
230
- def dispatch(&block)
231
- @broadcasts.each { |logger| block.call(logger) }
232
- true
202
+ def dispatch(method, *args, **kwargs, &block)
203
+ if block_given?
204
+ # Maintain semantics that the first logger yields the block
205
+ # as normal, but subsequent loggers won't re-execute the block.
206
+ # Instead, the initial result is immediately returned.
207
+ called, result = false, nil
208
+ block = proc { |*args, **kwargs|
209
+ if called then result
210
+ else
211
+ called = true
212
+ result = yield(*args, **kwargs)
213
+ end
214
+ }
215
+ end
216
+
217
+ @broadcasts.map { |logger|
218
+ logger.send(method, *args, **kwargs, &block)
219
+ }.first
233
220
  end
234
221
 
235
222
  def method_missing(name, ...)
@@ -57,8 +57,13 @@ module ActiveSupport
57
57
  # cache.write("baz", 5)
58
58
  # cache.increment("baz") # => 6
59
59
  #
60
- def increment(name, amount = 1, options = nil)
61
- modify_value(name, amount, options)
60
+ def increment(name, amount = 1, **options)
61
+ options = merged_options(options)
62
+ key = normalize_key(name, options)
63
+
64
+ instrument(:increment, key, amount: amount) do
65
+ modify_value(name, amount, options)
66
+ end
62
67
  end
63
68
 
64
69
  # Decrement a cached integer value. Returns the updated value.
@@ -72,8 +77,13 @@ module ActiveSupport
72
77
  # cache.write("baz", 5)
73
78
  # cache.decrement("baz") # => 4
74
79
  #
75
- def decrement(name, amount = 1, options = nil)
76
- modify_value(name, -amount, options)
80
+ def decrement(name, amount = 1, **options)
81
+ options = merged_options(options)
82
+ key = normalize_key(name, options)
83
+
84
+ instrument(:decrement, key, amount: amount) do
85
+ modify_value(name, -amount, options)
86
+ end
77
87
  end
78
88
 
79
89
  def delete_matched(matcher, options = nil)
@@ -41,7 +41,6 @@ module ActiveSupport
41
41
 
42
42
  prepend Strategy::LocalCache
43
43
 
44
- KEY_MAX_SIZE = 250
45
44
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
46
45
 
47
46
  # Creates a new Dalli::Client instance with specified addresses and options.
@@ -60,7 +59,7 @@ module ActiveSupport
60
59
  pool_options = retrieve_pool_options(options)
61
60
 
62
61
  if pool_options
63
- ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
62
+ ConnectionPool.new(**pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
64
63
  else
65
64
  Dalli::Client.new(addresses, options)
66
65
  end
@@ -80,6 +79,7 @@ module ActiveSupport
80
79
  if options.key?(:cache_nils)
81
80
  options[:skip_nil] = !options.delete(:cache_nils)
82
81
  end
82
+ options[:max_key_size] ||= MAX_KEY_SIZE
83
83
  super(options)
84
84
 
85
85
  unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
@@ -110,9 +110,6 @@ module ActiveSupport
110
110
  # * <tt>raw: true</tt> - Sends the value directly to the server as raw
111
111
  # bytes. The value must be a string or number. You can use memcached
112
112
  # direct operations like +increment+ and +decrement+ only on raw values.
113
- #
114
- # * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
115
- # entry.
116
113
 
117
114
  # Increment a cached integer value using the memcached incr atomic operator.
118
115
  # Returns the updated value.
@@ -129,6 +126,11 @@ module ActiveSupport
129
126
  #
130
127
  # Incrementing a non-numeric value, or a value written without
131
128
  # <tt>raw: true</tt>, will fail and return +nil+.
129
+ #
130
+ # To read the value later, call #read_counter:
131
+ #
132
+ # cache.increment("baz") # => 7
133
+ # cache.read_counter("baz") # 7
132
134
  def increment(name, amount = 1, options = nil)
133
135
  options = merged_options(options)
134
136
  key = normalize_key(name, options)
@@ -155,6 +157,11 @@ module ActiveSupport
155
157
  #
156
158
  # Decrementing a non-numeric value, or a value written without
157
159
  # <tt>raw: true</tt>, will fail and return +nil+.
160
+ #
161
+ # To read the value later, call #read_counter:
162
+ #
163
+ # cache.decrement("baz") # => 3
164
+ # cache.read_counter("baz") # 3
158
165
  def decrement(name, amount = 1, options = nil)
159
166
  options = merged_options(options)
160
167
  key = normalize_key(name, options)
@@ -212,26 +219,24 @@ module ActiveSupport
212
219
  def read_multi_entries(names, **options)
213
220
  keys_to_names = names.index_by { |name| normalize_key(name, options) }
214
221
 
215
- raw_values = begin
216
- @data.with { |c| c.get_multi(keys_to_names.keys) }
217
- rescue Dalli::UnmarshalError
218
- {}
219
- end
222
+ rescue_error_with({}) do
223
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
220
224
 
221
- values = {}
225
+ values = {}
222
226
 
223
- raw_values.each do |key, value|
224
- entry = deserialize_entry(value, raw: options[:raw])
227
+ raw_values.each do |key, value|
228
+ entry = deserialize_entry(value, raw: options[:raw])
225
229
 
226
- unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
227
- begin
228
- values[keys_to_names[key]] = entry.value
229
- rescue DeserializationError
230
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
231
+ begin
232
+ values[keys_to_names[key]] = entry.value
233
+ rescue DeserializationError
234
+ end
230
235
  end
231
236
  end
232
- end
233
237
 
234
- values
238
+ values
239
+ end
235
240
  end
236
241
 
237
242
  # Delete an entry from the cache.
@@ -251,19 +256,12 @@ module ActiveSupport
251
256
  # before applying the regular expression to ensure we are escaping all
252
257
  # characters properly.
253
258
  def normalize_key(key, options)
254
- key = super
259
+ key = expand_and_namespace_key(key, options)
255
260
  if key
256
261
  key = key.dup.force_encoding(Encoding::ASCII_8BIT)
257
262
  key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
258
-
259
- if key.size > KEY_MAX_SIZE
260
- key_separator = ":hash:"
261
- key_hash = ActiveSupport::Digest.hexdigest(key)
262
- key_trim_size = KEY_MAX_SIZE - key_separator.size - key_hash.size
263
- key = "#{key[0, key_trim_size]}#{key_separator}#{key_hash}"
264
- end
265
263
  end
266
- key
264
+ truncate_key(key)
267
265
  end
268
266
 
269
267
  def deserialize_entry(payload, raw: false, **)
@@ -276,7 +274,7 @@ module ActiveSupport
276
274
 
277
275
  def rescue_error_with(fallback)
278
276
  yield
279
- rescue Dalli::DalliError => error
277
+ rescue Dalli::DalliError, ConnectionPool::Error, ConnectionPool::TimeoutError => error
280
278
  logger.error("DalliError (#{error}): #{error.message}") if logger
281
279
  ActiveSupport.error_reporter&.report(
282
280
  error,
@@ -26,6 +26,8 @@ module ActiveSupport
26
26
  #
27
27
  # +MemoryStore+ is thread-safe.
28
28
  class MemoryStore < Store
29
+ prepend Strategy::LocalCache
30
+
29
31
  module DupCoder # :nodoc:
30
32
  extend self
31
33
 
@@ -146,8 +148,10 @@ module ActiveSupport
146
148
  # cache.write("baz", 5)
147
149
  # cache.increment("baz") # => 6
148
150
  #
149
- def increment(name, amount = 1, options = nil)
150
- modify_value(name, amount, options)
151
+ def increment(name, amount = 1, **options)
152
+ instrument(:increment, name, amount: amount) do
153
+ modify_value(name, amount, **options)
154
+ end
151
155
  end
152
156
 
153
157
  # Decrement a cached integer value. Returns the updated value.
@@ -161,8 +165,10 @@ module ActiveSupport
161
165
  # cache.write("baz", 5)
162
166
  # cache.decrement("baz") # => 4
163
167
  #
164
- def decrement(name, amount = 1, options = nil)
165
- modify_value(name, -amount, options)
168
+ def decrement(name, amount = 1, **options)
169
+ instrument(:decrement, name, amount: amount) do
170
+ modify_value(name, -amount, **options)
171
+ end
166
172
  end
167
173
 
168
174
  # Deletes cache entries if the cache key matches a given pattern.
@@ -234,7 +240,7 @@ module ActiveSupport
234
240
 
235
241
  # Modifies the amount of an integer value that is stored in the cache.
236
242
  # If the key is not found it is created and set to +amount+.
237
- def modify_value(name, amount, options)
243
+ def modify_value(name, amount, **options)
238
244
  options = merged_options(options)
239
245
  key = normalize_key(name, options)
240
246
  version = normalize_version(name, options)
@@ -25,10 +25,10 @@ module ActiveSupport
25
25
  def cleanup(options = nil)
26
26
  end
27
27
 
28
- def increment(name, amount = 1, options = nil)
28
+ def increment(name, amount = 1, **options)
29
29
  end
30
30
 
31
- def decrement(name, amount = 1, options = nil)
31
+ def decrement(name, amount = 1, **options)
32
32
  end
33
33
 
34
34
  def delete_matched(matcher, options = nil)