activesupport 5.2.0 → 6.0.3.2

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 (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +479 -330
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support.rb +2 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +27 -1
  8. data/lib/active_support/cache.rb +104 -84
  9. data/lib/active_support/cache/file_store.rb +29 -30
  10. data/lib/active_support/cache/mem_cache_store.rb +14 -19
  11. data/lib/active_support/cache/memory_store.rb +15 -9
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +73 -34
  14. data/lib/active_support/cache/strategy/local_cache.rb +23 -23
  15. data/lib/active_support/callbacks.rb +16 -8
  16. data/lib/active_support/concern.rb +31 -4
  17. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  18. data/lib/active_support/concurrency/share_lock.rb +0 -1
  19. data/lib/active_support/configurable.rb +7 -11
  20. data/lib/active_support/core_ext/array.rb +1 -1
  21. data/lib/active_support/core_ext/array/access.rb +18 -6
  22. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  23. data/lib/active_support/core_ext/array/extract.rb +21 -0
  24. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  25. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  26. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  27. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  28. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  29. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  30. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  31. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  32. data/lib/active_support/core_ext/digest.rb +3 -0
  33. data/lib/active_support/core_ext/enumerable.rb +97 -68
  34. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  35. data/lib/active_support/core_ext/hash.rb +1 -2
  36. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  37. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +1 -1
  40. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  41. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  42. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/module.rb +0 -1
  47. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  48. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  49. data/lib/active_support/core_ext/module/delegation.rb +41 -8
  50. data/lib/active_support/core_ext/module/introspection.rb +38 -13
  51. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  52. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  55. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  56. data/lib/active_support/core_ext/object/blank.rb +1 -2
  57. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  58. data/lib/active_support/core_ext/object/json.rb +1 -0
  59. data/lib/active_support/core_ext/object/to_query.rb +5 -2
  60. data/lib/active_support/core_ext/object/try.rb +17 -7
  61. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  62. data/lib/active_support/core_ext/range.rb +1 -1
  63. data/lib/active_support/core_ext/range/compare_range.rb +76 -0
  64. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  65. data/lib/active_support/core_ext/range/each.rb +0 -1
  66. data/lib/active_support/core_ext/range/include_range.rb +6 -22
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  68. data/lib/active_support/core_ext/regexp.rb +0 -4
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +8 -0
  71. data/lib/active_support/core_ext/string/filters.rb +42 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  73. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  74. data/lib/active_support/core_ext/string/output_safety.rb +63 -6
  75. data/lib/active_support/core_ext/string/strip.rb +3 -1
  76. data/lib/active_support/core_ext/time/calculations.rb +31 -2
  77. data/lib/active_support/core_ext/uri.rb +2 -4
  78. data/lib/active_support/current_attributes.rb +8 -0
  79. data/lib/active_support/dependencies.rb +77 -18
  80. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  81. data/lib/active_support/deprecation.rb +1 -1
  82. data/lib/active_support/deprecation/behaviors.rb +5 -1
  83. data/lib/active_support/deprecation/method_wrappers.rb +20 -13
  84. data/lib/active_support/deprecation/proxy_wrappers.rb +28 -5
  85. data/lib/active_support/deprecation/reporting.rb +1 -1
  86. data/lib/active_support/descendants_tracker.rb +55 -9
  87. data/lib/active_support/duration.rb +19 -16
  88. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  89. data/lib/active_support/duration/iso8601_serializer.rb +3 -5
  90. data/lib/active_support/encrypted_configuration.rb +1 -5
  91. data/lib/active_support/encrypted_file.rb +4 -3
  92. data/lib/active_support/evented_file_update_checker.rb +39 -10
  93. data/lib/active_support/execution_wrapper.rb +1 -0
  94. data/lib/active_support/file_update_checker.rb +0 -1
  95. data/lib/active_support/gem_version.rb +4 -4
  96. data/lib/active_support/hash_with_indifferent_access.rb +36 -18
  97. data/lib/active_support/i18n.rb +1 -0
  98. data/lib/active_support/i18n_railtie.rb +18 -2
  99. data/lib/active_support/inflector/inflections.rb +1 -5
  100. data/lib/active_support/inflector/methods.rb +18 -29
  101. data/lib/active_support/inflector/transliterate.rb +47 -18
  102. data/lib/active_support/json/decoding.rb +23 -24
  103. data/lib/active_support/json/encoding.rb +6 -2
  104. data/lib/active_support/key_generator.rb +0 -32
  105. data/lib/active_support/lazy_load_hooks.rb +5 -2
  106. data/lib/active_support/locale/en.rb +33 -0
  107. data/lib/active_support/log_subscriber.rb +31 -9
  108. data/lib/active_support/logger.rb +1 -16
  109. data/lib/active_support/logger_silence.rb +28 -12
  110. data/lib/active_support/logger_thread_safe_level.rb +28 -5
  111. data/lib/active_support/message_encryptor.rb +4 -6
  112. data/lib/active_support/message_verifier.rb +5 -5
  113. data/lib/active_support/messages/metadata.rb +3 -2
  114. data/lib/active_support/messages/rotator.rb +4 -4
  115. data/lib/active_support/multibyte/chars.rb +29 -49
  116. data/lib/active_support/multibyte/unicode.rb +44 -282
  117. data/lib/active_support/notifications.rb +41 -4
  118. data/lib/active_support/notifications/fanout.rb +100 -15
  119. data/lib/active_support/notifications/instrumenter.rb +80 -9
  120. data/lib/active_support/number_helper.rb +11 -0
  121. data/lib/active_support/number_helper/number_converter.rb +4 -5
  122. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -10
  123. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  124. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -2
  125. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -2
  126. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  127. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  128. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -4
  129. data/lib/active_support/option_merger.rb +21 -3
  130. data/lib/active_support/ordered_hash.rb +1 -1
  131. data/lib/active_support/ordered_options.rb +5 -1
  132. data/lib/active_support/parameter_filter.rb +128 -0
  133. data/lib/active_support/rails.rb +0 -6
  134. data/lib/active_support/reloader.rb +4 -5
  135. data/lib/active_support/security_utils.rb +1 -1
  136. data/lib/active_support/string_inquirer.rb +0 -1
  137. data/lib/active_support/subscriber.rb +65 -22
  138. data/lib/active_support/tagged_logging.rb +13 -4
  139. data/lib/active_support/test_case.rb +92 -1
  140. data/lib/active_support/testing/assertions.rb +15 -1
  141. data/lib/active_support/testing/deprecation.rb +0 -1
  142. data/lib/active_support/testing/file_fixtures.rb +2 -0
  143. data/lib/active_support/testing/isolation.rb +2 -2
  144. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  145. data/lib/active_support/testing/parallelization.rb +134 -0
  146. data/lib/active_support/testing/setup_and_teardown.rb +5 -9
  147. data/lib/active_support/testing/stream.rb +1 -2
  148. data/lib/active_support/testing/time_helpers.rb +7 -9
  149. data/lib/active_support/time_with_zone.rb +15 -5
  150. data/lib/active_support/values/time_zone.rb +14 -8
  151. data/lib/active_support/xml_mini.rb +2 -10
  152. data/lib/active_support/xml_mini/jdom.rb +2 -3
  153. data/lib/active_support/xml_mini/libxml.rb +2 -2
  154. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  155. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  156. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  157. data/lib/active_support/xml_mini/rexml.rb +2 -2
  158. metadata +42 -13
  159. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  160. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005-2018 David Heinemeier Hansson
1
+ Copyright (c) 2005-2019 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -5,6 +5,7 @@ extensions that were found useful for the Rails framework. These additions
5
5
  reside in this package so they can be loaded as needed in Ruby projects
6
6
  outside of Rails.
7
7
 
8
+ You can read more about the extensions in the {Active Support Core Extensions}[https://edgeguides.rubyonrails.org/active_support_core_extensions.html] guide.
8
9
 
9
10
  == Download and installation
10
11
 
@@ -14,7 +15,7 @@ The latest version of Active Support can be installed with RubyGems:
14
15
 
15
16
  Source code can be downloaded as part of the Rails project on GitHub:
16
17
 
17
- * https://github.com/rails/rails/tree/5-2-stable/activesupport
18
+ * https://github.com/rails/rails/tree/master/activesupport
18
19
 
19
20
 
20
21
  == License
@@ -28,7 +29,7 @@ Active Support is released under the MIT license:
28
29
 
29
30
  API documentation is at:
30
31
 
31
- * http://api.rubyonrails.org
32
+ * https://api.rubyonrails.org
32
33
 
33
34
  Bug reports for the Ruby on Rails project can be filed here:
34
35
 
@@ -36,4 +37,4 @@ Bug reports for the Ruby on Rails project can be filed here:
36
37
 
37
38
  Feature requests should be discussed on the rails-core mailing list here:
38
39
 
39
- * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
40
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2005-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2005-2019 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -34,6 +34,7 @@ module ActiveSupport
34
34
  extend ActiveSupport::Autoload
35
35
 
36
36
  autoload :Concern
37
+ autoload :ActionableError
37
38
  autoload :CurrentAttributes
38
39
  autoload :Dependencies
39
40
  autoload :DescendantsTracker
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Actionable errors let's you define actions to resolve an error.
5
+ #
6
+ # To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
7
+ # module and invoke the +action+ class macro to define the action. An action
8
+ # needs a name and a block to execute.
9
+ module ActionableError
10
+ extend Concern
11
+
12
+ class NonActionable < StandardError; end
13
+
14
+ included do
15
+ class_attribute :_actions, default: {}
16
+ end
17
+
18
+ def self.actions(error) # :nodoc:
19
+ case error
20
+ when ActionableError, -> it { Class === it && it < ActionableError }
21
+ error._actions
22
+ else
23
+ {}
24
+ end
25
+ end
26
+
27
+ def self.dispatch(error, name) # :nodoc:
28
+ actions(error).fetch(name).call
29
+ rescue KeyError
30
+ raise NonActionable, "Cannot find action \"#{name}\""
31
+ end
32
+
33
+ module ClassMethods
34
+ # Defines an action that can resolve the error.
35
+ #
36
+ # class PendingMigrationError < MigrationError
37
+ # include ActiveSupport::ActionableError
38
+ #
39
+ # action "Run pending migrations" do
40
+ # ActiveRecord::Tasks::DatabaseTasks.migrate
41
+ # end
42
+ # end
43
+ def action(name, &block)
44
+ _actions[name] = block
45
+ end
46
+ end
47
+ end
48
+ end
@@ -31,6 +31,9 @@ module ActiveSupport
31
31
  class BacktraceCleaner
32
32
  def initialize
33
33
  @filters, @silencers = [], []
34
+ add_gem_filter
35
+ add_gem_silencer
36
+ add_stdlib_silencer
34
37
  end
35
38
 
36
39
  # Returns the backtrace after all filters and silencers have been run
@@ -82,6 +85,25 @@ module ActiveSupport
82
85
  end
83
86
 
84
87
  private
88
+ FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
89
+
90
+ def add_gem_filter
91
+ gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
92
+ return if gems_paths.empty?
93
+
94
+ gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
95
+ gems_result = '\3 (\4) \5'
96
+ add_filter { |line| line.sub(gems_regexp, gems_result) }
97
+ end
98
+
99
+ def add_gem_silencer
100
+ add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
101
+ end
102
+
103
+ def add_stdlib_silencer
104
+ add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) }
105
+ end
106
+
85
107
  def filter_backtrace(backtrace)
