activesupport 6.0.3.6 → 6.1.1

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -475
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support.rb +13 -1
  5. data/lib/active_support/array_inquirer.rb +4 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache.rb +85 -44
  9. data/lib/active_support/cache/file_store.rb +4 -3
  10. data/lib/active_support/cache/mem_cache_store.rb +21 -14
  11. data/lib/active_support/cache/memory_store.rb +46 -26
  12. data/lib/active_support/cache/redis_cache_store.rb +27 -27
  13. data/lib/active_support/cache/strategy/local_cache.rb +21 -6
  14. data/lib/active_support/callbacks.rb +65 -56
  15. data/lib/active_support/concern.rb +46 -2
  16. data/lib/active_support/configurable.rb +3 -3
  17. data/lib/active_support/configuration_file.rb +46 -0
  18. data/lib/active_support/core_ext.rb +1 -1
  19. data/lib/active_support/core_ext/benchmark.rb +2 -2
  20. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  21. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  22. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  23. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  24. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  25. data/lib/active_support/core_ext/enumerable.rb +76 -4
  26. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  27. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  28. data/lib/active_support/core_ext/hash/except.rb +1 -1
  29. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  30. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  31. data/lib/active_support/core_ext/load_error.rb +1 -1
  32. data/lib/active_support/core_ext/marshal.rb +2 -0
  33. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  34. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  35. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  36. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  37. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  38. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  39. data/lib/active_support/core_ext/name_error.rb +29 -2
  40. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  41. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  42. data/lib/active_support/core_ext/object/json.rb +13 -2
  43. data/lib/active_support/core_ext/object/try.rb +2 -2
  44. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  45. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  46. data/lib/active_support/core_ext/regexp.rb +8 -1
  47. data/lib/active_support/core_ext/string/access.rb +5 -24
  48. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  49. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  50. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  51. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  52. data/lib/active_support/core_ext/string/output_safety.rb +10 -10
  53. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  54. data/lib/active_support/core_ext/symbol.rb +3 -0
  55. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  56. data/lib/active_support/core_ext/time/calculations.rb +19 -1
  57. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  58. data/lib/active_support/core_ext/uri.rb +5 -1
  59. data/lib/active_support/current_attributes.rb +7 -2
  60. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  61. data/lib/active_support/dependencies.rb +43 -19
  62. data/lib/active_support/deprecation.rb +6 -1
  63. data/lib/active_support/deprecation/behaviors.rb +15 -2
  64. data/lib/active_support/deprecation/disallowed.rb +56 -0
  65. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  66. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  67. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  68. data/lib/active_support/deprecation/reporting.rb +50 -7
  69. data/lib/active_support/descendants_tracker.rb +6 -2
  70. data/lib/active_support/duration.rb +71 -22
  71. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  72. data/lib/active_support/encrypted_file.rb +19 -2
  73. data/lib/active_support/environment_inquirer.rb +20 -0
  74. data/lib/active_support/evented_file_update_checker.rb +69 -133
  75. data/lib/active_support/fork_tracker.rb +62 -0
  76. data/lib/active_support/gem_version.rb +3 -3
  77. data/lib/active_support/hash_with_indifferent_access.rb +42 -23
  78. data/lib/active_support/i18n_railtie.rb +14 -19
  79. data/lib/active_support/inflector/inflections.rb +1 -2
  80. data/lib/active_support/inflector/methods.rb +35 -31
  81. data/lib/active_support/inflector/transliterate.rb +4 -4
  82. data/lib/active_support/json/decoding.rb +4 -4
  83. data/lib/active_support/json/encoding.rb +5 -1
  84. data/lib/active_support/key_generator.rb +1 -1
  85. data/lib/active_support/locale/en.yml +7 -3
  86. data/lib/active_support/log_subscriber.rb +8 -0
  87. data/lib/active_support/logger.rb +1 -1
  88. data/lib/active_support/logger_silence.rb +2 -26
  89. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  90. data/lib/active_support/message_encryptor.rb +4 -7
  91. data/lib/active_support/message_verifier.rb +5 -5
  92. data/lib/active_support/messages/metadata.rb +9 -1
  93. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  94. data/lib/active_support/messages/rotator.rb +6 -5
  95. data/lib/active_support/multibyte/chars.rb +4 -42
  96. data/lib/active_support/multibyte/unicode.rb +9 -83
  97. data/lib/active_support/notifications.rb +31 -4
  98. data/lib/active_support/notifications/fanout.rb +23 -8
  99. data/lib/active_support/notifications/instrumenter.rb +6 -15
  100. data/lib/active_support/number_helper.rb +29 -14
  101. data/lib/active_support/number_helper/number_converter.rb +1 -1
  102. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
  103. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  104. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  105. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  106. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  107. data/lib/active_support/option_merger.rb +3 -2
  108. data/lib/active_support/ordered_options.rb +8 -2
  109. data/lib/active_support/parameter_filter.rb +15 -10
  110. data/lib/active_support/per_thread_registry.rb +1 -1
  111. data/lib/active_support/rails.rb +1 -4
  112. data/lib/active_support/railtie.rb +23 -1
  113. data/lib/active_support/rescuable.rb +4 -4
  114. data/lib/active_support/secure_compare_rotator.rb +51 -0
  115. data/lib/active_support/security_utils.rb +19 -12
  116. data/lib/active_support/string_inquirer.rb +4 -2
  117. data/lib/active_support/subscriber.rb +12 -7
  118. data/lib/active_support/tagged_logging.rb +29 -4
  119. data/lib/active_support/testing/assertions.rb +18 -11
  120. data/lib/active_support/testing/parallelization.rb +12 -95
  121. data/lib/active_support/testing/parallelization/server.rb +78 -0
  122. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  123. data/lib/active_support/testing/time_helpers.rb +40 -3
  124. data/lib/active_support/time_with_zone.rb +66 -42
  125. data/lib/active_support/values/time_zone.rb +20 -10
  126. data/lib/active_support/xml_mini/rexml.rb +8 -1
  127. metadata +37 -39
  128. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  129. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  130. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  131. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  132. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  133. data/lib/active_support/core_ext/range/include_range.rb +0 -9
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005-2019 David Heinemeier Hansson
1
+ Copyright (c) 2005-2020 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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2005-2019 David Heinemeier Hansson
4
+ # Copyright (c) 2005-2020 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
@@ -35,6 +35,7 @@ module ActiveSupport
35
35
 
