activesupport 6.1.5 → 7.0.0.alpha1

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

Potentially problematic release.


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

Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +151 -584
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +15 -9
  8. data/lib/active_support/cache/mem_cache_store.rb +119 -28
  9. data/lib/active_support/cache/memory_store.rb +21 -13
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +39 -59
  12. data/lib/active_support/cache/strategy/local_cache.rb +29 -49
  13. data/lib/active_support/cache.rb +189 -45
  14. data/lib/active_support/callbacks.rb +35 -31
  15. data/lib/active_support/concern.rb +5 -5
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  17. data/lib/active_support/concurrency/share_lock.rb +2 -2
  18. data/lib/active_support/configurable.rb +6 -3
  19. data/lib/active_support/configuration_file.rb +1 -1
  20. data/lib/active_support/core_ext/array/access.rb +1 -5
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -6
  22. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  23. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  24. data/lib/active_support/core_ext/date/blank.rb +1 -1
  25. data/lib/active_support/core_ext/date/calculations.rb +2 -2
  26. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  27. data/lib/active_support/core_ext/digest/uuid.rb +13 -13
  28. data/lib/active_support/core_ext/enumerable.rb +64 -12
  29. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  30. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  31. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  32. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  33. data/lib/active_support/core_ext/name_error.rb +2 -8
  34. data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
  35. data/lib/active_support/core_ext/object/blank.rb +2 -2
  36. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  37. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  38. data/lib/active_support/core_ext/object/json.rb +29 -24
  39. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  40. data/lib/active_support/core_ext/object/try.rb +20 -20
  41. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  42. data/lib/active_support/core_ext/range/each.rb +1 -1
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +1 -1
  44. data/lib/active_support/core_ext/string/filters.rb +1 -1
  45. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  46. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  47. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  48. data/lib/active_support/core_ext/time/calculations.rb +4 -5
  49. data/lib/active_support/core_ext/time/zones.rb +2 -17
  50. data/lib/active_support/core_ext/uri.rb +0 -14
  51. data/lib/active_support/current_attributes.rb +17 -1
  52. data/lib/active_support/dependencies/interlock.rb +10 -18
  53. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  54. data/lib/active_support/dependencies.rb +58 -788
  55. data/lib/active_support/deprecation/behaviors.rb +4 -1
  56. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  57. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  58. data/lib/active_support/deprecation.rb +1 -1
  59. data/lib/active_support/descendants_tracker.rb +12 -9
  60. data/lib/active_support/digest.rb +4 -4
  61. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  62. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  63. data/lib/active_support/duration.rb +80 -52
  64. data/lib/active_support/encrypted_configuration.rb +11 -1
  65. data/lib/active_support/encrypted_file.rb +1 -1
  66. data/lib/active_support/environment_inquirer.rb +1 -1
  67. data/lib/active_support/evented_file_update_checker.rb +1 -1
  68. data/lib/active_support/execution_wrapper.rb +13 -16
  69. data/lib/active_support/fork_tracker.rb +2 -4
  70. data/lib/active_support/gem_version.rb +4 -4
  71. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  72. data/lib/active_support/i18n.rb +1 -0
  73. data/lib/active_support/inflector/inflections.rb +11 -4
  74. data/lib/active_support/inflector/methods.rb +23 -47
  75. data/lib/active_support/json/encoding.rb +3 -3
  76. data/lib/active_support/key_generator.rb +18 -1
  77. data/lib/active_support/locale/en.yml +1 -1
  78. data/lib/active_support/log_subscriber.rb +13 -3
  79. data/lib/active_support/logger_thread_safe_level.rb +5 -13
  80. data/lib/active_support/message_encryptor.rb +3 -3
  81. data/lib/active_support/message_verifier.rb +4 -4
  82. data/lib/active_support/messages/metadata.rb +2 -2
  83. data/lib/active_support/multibyte/chars.rb +10 -11
  84. data/lib/active_support/multibyte.rb +1 -1
  85. data/lib/active_support/notifications/fanout.rb +31 -11
  86. data/lib/active_support/notifications/instrumenter.rb +17 -0
  87. data/lib/active_support/notifications.rb +10 -0
  88. data/lib/active_support/number_helper/number_converter.rb +1 -3
  89. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  90. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  91. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  92. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  93. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  94. data/lib/active_support/number_helper.rb +0 -2
  95. data/lib/active_support/option_merger.rb +4 -16
  96. data/lib/active_support/ordered_hash.rb +1 -1
  97. data/lib/active_support/parameter_filter.rb +5 -0
  98. data/lib/active_support/per_thread_registry.rb +1 -1
  99. data/lib/active_support/railtie.rb +33 -10
  100. data/lib/active_support/reloader.rb +1 -1
  101. data/lib/active_support/rescuable.rb +2 -2
  102. data/lib/active_support/secure_compare_rotator.rb +1 -1
  103. data/lib/active_support/string_inquirer.rb +0 -2
  104. data/lib/active_support/subscriber.rb +5 -0
  105. data/lib/active_support/test_case.rb +9 -21
  106. data/lib/active_support/testing/assertions.rb +34 -4
  107. data/lib/active_support/testing/deprecation.rb +1 -1
  108. data/lib/active_support/testing/isolation.rb +1 -1
  109. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  110. data/lib/active_support/testing/parallelization/server.rb +4 -0
  111. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  112. data/lib/active_support/testing/parallelization.rb +4 -0
  113. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  114. data/lib/active_support/testing/stream.rb +3 -5
  115. data/lib/active_support/testing/tagged_logging.rb +1 -1
  116. data/lib/active_support/testing/time_helpers.rb +13 -2
  117. data/lib/active_support/time_with_zone.rb +19 -6
  118. data/lib/active_support/values/time_zone.rb +25 -11
  119. data/lib/active_support/xml_mini/jdom.rb +1 -1
  120. data/lib/active_support/xml_mini/libxml.rb +5 -5
  121. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  122. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  123. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  124. data/lib/active_support/xml_mini/rexml.rb +1 -1
  125. data/lib/active_support/xml_mini.rb +2 -1
  126. data/lib/active_support.rb +14 -1
  127. metadata +11 -26
  128. data/lib/active_support/core_ext/marshal.rb +0 -26
  129. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