86
108
  @filters.each do |f|
87
109
  backtrace = backtrace.map { |line| f.call(line) }
@@ -99,7 +121,11 @@ module ActiveSupport
99
121
  end
100
122
 
101
123
  def noise(backtrace)
102
- backtrace - silence(backtrace)
124
+ backtrace.select do |line|
125
+ @silencers.any? do |s|
126
+ s.call(line)
127
+ end
128
+ end
103
129
  end
104
130
  end
105
131
  end
@@ -52,12 +52,13 @@ module ActiveSupport
52
52
  #
53
53
  # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
54
54
  # # => returns MyOwnCacheStore.new
55
- def lookup_store(*store_option)
56
- store, *parameters = *Array.wrap(store_option).flatten
57
-
55
+ def lookup_store(store = nil, *parameters)
58
56
  case store
59
57
  when Symbol
60
- retrieve_store_class(store).new(*parameters)
58
+ options = parameters.extract_options!
59
+ retrieve_store_class(store).new(*parameters, **options)
60
+ when Array
61
+ lookup_store(*store)
61
62
  when nil
62
63
  ActiveSupport::Cache::MemoryStore.new
63
64
  else
@@ -229,6 +230,14 @@ module ActiveSupport
229
230
  # ask whether you should force a cache write. Otherwise, it's clearer to