36
36
  autoload :Concern
37
37
  autoload :ActionableError
38
+ autoload :ConfigurationFile
38
39
  autoload :CurrentAttributes
39
40
  autoload :Dependencies
40
41
  autoload :DescendantsTracker
@@ -42,9 +43,11 @@ module ActiveSupport
42
43
  autoload :Executor
43
44
  autoload :FileUpdateChecker
44
45
  autoload :EventedFileUpdateChecker
46
+ autoload :ForkTracker
45
47
  autoload :LogSubscriber
46
48
  autoload :Notifications
47
49
  autoload :Reloader
50
+ autoload :SecureCompareRotator
48
51
 
49
52
  eager_autoload do
50
53
  autoload :BacktraceCleaner
@@ -67,6 +70,7 @@ module ActiveSupport
67
70
  autoload :OrderedHash
68
71
  autoload :OrderedOptions
69
72
  autoload :StringInquirer
73
+ autoload :EnvironmentInquirer
70
74
  autoload :TaggedLogging
71
75
  autoload :XmlMini
72
76
  autoload :ArrayInquirer
@@ -91,6 +95,14 @@ module ActiveSupport
91
95
  def self.to_time_preserves_timezone=(value)
92
96
  DateAndTime::Compatibility.preserve_timezone = value
93
97
  end
98
+
99
+ def self.utc_to_local_returns_utc_offset_times
100
+ DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times
101
+ end
102
+
103
+ def self.utc_to_local_returns_utc_offset_times=(value)
104
+ DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times = value
105
+ end
94
106
  end
95
107
 
96
108
  autoload :I18n, "active_support/i18n"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module ActiveSupport
4
6
  # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
5
7
  # its string-like contents:
@@ -34,11 +36,11 @@ module ActiveSupport
34
36
 
35
37
  private
36
38
  def respond_to_missing?(name, include_private = false)
37
- (name[-1] == "?") || super
39
+ name.end_with?("?") || super
38
40
  end
39
41
 
40
42
  def method_missing(name, *args)
41
- if name[-1] == "?"
43
+ if name.end_with?("?")
42
44
  any?(name[0..-2])
43
45
  else
44
46
  super
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  #
17
17
  # bc = ActiveSupport::BacktraceCleaner.new
18
18
  # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