@@ -34,57 +34,40 @@ module ActiveSupport
34
34
 
35
35
  # Simple memory backed cache. This cache is not thread safe and is intended only
36
36
  # for serving as a temporary memory cache for a single thread.
37
- class LocalStore < Store
37
+ class LocalStore
38
38
  def initialize
39
- super
40
39
  @data = {}
41
40
  end
42
41
 
43
- # Don't allow synchronizing since it isn't thread safe.
44
- def synchronize # :nodoc:
45
- yield
46
- end
47
-
48
42
  def clear(options = nil)
49
43
  @data.clear
50
44
  end
51
45
 
52
- def read_entry(key, **options)
46
+ def read_entry(key)
53
47
  @data[key]
54
48
  end
55
49
 
56
- def read_multi_entries(keys, **options)
57
- values = {}
58
-
59
- keys.each do |name|
60
- entry = read_entry(name, **options)
61
- values[name] = entry.value if entry
62
- end
63
-
64
- values
50
+ def read_multi_entries(keys)
51
+ @data.slice(*keys)
65
52
  end
66
53
 
67
- def write_entry(key, entry, **options)
68
- entry.dup_value!
54
+ def write_entry(key, entry)
69
55
  @data[key] = entry
70
56
  true
71
57
  end
72
58
 
73
- def delete_entry(key, **options)
59
+ def delete_entry(key)
74
60
  !!@data.delete(key)
75
61
  end
76
62
 