230
231
  # just call <tt>Cache#write</tt>.
231
232
  #
233
+ # Setting <tt>skip_nil: true</tt> will not cache nil result:
234
+ #
235
+ # cache.fetch('foo') { nil }
236
+ # cache.fetch('bar', skip_nil: true) { nil }
237
+ # cache.exist?('foo') # => true
238
+ # cache.exist?('bar') # => false
239
+ #
240
+ #
232
241
  # Setting <tt>compress: false</tt> disables compression of the cache entry.
233
242
  #
234
243
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
@@ -310,7 +319,7 @@ module ActiveSupport
310
319
 
311
320
  entry = nil
312
321
  instrument(:read, name, options) do |payload|
313
- cached_entry = read_entry(key, options) unless options[:force]
322
+ cached_entry = read_entry(key, **options) unless options[:force]
314
323
  entry = handle_expired_entry(cached_entry, key, options)
315
324
  entry = nil if entry && entry.mismatched?(normalize_version(name, options))
316
325
  payload[:super_operation] = :fetch if payload
@@ -318,9 +327,9 @@ module ActiveSupport
318
327
  end
319
328
 
320
329
  if entry
321
- get_entry_value(entry, name, options)
330
+ get_entry_value(entry, name, **options)
322
331
  else
323
- save_block_result_to_cache(name, options) { |_name| yield _name }
332
+ save_block_result_to_cache(name, **options) { |_name| yield _name }
324
333
  end