19
- # bc.add_silencer { |line| line =~ /puma|rubygems/ } # skip any lines from puma or rubygems
19
+ # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
20
20
  # bc.clean(exception.backtrace) # perform the cleanup
21
21
  #
22
22
  # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
@@ -65,7 +65,7 @@ module ActiveSupport
65
65
  # for a given line, it will be excluded from the clean backtrace.
66
66
  #
67
67
  # # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
68
- # backtrace_cleaner.add_silencer { |line| line =~ /puma/ }
68
+ # backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
69
69
  def add_silencer(&block)
70
70
  @silencers << block
71
71
  end
@@ -91,7 +91,7 @@ module ActiveSupport
91
91
  gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
92
92
  return if gems_paths.empty?
93
93
 
94
- gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
94
+ gems_regexp = %r{\A(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
95
95
  gems_result = '\3 (\4) \5'
96
96
  add_filter { |line| line.sub(gems_regexp, gems_result) }
97
97
  end
@@ -41,7 +41,7 @@ module ActiveSupport
41
41
 
42
42
  result = nil
43
43
  ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
44
- logger.send(options[:level], "%s (%.1fms)" % [ message, ms ])
44
+ logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
45
45
  result
46
46
  else
47
47
  yield
@@ -3,10 +3,12 @@
3
3
  require "zlib"
4
4
  require "active_support/core_ext/array/extract_options"
5
5
  require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/enumerable"
6
7
  require "active_support/core_ext/module/attribute_accessors"
7
8
  require "active_support/core_ext/numeric/bytes"
8
9
  require "active_support/core_ext/numeric/time"
9
10
  require "active_support/core_ext/object/to_param"
11
+ require "active_support/core_ext/object/try"
10
12
  require "active_support/core_ext/string/inflections"
11
13
 
12
14
  module ActiveSupport
@@ -20,7 +22,7 @@ module ActiveSupport
20
22
 
21
23
  # These options mean something to all cache implementations. Individual cache
22
24
  # implementations may support additional options.
23
- UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
25
+ UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl, :coder]
24
26
 
25
27
  module Strategy
26
28
  autoload :LocalCache, "active_support/cache/strategy/local_cache"
@@ -79,7 +81,7 @@ module ActiveSupport
79
81
  #
80
82
  # The +key+ argument can also respond to +cache_key+ or +to_param+.
81
83
  def expand_cache_key(key, namespace = nil)
82
- expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
84
+ expanded_cache_key = namespace ? +"#{namespace}/" : +""
83
85
 
84
86
  if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
85
87
  expanded_cache_key << "#{prefix}/"
@@ -156,6 +158,8 @@ module ActiveSupport
156
158
  # threshold is configurable with the <tt>:compress_threshold</tt> option,
157
159
  # specified in bytes.
158
160
  class Store
161
+ DEFAULT_CODER = Marshal
162
+
159
163
  cattr_accessor :logger, instance_writer: true
160
164
 
161
165
  attr_reader :silence, :options
@@ -183,6 +187,7 @@ module ActiveSupport
183
187
  # namespace for the cache.
184
188
  def initialize(options = nil)
185
189
  @options = options ? options.dup : {}
190
+ @coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
186
191
  end
187
192
 
188
193
  # Silences the logger.
@@ -312,14 +317,14 @@ module ActiveSupport
312
317
  # :bar
313
318
  # end
314
319
  # cache.fetch('foo') # => "bar"
315
- def fetch(name, options = nil)
320
+ def fetch(name, options = nil, &block)
316
321
  if block_given?
317
322
  options = merged_options(options)
318
323
  key = normalize_key(name, options)
319
324
 
320
325
  entry = nil
321
326
  instrument(:read, name, options) do |payload|
322
- cached_entry = read_entry(key, **options) unless options[:force]
327
+ cached_entry = read_entry(key, **options, event: payload) unless options[:force]
323
328
  entry = handle_expired_entry(cached_entry, key, options)
324
329
  entry = nil if entry && entry.mismatched?(normalize_version(name, options))
325
330
  payload[:super_operation] = :fetch if payload
@@ -327,9 +332,9 @@ module ActiveSupport
327
332
  end
328
333
 
329
334
  if entry
330
- get_entry_value(entry, name, **options)
335
+ get_entry_value(entry, name, options)
331
336
  else
332
- save_block_result_to_cache(name, **options) { |_name| yield _name }
337
+ save_block_result_to_cache(name, options, &block)
333
338
  end
334
339
  elsif options && options[:force]
335
340
  raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
@@ -353,7 +358,7 @@ module ActiveSupport
353
358
  version = normalize_version(name, options)
354
359
 
355
360
  instrument(:read, name, options) do |payload|
356
- entry = read_entry(key, **options)
361
+ entry = read_entry(key, **options, event: payload)
357
362
 
358
363
  if entry
359
364
  if entry.expired?
@@ -385,7 +390,7 @@ module ActiveSupport
385
390
  options = merged_options(options)
386
391
 
387
392
  instrument :read_multi, names, options do |payload|
388
- read_multi_entries(names, **options).tap do |results|
393
+ read_multi_entries(names, **options, event: payload).tap do |results|
389
394
  payload[:hits] = results.keys
390
395
  end
391
396
  end
@@ -441,14 +446,14 @@ module ActiveSupport
441
446
  instrument :read_multi, names, options do |payload|
442
447
  reads = read_multi_entries(names, **options)
443
448
  writes = {}
444
- ordered = names.each_with_object({}) do |name, hash|
445
- hash[name] = reads.fetch(name) { writes[name] = yield(name) }
449
+ ordered = names.index_with do |name|
450
+ reads.fetch(name) { writes[name] = yield(name) }
446
451
  end
447
452
 
448
453
  payload[:hits] = reads.keys
449
454
  payload[:super_operation] = :fetch_multi
450
455
 
451
- write_multi(writes, **options)
456
+ write_multi(writes, options)
452
457
 
453
458
  ordered
454
459
  end
@@ -477,14 +482,26 @@ module ActiveSupport
477
482
  end
478
483
  end
479
484
 
485
+ # Deletes multiple entries in the cache.
486
+ #
487
+ # Options are passed to the underlying cache implementation.
488
+ def delete_multi(names, options = nil)
489
+ options = merged_options(options)
490
+ names.map! { |key| normalize_key(key, options) }
491
+
492
+ instrument :delete_multi, names do
493
+ delete_multi_entries(names, **options)
494
+ end
495
+ end
496
+
480
497
  # Returns +true+ if the cache contains an entry for the given key.
481
498
  #
482
499
  # Options are passed to the underlying cache implementation.
483
500
  def exist?(name, options = nil)
484
501
  options = merged_options(options)
485
502
 
486
- instrument(:exist?, name) do
487
- entry = read_entry(normalize_key(name, options), **options)
503
+ instrument(:exist?, name) do |payload|
504
+ entry = read_entry(normalize_key(name, options), **options, event: payload)
488
505
  (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
489
506
  end
490
507
  end
@@ -567,26 +584,31 @@ module ActiveSupport
567
584
  raise NotImplementedError.new
568
585
  end
569
586
 
587
+ def serialize_entry(entry)
588
+ @coder.dump(entry)
589
+ end
590
+
591
+ def deserialize_entry(payload)
592
+ payload.nil? ? nil : @coder.load(payload)
593
+ end
594
+
570
595
  # Reads multiple entries from the cache implementation. Subclasses MAY
571
596
  # implement this method.
572
597
  def read_multi_entries(names, **options)
573
- results = {}
574
- names.each do |name|
575
- key = normalize_key(name, options)
598
+ names.each_with_object({}) do |name, results|
599
+ key = normalize_key(name, options)
600
+ entry = read_entry(key, **options)
601
+
602
+ next unless entry
603
+
576
604
  version = normalize_version(name, options)
577
- entry = read_entry(key, **options)
578
-
579
- if entry
580
- if entry.expired?
581
- delete_entry(key, **options)
582
- elsif entry.mismatched?(version)
583
- # Skip mismatched versions
584
- else
585
- results[name] = entry.value
586
- end
605
+
606
+ if entry.expired?
607
+ delete_entry(key, **options)
608
+ elsif !entry.mismatched?(version)
609
+ results[name] = entry.value
587
610
  end
588
611
  end
589
- results
590
612
  end
591
613
 
592
614
  # Writes multiple entries to the cache implementation. Subclasses MAY
@@ -603,6 +625,12 @@ module ActiveSupport
603
625
  raise NotImplementedError.new
604
626
  end
605
627
 
628
+ # Deletes multiples entries in the cache implementation. Subclasses MAY
629
+ # implement this method.
630
+ def delete_multi_entries(entries, **options)
631
+ entries.count { |key| delete_entry(key, **options) }
632
+ end
633
+
606
634
  # Merges the default options with ones specific to a method call.
607
635
  def merged_options(call_options)
608
636
  if call_options
@@ -639,6 +667,10 @@ module ActiveSupport
639
667
  namespace = namespace.call
640
668
  end
641
669
 
670
+ if key && key.encoding != Encoding::UTF_8
671
+ key = key.dup.force_encoding(Encoding::UTF_8)
672
+ end
673
+
642
674
  if namespace
643
675
  "#{namespace}:#{key}"
644
676
  else
@@ -655,15 +687,15 @@ module ActiveSupport
655
687
  case key
656
688
  when Array
657
689
  if key.size > 1
658
- key = key.collect { |element| expanded_key(element) }
690
+ key.collect { |element| expanded_key(element) }
659
691
  else
660
- key = expanded_key(key.first)
692
+ expanded_key(key.first)
661
693
  end
662
694
  when Hash
663
- key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
664
- end
665
-
666
- key.to_param
695
+ key.collect { |k, v| "#{k}=#{v}" }.sort!
696
+ else
697
+ key
698
+ end.to_param
667
699
  end
668
700
 
669
701
  def normalize_version(key, options = nil)
@@ -673,24 +705,21 @@ module ActiveSupport
673
705
  def expanded_version(key)
674
706
  case
675
707
  when key.respond_to?(:cache_version) then key.cache_version.to_param
676
- when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
708
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
677
709
  when key.respond_to?(:to_a) then expanded_version(key.to_a)
678
710
  end
679
711
  end
680
712
 
681
713
  def instrument(operation, key, options = nil)
682
- log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
714
+ if logger && logger.debug? && !silence?
715
+ logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
716
+ end
683
717
 
684
- payload = { key: key }
718
+ payload = { key: key, store: self.class.name }
685
719
  payload.merge!(options) if options.is_a?(Hash)
686
720
  ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
687
721
  end
688
722
 
689
- def log
690
- return unless logger && logger.debug? && !silence?
691
- logger.debug(yield)
692
- end
693
-
694
723
  def handle_expired_entry(entry, key, options)
695
724
  if entry && entry.expired?
696
725
  race_ttl = options[:race_condition_ttl].to_i
@@ -712,7 +741,7 @@ module ActiveSupport
712
741
  entry.value
713
742
  end
714
743
 
715
- def save_block_result_to_cache(name, **options)
744
+ def save_block_result_to_cache(name, options)
716
745
  result = instrument(:generate, name, options) do
717
746
  yield(name)
718
747
  end
@@ -722,6 +751,18 @@ module ActiveSupport
722
751
  end
723
752
  end
724
753
 
754
+ module NullCoder # :nodoc:
755
+ class << self
756
+ def load(payload)
757
+ payload
758
+ end
759
+
760
+ def dump(entry)
761
+ entry
762
+ end
763
+ end
764
+ end
765
+
725
766
  # This class is used to represent cache entries. Cache entries have a value, an optional
726
767
  # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
727
768
  # on the cache. The version is used to support the :version option on the cache for rejecting
@@ -772,8 +813,8 @@ module ActiveSupport
772
813
  end
773
814
 
774
815
  # Returns the size of the cached value. This could be less than
775
- # <tt>value.size</tt> if the data is compressed.
776
- def size
816
+ # <tt>value.bytesize</tt> if the data is compressed.
817
+ def bytesize
777
818
  case value
778
819
  when NilClass
779
820
  0
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  attr_reader :cache_path
17
17
 
18
18
  DIR_FORMATTER = "%03X"
19
- FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
19
+ FILENAME_MAX_SIZE = 226 # max filename size on file system is 255, minus room for timestamp, pid, and random characters appended by Tempfile (used by atomic write)
20
20
  FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
21
21
  GITKEEP_FILES = [".gitkeep", ".keep"].freeze
22
22
 
@@ -74,7 +74,8 @@ module ActiveSupport
74
74
  private
75
75
  def read_entry(key, **options)
76
76
  if File.exist?(key)
77
- File.open(key) { |f| Marshal.load(f) }
77
+ entry = File.open(key) { |f| deserialize_entry(f.read) }
78
+ entry if entry.is_a?(Cache::Entry)
78
79
  end
79
80
  rescue => e
80
81
  logger.error("FileStoreError (#{e}): #{e.message}") if logger
@@ -84,7 +85,7 @@ module ActiveSupport
84
85
  def write_entry(key, entry, **options)
85
86
  return false if options[:unless_exist] && File.exist?(key)
86
87
  ensure_cache_path(File.dirname(key))
87
- File.atomic_write(key, cache_path) { |f| Marshal.dump(entry, f) }
88
+ File.atomic_write(key, cache_path) { |f| f.write(serialize_entry(entry)) }
88
89
  true
89
90
  end
90
91
 
@@ -7,6 +7,8 @@ rescue LoadError => e
7
7
  raise e
8
8
  end
9
9
 
10
+ require "active_support/core_ext/enumerable"
11
+ require "active_support/core_ext/marshal"
10
12
  require "active_support/core_ext/array/extract_options"
11
13
 
12
14
  module ActiveSupport
@@ -24,6 +26,8 @@ module ActiveSupport
24
26
  # MemCacheStore implements the Strategy::LocalCache strategy which implements
25
27
  # an in-memory cache inside of a block.
26
28
  class MemCacheStore < Store
29
+ DEFAULT_CODER = NullCoder # Dalli automatically Marshal values
30
+
27
31
  # Provide support for raw values in the local cache strategy.
28
32
  module LocalCacheWithRaw # :nodoc:
29
33
  private
@@ -49,16 +53,18 @@ module ActiveSupport
49
53
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
50
54
 
51
55
  # Creates a new Dalli::Client instance with specified addresses and options.
52
- # By default address is equal localhost:11211.
56
+ # If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
57
+ # - ENV["MEMCACHE_SERVERS"] (if defined)
58
+ # - "127.0.0.1:11211" (otherwise)
53
59
  #
54
60
  # ActiveSupport::Cache::MemCacheStore.build_mem_cache
55
- # # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
61
+ # # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
56
62
  # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
57
63
  # # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
58
64
  def self.build_mem_cache(*addresses) # :nodoc:
59
65
  addresses = addresses.flatten
60
66
  options = addresses.extract_options!
61
- addresses = ["localhost:11211"] if addresses.empty?
67
+ addresses = nil if addresses.empty?
62
68
  pool_options = retrieve_pool_options(options)
63
69
 
64
70
  if pool_options.empty?
@@ -75,8 +81,8 @@ module ActiveSupport
75
81
  #
76
82
  # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
77
83
  #
78
- # If no addresses are specified, then MemCacheStore will connect to
79
- # localhost port 11211 (the default memcached port).
84
+ # If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
85
+ # MemCacheStore will connect to localhost:11211 (the default memcached port).
80
86
  def initialize(*addresses)
81
87
  addresses = addresses.flatten
82
88
  options = addresses.extract_options!
@@ -139,21 +145,22 @@ module ActiveSupport
139
145
 
140
146
  # Write an entry to the cache.
141
147
  def write_entry(key, entry, **options)
142
- method = options && options[:unless_exist] ? :add : :set
143
- value = options[:raw] ? entry.value.to_s : entry
148
+ method = options[:unless_exist] ? :add : :set
149
+ value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
144
150
  expires_in = options[:expires_in].to_i
145
- if expires_in > 0 && !options[:raw]
151
+ if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
146
152
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
147
153
  expires_in += 5.minutes
148
154
  end
149
155
  rescue_error_with false do
150
- @data.with { |c| c.send(method, key, value, expires_in, **options) }
156
+ # The value "compress: false" prevents duplicate compression within Dalli.
157
+ @data.with { |c| c.send(method, key, value, expires_in, **options, compress: false) }
151
158
  end
152
159
  end
153
160
 
154
161
  # Reads multiple entries from the cache implementation.
155
162
  def read_multi_entries(names, **options)
156
- keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
163
+ keys_to_names = names.index_by { |name| normalize_key(name, options) }
157
164
 
158
165
  raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
159
166
  values = {}
@@ -185,10 +192,10 @@ module ActiveSupport
185
192
  key
186
193
  end
187
194
 
188
- def deserialize_entry(entry)
189
- if entry
190
- entry.is_a?(Entry) ? entry : Entry.new(entry)
191
- end
195
+ def deserialize_entry(payload)
196
+ entry = super
197
+ entry = Entry.new(entry, compress: false) if entry && !entry.is_a?(Entry)
198
+ entry
192
199
  end
193
200
 
194
201
  def rescue_error_with(fallback)