77
- def fetch_entry(key, options = nil) # :nodoc:
78
- entry = @data.fetch(key) { @data[key] = yield }
79
- dup_entry = entry.dup
80
- dup_entry&.dup_value!
81
- dup_entry
63
+ def fetch_entry(key) # :nodoc:
64
+ @data.fetch(key) { @data[key] = yield }
82
65
  end
83
66
  end
84
67
 
85
68
  # Use a local cache for the duration of block.
86
- def with_local_cache
87
- use_temporary_local_cache(LocalStore.new) { yield }
69
+ def with_local_cache(&block)
70
+ use_temporary_local_cache(LocalStore.new, &block)
88
71
  end
89
72
 
90
73
  # Middleware class can be inserted as a Rack handler to be local cache for the
@@ -116,27 +99,27 @@ module ActiveSupport
116
99
  def increment(name, amount = 1, **options) # :nodoc:
117
100
  return super unless local_cache
118
101
  value = bypass_local_cache { super }
119
- write_cache_value(name, value, **options)
102
+ write_cache_value(name, value, raw: true, **options)
120
103
  value
121
104
  end
122
105
 
123
106
  def decrement(name, amount = 1, **options) # :nodoc:
124
107
  return super unless local_cache
125
108
  value = bypass_local_cache { super }
126
- write_cache_value(name, value, **options)
109
+ write_cache_value(name, value, raw: true, **options)
127
110
  value
128
111
  end
129
112
 
130
113
  private
131
- def read_entry(key, **options)
114
+ def read_serialized_entry(key, raw: false, **options)
132
115
  if cache = local_cache
133
116
  hit = true
134
- value = cache.fetch_entry(key) do
117
+ entry = cache.fetch_entry(key) do
135
118
  hit = false
136
119
  super
137
120
  end
138
121
  options[:event][:store] = cache.class.name if hit && options[:event]
139
- value
122
+ entry
140
123
  else
141
124
  super
142
125
  end
@@ -145,7 +128,7 @@ module ActiveSupport
145
128
  def read_multi_entries(keys, **options)
146
129
  return super unless local_cache
147
130
 
148
- local_entries = local_cache.read_multi_entries(keys, **options)
131
+ local_entries = local_cache.read_multi_entries(keys)
149
132
  missed_keys = keys - local_entries.keys
150
133
 
151
134
  if missed_keys.any?
@@ -155,30 +138,27 @@ module ActiveSupport
155
138
  end
156
139
  end
157
140
 
158
- def write_entry(key, entry, **options)
159
- if options[:unless_exist]
160
- local_cache.delete_entry(key, **options) if local_cache
141
+ def write_serialized_entry(key, payload, **)
142
+ if return_value = super
143
+ local_cache.write_entry(key, payload) if local_cache
161
144
  else
162
- local_cache.write_entry(key, entry, **options) if local_cache
145
+ local_cache.delete_entry(key) if local_cache
163
146
  end
164
-
165
- super
147
+ return_value
166
148
  end
167
149
 
168
- def delete_entry(key, **options)
169
- local_cache.delete_entry(key, **options) if local_cache
150
+ def delete_entry(key, **)
151
+ local_cache.delete_entry(key) if local_cache
170
152
  super
171
153
  end
172
154
 
173
155
  def write_cache_value(name, value, **options)
174
156
  name = normalize_key(name, options)
175
157
  cache = local_cache
176
- cache.mute do
177
- if value
178
- cache.write(name, value, options)
179
- else
180
- cache.delete(name, **options)
181
- end
158
+ if value
159
+ cache.write_entry(name, serialize_entry(new_entry(value, **options), **options))
160
+ else
161
+ cache.delete_entry(name)
182
162
  end
183
163
  end
184
164
 
@@ -190,8 +170,8 @@ module ActiveSupport
190
170
  LocalCacheRegistry.cache_for(local_cache_key)
191
171
  end
192
172
 
193
- def bypass_local_cache
194
- use_temporary_local_cache(nil) { yield }
173
+ def bypass_local_cache(&block)
174
+ use_temporary_local_cache(nil, &block)
195
175
  end