325
334
  elsif options && options[:force]
326
335
  raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
@@ -333,8 +342,9 @@ module ActiveSupport
333
342
  # the cache with the given key, then that data is returned. Otherwise,
334
343
  # +nil+ is returned.
335
344
  #
336
- # Note, if data was written with the <tt>:expires_in<tt> or <tt>:version</tt> options,
337
- # both of these conditions are applied before the data is returned.
345
+ # Note, if data was written with the <tt>:expires_in</tt> or
346
+ # <tt>:version</tt> options, both of these conditions are applied before
347
+ # the data is returned.
338
348
  #
339
349
  # Options are passed to the underlying cache implementation.
340
350
  def read(name, options = nil)
@@ -343,11 +353,11 @@ module ActiveSupport
343
353
  version = normalize_version(name, options)
344
354
 
345
355
  instrument(:read, name, options) do |payload|
346
- entry = read_entry(key, options)
356
+ entry = read_entry(key, **options)
347
357
 
348
358
  if entry
349
359
  if entry.expired?
350
- delete_entry(key, options)
360
+ delete_entry(key, **options)
351
361
  payload[:hit] = false if payload
352
362
  nil
353
363
  elsif entry.mismatched?(version)
@@ -375,7 +385,7 @@ module ActiveSupport
375
385
  options = merged_options(options)
376
386
 
377
387
  instrument :read_multi, names, options do |payload|
378
- read_multi_entries(names, options).tap do |results|
388
+ read_multi_entries(names, **options).tap do |results|
379
389
  payload[:hits] = results.keys
380
390
  end
381
391
  end
@@ -387,10 +397,10 @@ module ActiveSupport
387
397
 
388
398
  instrument :write_multi, hash, options do |payload|
389
399
  entries = hash.each_with_object({}) do |(name, value), memo|
390
- memo[normalize_key(name, options)] = Entry.new(value, options.merge(version: normalize_version(name, options)))
400
+ memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
391
401
  end
392
402
 
393
- write_multi_entries entries, options
403
+ write_multi_entries entries, **options
394
404
  end
395
405
  end
396
406
 
@@ -402,8 +412,6 @@ module ActiveSupport
402
412
  # to the cache. If you do not want to write the cache when the cache is
403
413
  # not found, use #read_multi.
404
414
  #
405
- # Options are passed to the underlying cache implementation.
406
- #
407
415
  # Returns a hash with the data for each of the names. For example:
408
416
  #
409
417
  # cache.write("bim", "bam")
@@ -413,6 +421,17 @@ module ActiveSupport
413
421
  # # => { "bim" => "bam",
414
422
  # # "unknown_key" => "Fallback value for key: unknown_key" }
415
423
  #
424
+ # Options are passed to the underlying cache implementation. For example:
425
+ #
426
+ # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
427
+ # "buzz"
428
+ # end
429
+ # # => {"fizz"=>"buzz"}
430
+ # cache.read("fizz")
431
+ # # => "buzz"
432
+ # sleep(6)
433
+ # cache.read("fizz")
434
+ # # => nil
416
435
  def fetch_multi(*names)
417
436
  raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
418
437
 
@@ -420,18 +439,18 @@ module ActiveSupport
420
439
  options = merged_options(options)
421
440
 
422
441
  instrument :read_multi, names, options do |payload|
423
- read_multi_entries(names, options).tap do |results|
424
- payload[:hits] = results.keys
425
- payload[:super_operation] = :fetch_multi
442
+ reads = read_multi_entries(names, **options)
443
+ writes = {}
444
+ ordered = names.each_with_object({}) do |name, hash|
445
+ hash[name] = reads.fetch(name) { writes[name] = yield(name) }
446
+ end
426
447
 
427
- writes = {}
448
+ payload[:hits] = reads.keys
449
+ payload[:super_operation] = :fetch_multi
428
450
 
429
- (names - results.keys).each do |name|
430
- results[name] = writes[name] = yield(name)
431
- end
451
+ write_multi(writes, **options)
432
452
 
433
- write_multi writes, options
434
- end
453
+ ordered
435
454
  end
436
455
  end
437
456
 
@@ -442,8 +461,8 @@ module ActiveSupport
442
461
  options = merged_options(options)
443
462
 
444
463
  instrument(:write, name, options) do
445
- entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
446
- write_entry(normalize_key(name, options), entry, options)
464
+ entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
465
+ write_entry(normalize_key(name, options), entry, **options)
447
466
  end
448
467
  end
449
468
 
@@ -454,7 +473,7 @@ module ActiveSupport
454
473
  options = merged_options(options)
455
474
 
456
475
  instrument(:delete, name) do
457
- delete_entry(normalize_key(name, options), options)
476
+ delete_entry(normalize_key(name, options), **options)
458
477
  end
459
478
  end
460
479
 
@@ -465,7 +484,7 @@ module ActiveSupport
465
484
  options = merged_options(options)
466
485
 
467
486
  instrument(:exist?, name) do