196
176
 
197
177
  def use_temporary_local_cache(temporary_cache)
@@ -22,13 +22,24 @@ module ActiveSupport
22
22
 
23
23
  # These options mean something to all cache implementations. Individual cache
24
24
  # implementations may support additional options.
25
- UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl, :coder]
25
+ UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :expire_in, :expired_in, :race_condition_ttl, :coder, :skip_nil]
26
+
27
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
28
+
29
+ # Mapping of canonical option names to aliases that a store will recognize.
30
+ OPTION_ALIASES = {
31
+ expires_in: [:expire_in, :expired_in]
32
+ }.freeze
26
33
 
27
34
  module Strategy
28
35
  autoload :LocalCache, "active_support/cache/strategy/local_cache"
29
36
  end
30
37
 
38
+ @format_version = 6.1
39
+
31
40
  class << self
41
+ attr_accessor :format_version
42
+
32
43
  # Creates a new Store object according to the given options.
33
44
  #
34
45
  # If no arguments are passed to this method, then a new
@@ -164,8 +175,6 @@ module ActiveSupport
164
175
  # threshold is configurable with the <tt>:compress_threshold</tt> option,
165
176
  # specified in bytes.
166
177
  class Store
167
- DEFAULT_CODER = Marshal
168
-
169
178
  cattr_accessor :logger, instance_writer: true
170
179
 
171
180
  attr_reader :silence, :options
@@ -192,8 +201,12 @@ module ActiveSupport
192
201
  # except for <tt>:namespace</tt> which can be used to set the global
193
202
  # namespace for the cache.
194
203
  def initialize(options = nil)
195
- @options = options ? options.dup : {}
196
- @coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
204
+ @options = options ? normalize_options(options) : {}
205
+ @options[:compress] = true unless @options.key?(:compress)
206
+ @options[:compress_threshold] = DEFAULT_COMPRESS_LIMIT unless @options.key?(:compress_threshold)
207
+
208
+ @coder = @options.delete(:coder) { default_coder } || NullCoder
209
+ @coder_supports_compression = @coder.respond_to?(:dump_compressed)
197
210
  end
198
211
 
199
212
  # Silences the logger.
@@ -255,11 +268,21 @@ module ActiveSupport
255
268
  # All caches support auto-expiring content after a specified number of
256
269
  # seconds. This value can be specified as an option to the constructor
257
270
  # (in which case all entries will be affected), or it can be supplied to
258
- # the +fetch+ or +write+ method to effect just one entry.
271
+ # the +fetch+ or +write+ method to affect just one entry.
272
+ # <tt>:expire_in</tt> and <tt>:expired_in</tt> are aliases for
273
+ # <tt>:expires_in</tt>.
259
274
  #
260
275
  # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
261
276
  # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
262
277
  #
278
+ # Setting <tt>:expires_at</tt> will set an absolute expiration time on the cache.
279
+ # All caches support auto-expiring content after a specified number of
280
+ # seconds. This value can only be supplied to the +fetch+ or +write+ method to
281
+ # affect just one entry.
282
+ #
283
+ # cache = ActiveSupport::Cache::MemoryStore.new
284
+ # cache.write(key, value, expires_at: Time.now.at_end_of_hour)
285
+ #
263
286
  # Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
264
287
  # is of the same version. nil is returned on mismatches despite contents.
265
288
  # This feature is used to support recyclable cache keys.
@@ -512,6 +535,10 @@ module ActiveSupport
512
535
  end
513
536
  end
514
537
 
538
+ def new_entry(value, options = nil) # :nodoc:
539
+ Entry.new(value, **merged_options(options))
540
+ end
541
+
515
542
  # Deletes all entries with keys matching the pattern.
516
543
  #
517
544
  # Options are passed to the underlying cache implementation.
@@ -559,6 +586,10 @@ module ActiveSupport
559
586
  end
560
587
 
561
588
  private