468
- entry = read_entry(normalize_key(name, options), options)
487
+ entry = read_entry(normalize_key(name, options), **options)
469
488
  (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
470
489
  end
471
490
  end
@@ -474,7 +493,7 @@ module ActiveSupport
474
493
  #
475
494
  # Options are passed to the underlying cache implementation.
476
495
  #
477
- # All implementations may not support this method.
496
+ # Some implementations may not support this method.
478
497
  def delete_matched(matcher, options = nil)
479
498
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
480
499
  end
@@ -483,7 +502,7 @@ module ActiveSupport
483
502
  #
484
503
  # Options are passed to the underlying cache implementation.
485
504
  #
486
- # All implementations may not support this method.
505
+ # Some implementations may not support this method.
487
506
  def increment(name, amount = 1, options = nil)
488
507
  raise NotImplementedError.new("#{self.class.name} does not support increment")
489
508
  end
@@ -492,7 +511,7 @@ module ActiveSupport
492
511
  #
493
512
  # Options are passed to the underlying cache implementation.
494
513
  #
495
- # All implementations may not support this method.
514
+ # Some implementations may not support this method.
496
515
  def decrement(name, amount = 1, options = nil)
497
516
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
498
517
  end
@@ -501,7 +520,7 @@ module ActiveSupport
501
520
  #
502
521
  # Options are passed to the underlying cache implementation.
503
522
  #
504
- # All implementations may not support this method.
523
+ # Some implementations may not support this method.
505
524
  def cleanup(options = nil)
506
525
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
507
526
  end
@@ -511,7 +530,7 @@ module ActiveSupport
511
530
  #
512
531
  # The options hash is passed to the underlying cache implementation.
513
532
  #
514
- # All implementations may not support this method.
533
+ # Some implementations may not support this method.
515
534
  def clear(options = nil)
516
535
  raise NotImplementedError.new("#{self.class.name} does not support clear")
517
536
  end
@@ -538,28 +557,28 @@ module ActiveSupport
538
557
 
539
558
  # Reads an entry from the cache implementation. Subclasses must implement
540
559
  # this method.
541
- def read_entry(key, options)
560
+ def read_entry(key, **options)
542
561
  raise NotImplementedError.new
543
562
  end
544
563
 
545
564
  # Writes an entry to the cache implementation. Subclasses must implement
546
565
  # this method.
547
- def write_entry(key, entry, options)
566
+ def write_entry(key, entry, **options)
548
567
  raise NotImplementedError.new
549
568
  end
550
569
 
551
570
  # Reads multiple entries from the cache implementation. Subclasses MAY
552
571
  # implement this method.
553
- def read_multi_entries(names, options)
572
+ def read_multi_entries(names, **options)
554
573
  results = {}
555
574
  names.each do |name|
556
575
  key = normalize_key(name, options)
557
576
  version = normalize_version(name, options)
558
- entry = read_entry(key, options)
577
+ entry = read_entry(key, **options)
559
578
 
560
579
  if entry
561
580
  if entry.expired?
562
- delete_entry(key, options)
581
+ delete_entry(key, **options)
563
582
  elsif entry.mismatched?(version)
564
583
  # Skip mismatched versions
565
584
  else
@@ -572,24 +591,28 @@ module ActiveSupport
572
591
 
573
592
  # Writes multiple entries to the cache implementation. Subclasses MAY
574
593
  # implement this method.
575
- def write_multi_entries(hash, options)
594
+ def write_multi_entries(hash, **options)
576
595
  hash.each do |key, entry|
577
- write_entry key, entry, options
596
+ write_entry key, entry, **options
578
597
  end
579
598
  end
580
599
 
581
600
  # Deletes an entry from the cache implementation. Subclasses must
582
601
  # implement this method.
583
- def delete_entry(key, options)
602
+ def delete_entry(key, **options)
584
603
  raise NotImplementedError.new
585
604
  end
586
605
 
587
606
  # Merges the default options with ones specific to a method call.
588
607
  def merged_options(call_options)
589
608
  if call_options
590
- options.merge(call_options)
609
+ if options.empty?
610
+ call_options
611
+ else
612
+ options.merge(call_options)
613
+ end
591
614
  else
592
- options.dup
615
+ options
593
616
  end
594
617
  end
595
618
 
@@ -634,7 +657,7 @@ module ActiveSupport
634
657
  if key.size > 1
635
658
  key = key.collect { |element| expanded_key(element) }
636
659
  else
637
- key = key.first
660
+ key = expanded_key(key.first)
638
661
  end
639
662
  when Hash
640
663
  key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
@@ -677,7 +700,7 @@ module ActiveSupport
677
700
  entry.expires_at = Time.now + race_ttl
678
701
  write_entry(key, entry, expires_in: race_ttl * 2)
679
702
  else
680
- delete_entry(key, options)
703
+ delete_entry(key, **options)
681
704
  end
682
705
  entry = nil
683
706
  end
@@ -685,16 +708,16 @@ module ActiveSupport
685
708
  end
686
709
 
687
710
  def get_entry_value(entry, name, options)
688
- instrument(:fetch_hit, name, options) {}
711
+ instrument(:fetch_hit, name, options) { }
689
712
  entry.value
690
713
  end
691
714
 
692
- def save_block_result_to_cache(name, options)
715
+ def save_block_result_to_cache(name, **options)
693
716
  result = instrument(:generate, name, options) do
694
717
  yield(name)
695
718
  end
696
719
 
697
- write(name, result, options)
720
+ write(name, result, options) unless result.nil? && options[:skip_nil]
698
721
  result
699
722
  end
700
723
  end
@@ -712,17 +735,14 @@ module ActiveSupport
712
735
  DEFAULT_COMPRESS_LIMIT = 1.kilobyte
713
736
 
714
737
  # Creates a new cache entry for the specified value. Options supported are
715
- # +:compress+, +:compress_threshold+, and +:expires_in+.
716
- def initialize(value, options = {})
717
- @value = value
718
- if should_compress?(options)
719
- compress!
720
- end
721
-
722
- @version = options[:version]
738
+ # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+.
739
+ def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **)
740
+ @value = value
741
+ @version = version
723
742
  @created_at = Time.now.to_f