589
+ def default_coder
590
+ Coders[Cache.format_version]
591
+ end
592
+
562
593
  # Adds the namespace defined in the options to a pattern designed to
563
594
  # match keys. Implementations that support delete_matched should call
564
595
  # this method to translate a pattern that matches names into one that
@@ -590,8 +621,13 @@ module ActiveSupport
590
621
  raise NotImplementedError.new
591
622
  end
592
623
 
593
- def serialize_entry(entry)
594
- @coder.dump(entry)
624
+ def serialize_entry(entry, **options)
625
+ options = merged_options(options)
626
+ if @coder_supports_compression && options[:compress]
627
+ @coder.dump_compressed(entry, options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT)
628
+ else
629
+ @coder.dump(entry)
630
+ end
595
631
  end
596
632
 
597
633
  def deserialize_entry(payload)
@@ -640,6 +676,7 @@ module ActiveSupport
640
676
  # Merges the default options with ones specific to a method call.
641
677
  def merged_options(call_options)
642
678
  if call_options
679
+ call_options = normalize_options(call_options)
643
680
  if options.empty?
644
681
  call_options
645
682
  else
@@ -650,6 +687,18 @@ module ActiveSupport
650
687
  end
651
688
  end
652
689
 
690
+ # Normalize aliased options to their canonical form
691
+ def normalize_options(options)
692
+ options = options.dup
693
+ OPTION_ALIASES.each do |canonical_name, aliases|
694
+ alias_key = aliases.detect { |key| options.key?(key) }
695
+ options[canonical_name] ||= options[alias_key] if alias_key
696
+ options.except!(*aliases)
697
+ end
698
+
699
+ options
700
+ end
701
+
653
702
  # Expands and namespaces the cache key. May be overridden by
654
703
  # cache stores to do additional normalization.
655
704
  def normalize_key(key, options = nil)
@@ -732,7 +781,7 @@ module ActiveSupport
732
781
  if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
733
782
  # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
734
783
  # for a brief period while the entry is being recalculated.
735
- entry.expires_at = Time.now + race_ttl
784
+ entry.expires_at = Time.now.to_f + race_ttl
736
785
  write_entry(key, entry, expires_in: race_ttl * 2)
737
786
  else
738
787
  delete_entry(key, **options)
@@ -758,13 +807,93 @@ module ActiveSupport
758
807
  end
759
808
 
760
809
  module NullCoder # :nodoc:
810
+ extend self
811
+
812
+ def dump(entry)
813
+ entry
814
+ end
815
+
816
+ def dump_compressed(entry, threshold)
817
+ entry.compressed(threshold)
818
+ end
819
+
820
+ def load(payload)
821
+ payload
822
+ end
823
+ end
824
+
825
+ module Coders # :nodoc:
826
+ MARK_61 = "\x04\b".b.freeze # The one set by Marshal.
827
+ MARK_70_UNCOMPRESSED = "\x00".b.freeze
828
+ MARK_70_COMPRESSED = "\x01".b.freeze
829
+
761
830
  class << self
831
+ def [](version)
832
+ case version
833
+ when 6.1
834
+ Rails61Coder
835
+ when 7.0
836
+ Rails70Coder
837
+ else
838
+ raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
839
+ end
840
+ end
841
+ end
842
+
843
+ module Loader
844
+ extend self
845
+
762
846
  def load(payload)
763
- payload
847
+ if !payload.is_a?(String)
848
+ ActiveSupport::Cache::Store.logger&.warn %{Payload wasn't a string, was #{payload.class.name} - couldn't unmarshal, so returning nil."}
849
+
850
+ return nil
851
+ elsif payload.start_with?(MARK_70_UNCOMPRESSED)
852
+ members = Marshal.load(payload.byteslice(1..-1))
853
+ elsif payload.start_with?(MARK_70_COMPRESSED)
854
+ members = Marshal.load(Zlib::Inflate.inflate(payload.byteslice(1..-1)))
855
+ elsif payload.start_with?(MARK_61)
856
+ return Marshal.load(payload)
857
+ else
858
+ ActiveSupport::Cache::Store.logger&.warn %{Invalid cache prefix: #{payload.byteslice(0).inspect}, expected "\\x00" or "\\x01"}
859
+
860
+ return nil
861
+ end
862
+ Entry.unpack(members)
764
863
  end
864
+ end
865
+
866
+ module Rails61Coder
867
+ include Loader
868
+ extend self
765
869
 
766
870
  def dump(entry)
767
- entry
871
+ Marshal.dump(entry)
872
+ end
873
+
874
+ def dump_compressed(entry, threshold)
875
+ Marshal.dump(entry.compressed(threshold))
876
+ end
877
+ end
878
+
879
+ module Rails70Coder
880
+ include Loader
881
+ extend self
882
+
883
+ def dump(entry)
884
+ MARK_70_UNCOMPRESSED + Marshal.dump(entry.pack)
885
+ end
886
+
887
+ def dump_compressed(entry, threshold)
888
+ payload = Marshal.dump(entry.pack)
889
+ if payload.bytesize >= threshold
890
+ compressed_payload = Zlib::Deflate.deflate(payload)
891
+ if compressed_payload.bytesize < payload.bytesize
892
+ return MARK_70_COMPRESSED + compressed_payload
893
+ end
894
+ end
895
+
896
+ MARK_70_UNCOMPRESSED + payload
768
897
  end
769
898
  end
770
899
  end
@@ -777,19 +906,22 @@ module ActiveSupport
777
906
  # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
778
907
  # using short instance variable names that are lazily defined.
779
908
  class Entry # :nodoc:
780
- attr_reader :version
909
+ class << self
910
+ def unpack(members)
911
+ new(members[0], expires_at: members[1], version: members[2])
912
+ end
913
+ end
781
914
 
782
- DEFAULT_COMPRESS_LIMIT = 1.kilobyte
915
+ attr_reader :version
783
916
 
784
917
  # Creates a new cache entry for the specified value. Options supported are
785
- # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+.
786
- def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **)
918
+ # +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
919
+ def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
787
920
  @value = value
788
921
  @version = version
789
- @created_at = Time.now.to_f
790
- @expires_in = expires_in && expires_in.to_f
791
-
792
- compress!(compress_threshold) if compress
922
+ @created_at = 0.0
923
+ @expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
924
+ @compressed = true if compressed
793
925
  end
794
926
 
795
927
  def value
@@ -831,6 +963,38 @@ module ActiveSupport
831
963
  end
832
964
  end
833
965
 
966
+ def compressed? # :nodoc:
967
+ defined?(@compressed)
968
+ end
969
+
970
+ def compressed(compress_threshold)
971
+ return self if compressed?
972
+
973
+ case @value
974
+ when nil, true, false, Numeric
975
+ uncompressed_size = 0
976
+ when String
977
+ uncompressed_size = @value.bytesize
978
+ else
979
+ serialized = Marshal.dump(@value)
980
+ uncompressed_size = serialized.bytesize
981
+ end
982
+
983
+ if uncompressed_size >= compress_threshold
984
+ serialized ||= Marshal.dump(@value)
985
+ compressed = Zlib::Deflate.deflate(serialized)
986
+
987
+ if compressed.bytesize < uncompressed_size
988
+ return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
989
+ end
990
+ end
991
+ self
992
+ end
993
+
994
+ def local?
995
+ false
996
+ end
997
+
834
998
  # Duplicates the value in a class. This is used by cache implementations that don't natively
835
999
  # serialize entries to protect against accidental cache modifications.
836
1000
  def dup_value!
@@ -843,33 +1007,13 @@ module ActiveSupport
843
1007
  end
844
1008
  end
845
1009
 
846
- private
847
- def compress!(compress_threshold)
848
- case @value
849
- when nil, true, false, Numeric
850
- uncompressed_size = 0
851
- when String
852
- uncompressed_size = @value.bytesize
853
- else
854
- serialized = Marshal.dump(@value)
855
- uncompressed_size = serialized.bytesize
856
- end
857
-
858
- if uncompressed_size >= compress_threshold
859
- serialized ||= Marshal.dump(@value)
860
- compressed = Zlib::Deflate.deflate(serialized)
861
-
862
- if compressed.bytesize < uncompressed_size
863
- @value = compressed
864
- @compressed = true
865
- end
866
- end
867
- end
868
-
869
- def compressed?
870
- defined?(@compressed)
871
- end
1010
+ def pack
1011
+ members = [value, expires_at, version]
1012
+ members.pop while !members.empty? && members.last.nil?
1013
+ members
1014
+ end
872
1015
 
1016
+ private
873
1017
  def uncompress(value)
874
1018
  Marshal.load(Zlib::Inflate.inflate(value))
875
1019
  end
@@ -5,6 +5,7 @@ require "active_support/descendants_tracker"
5
5
  require "active_support/core_ext/array/extract_options"
6
6
  require "active_support/core_ext/class/attribute"
7
7
  require "active_support/core_ext/string/filters"
8
+ require "active_support/core_ext/object/blank"
8
9
  require "thread"
9
10
 
10
11
  module ActiveSupport
@@ -276,7 +277,7 @@ module ActiveSupport
276
277
  end
277
278
  end
278
279
 
279
- class Callback #:nodoc:#
280
+ class Callback # :nodoc:#
280
281
  def self.build(chain, filter, kind, options)
281
282
  if filter.is_a?(String)
282
283
  raise ArgumentError, <<-MSG.squish
@@ -289,21 +290,17 @@ module ActiveSupport
289
290
  end
290
291
 
291
292
  attr_accessor :kind, :name
292
- attr_reader :chain_config
293
+ attr_reader :chain_config, :filter
293
294
 
294
295
  def initialize(name, filter, kind, options, chain_config)
295
296
  @chain_config = chain_config
296
297
  @name = name
297
298
  @kind = kind
298
299
  @filter = filter
299
- @key = compute_identifier filter
300
300
  @if = check_conditionals(options[:if])
301
301
  @unless = check_conditionals(options[:unless])
302
302
  end
303
303
 
304
- def filter; @key; end
305
- def raw_filter; @filter; end
306
-
307
304
  def merge_conditional_options(chain, if_option:, unless_option:)
308
305
  options = {
309
306
  if: @if.dup,
@@ -356,7 +353,7 @@ module ActiveSupport
356
353
  return EMPTY_ARRAY if conditionals.blank?
357
354
 
358
355
  conditionals = Array(conditionals)
359
- if conditionals.any? { |c| c.is_a?(String) }
356
+ if conditionals.any?(String)
360
357
  raise ArgumentError, <<-MSG.squish
361
358
  Passing string to be evaluated in :if and :unless conditional
362
359
  options is not supported. Pass a symbol for an instance method,
@@ -367,15 +364,6 @@ module ActiveSupport
367
364
  conditionals.freeze
368
365
  end
369
366
 
370
- def compute_identifier(filter)
371
- case filter
372
- when ::Proc
373
- filter.object_id
374
- else
375
- filter
376
- end
377
- end
378
-
379
367
  def conditions_lambdas
380
368
  @if.map { |c| CallTemplate.build(c, self).make_lambda } +
381
369
  @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
@@ -517,7 +505,7 @@ module ActiveSupport
517
505
  end
518
506
  end
519
507
 
520
- class CallbackChain #:nodoc:#
508
+ class CallbackChain # :nodoc:#
521
509
  include Enumerable
522
510
 
523
511
  attr_reader :name, :config
@@ -619,7 +607,7 @@ module ActiveSupport
619
607
 
620
608
  # This is used internally to append, prepend and skip callbacks to the
621
609
  # CallbackChain.
622
- def __update_callbacks(name) #:nodoc:
610
+ def __update_callbacks(name) # :nodoc:
623
611
  ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
624
612
  chain = target.get_callbacks name
625
613
  yield target, chain.dup
@@ -688,10 +676,32 @@ module ActiveSupport
688
676
  # <tt>:unless</tt> options may be passed in order to control when the
689
677
  # callback is skipped.
690
678
  #
691
- # class Writer < Person
692
- # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
679
+ # class Writer < PersonRecord
680
+ # attr_accessor :age
681
+ # skip_callback :save, :before, :saving_message, if: -> { age > 18 }
693
682
  # end
694
683
  #
684
+ # When if option returns true, callback is skipped.
685
+ #
686
+ # writer = Writer.new
687
+ # writer.age = 20
688
+ # writer.save
689
+ #
690
+ # Output:
691
+ # - save
692
+ # saved
693
+ #
694
+ # When if option returns false, callback is NOT skipped.
695
+ #
696
+ # young_writer = Writer.new
697
+ # young_writer.age = 17
698
+ # young_writer.save
699
+ #
700
+ # Output:
701
+ # saving...
702
+ # - save
703
+ # saved
704
+ #
695
705
  # An <tt>ArgumentError</tt> will be raised if the callback has not
696
706
  # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
697
707
  def skip_callback(name, *filter_list, &block)
@@ -844,18 +854,12 @@ module ActiveSupport
844
854
  __callbacks[name.to_sym]
845
855
  end
846
856
 
847
- if Module.instance_method(:method_defined?).arity == 1 # Ruby 2.5 and older
848
- def set_callbacks(name, callbacks) # :nodoc:
849
- self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
850
- end
851
- else # Ruby 2.6 and newer
852
- def set_callbacks(name, callbacks) # :nodoc:
853
- unless singleton_class.method_defined?(:__callbacks, false)
854
- self.__callbacks = __callbacks.dup
855
- end
856
- self.__callbacks[name.to_sym] = callbacks
857
- self.__callbacks
857
+ def set_callbacks(name, callbacks) # :nodoc:
858
+ unless singleton_class.method_defined?(:__callbacks, false)
859
+ self.__callbacks = __callbacks.dup
858
860
  end
861
+ self.__callbacks[name.to_sym] = callbacks
862
+ self.__callbacks
859
863
  end
860
864
  end
861
865
  end
@@ -108,23 +108,23 @@ module ActiveSupport
108
108
  #
109
109
  # <tt>prepend</tt> is also used for any dependencies.
110
110
  module Concern
111
- class MultipleIncludedBlocks < StandardError #:nodoc:
111
+ class MultipleIncludedBlocks < StandardError # :nodoc:
112
112
  def initialize
113
113
  super "Cannot define multiple 'included' blocks for a Concern"
114
114
  end
115
115
  end
116
116
 
117
- class MultiplePrependBlocks < StandardError #:nodoc:
117
+ class MultiplePrependBlocks < StandardError # :nodoc:
118
118
  def initialize
119
119
  super "Cannot define multiple 'prepended' blocks for a Concern"
120
120
  end
121
121
  end
122
122
 
123
- def self.extended(base) #:nodoc:
123
+ def self.extended(base) # :nodoc:
124
124
  base.instance_variable_set(:@_dependencies, [])
125
125
  end
126
126
 
127
- def append_features(base) #:nodoc:
127
+ def append_features(base) # :nodoc:
128
128
  if base.instance_variable_defined?(:@_dependencies)
129
129
  base.instance_variable_get(:@_dependencies) << self
130
130
  false
@@ -137,7 +137,7 @@ module ActiveSupport
137
137
  end
138
138
  end
139
139
 
140
- def prepend_features(base) #:nodoc:
140
+ def prepend_features(base) # :nodoc:
141
141
  if base.instance_variable_defined?(:@_dependencies)
142
142
  base.instance_variable_get(:@_dependencies).unshift self
143
143
  false