724
- @expires_in = options[:expires_in]
725
- @expires_in = @expires_in.to_f if @expires_in
743
+ @expires_in = expires_in && expires_in.to_f
744
+
745
+ compress!(compress_threshold) if compress
726
746
  end
727
747
 
728
748
  def value
@@ -754,17 +774,13 @@ module ActiveSupport
754
774
  # Returns the size of the cached value. This could be less than
755
775
  # <tt>value.size</tt> if the data is compressed.
756
776
  def size
757
- if defined?(@s)
758
- @s
777
+ case value
778
+ when NilClass
779
+ 0
780
+ when String
781
+ @value.bytesize
759
782
  else
760
- case value
761
- when NilClass
762
- 0
763
- when String
764
- @value.bytesize
765
- else
766
- @s = Marshal.dump(@value).bytesize
767
- end
783
+ @s ||= Marshal.dump(@value).bytesize
768
784
  end
769
785
  end
770
786
 
@@ -781,31 +797,35 @@ module ActiveSupport
781
797
  end
782
798
 
783
799
  private
784
- def should_compress?(options)
785
- if @value && options.fetch(:compress, true)
786
- compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
787
- serialized_value_size = (@value.is_a?(String) ? @value : marshaled_value).bytesize
800
+ def compress!(compress_threshold)
801
+ case @value
802
+ when nil, true, false, Numeric
803
+ uncompressed_size = 0
804
+ when String
805
+ uncompressed_size = @value.bytesize
806
+ else
807
+ serialized = Marshal.dump(@value)
808
+ uncompressed_size = serialized.bytesize
809
+ end
810
+
811
+ if uncompressed_size >= compress_threshold
812
+ serialized ||= Marshal.dump(@value)
813
+ compressed = Zlib::Deflate.deflate(serialized)
788
814
 
789
- serialized_value_size >= compress_threshold
815
+ if compressed.bytesize < uncompressed_size
816
+ @value = compressed
817
+ @compressed = true
818
+ end
790
819
  end
791
820
  end
792
821
 
793
822
  def compressed?
794
- defined?(@compressed) ? @compressed : false
795
- end
796
-
797
- def compress!
798
- @value = Zlib::Deflate.deflate(marshaled_value)
799
- @compressed = true
823
+ defined?(@compressed)
800
824
  end
801
825
 
802
826
  def uncompress(value)
803
827
  Marshal.load(Zlib::Inflate.inflate(value))
804
828
  end
805
-
806
- def marshaled_value
807
- @marshaled_value ||= Marshal.dump(@value)
808
- end
809
829
  end
810
830
  end
811
831
  end