activesupport 5.2.4.4 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +353 -435
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support.rb +14 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +142 -78
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +32 -20
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +70 -43
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +171 -75
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +14 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +34 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +70 -13
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +16 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +64 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +50 -6
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +32 -17
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- metadata +58 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -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/
|
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
|
-
*
|
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://
|
40
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
data/lib/active_support.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2005-
|
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
|
@@ -34,6 +34,8 @@ module ActiveSupport
|
|
34
34
|
extend ActiveSupport::Autoload
|
35
35
|
|
36
36
|
autoload :Concern
|
37
|
+
autoload :ActionableError
|
38
|
+
autoload :ConfigurationFile
|
37
39
|
autoload :CurrentAttributes
|
38
40
|
autoload :Dependencies
|
39
41
|
autoload :DescendantsTracker
|
@@ -41,9 +43,11 @@ module ActiveSupport
|
|
41
43
|
autoload :Executor
|
42
44
|
autoload :FileUpdateChecker
|
43
45
|
autoload :EventedFileUpdateChecker
|
46
|
+
autoload :ForkTracker
|
44
47
|
autoload :LogSubscriber
|
45
48
|
autoload :Notifications
|
46
49
|
autoload :Reloader
|
50
|
+
autoload :SecureCompareRotator
|
47
51
|
|
48
52
|
eager_autoload do
|
49
53
|
autoload :BacktraceCleaner
|
@@ -66,6 +70,7 @@ module ActiveSupport
|
|
66
70
|
autoload :OrderedHash
|
67
71
|
autoload :OrderedOptions
|
68
72
|
autoload :StringInquirer
|
73
|
+
autoload :EnvironmentInquirer
|
69
74
|
autoload :TaggedLogging
|
70
75
|
autoload :XmlMini
|
71
76
|
autoload :ArrayInquirer
|
@@ -90,6 +95,14 @@ module ActiveSupport
|
|
90
95
|
def self.to_time_preserves_timezone=(value)
|
91
96
|
DateAndTime::Compatibility.preserve_timezone = value
|
92
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
|
93
106
|
end
|
94
107
|
|
95
108
|
autoload :I18n, "active_support/i18n"
|
@@ -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
|
@@ -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
|
-
(
|
39
|
+
name.end_with?("?") || super
|
38
40
|
end
|
39
41
|
|
40
42
|
def method_missing(name, *args)
|
41
|
-
if name
|
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|
|
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)
|
@@ -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
|
@@ -62,7 +65,7 @@ module ActiveSupport
|
|
62
65
|
# for a given line, it will be excluded from the clean backtrace.
|
63
66
|
#
|
64
67
|
# # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
|
65
|
-
# backtrace_cleaner.add_silencer { |line|
|
68
|
+
# backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
|
66
69
|
def add_silencer(&block)
|
67
70
|
@silencers << block
|
68
71
|
end
|
@@ -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{\A(#{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
|
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
|
@@ -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.
|
44
|
+
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
45
45
|
result
|
46
46
|
else
|
47
47
|
yield
|
data/lib/active_support/cache.rb
CHANGED
@@ -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"
|
@@ -52,12 +54,13 @@ module ActiveSupport
|
|
52
54
|
#
|
53
55
|
# ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
|
54
56
|
# # => returns MyOwnCacheStore.new
|
55
|
-
def lookup_store(*
|
56
|
-
store, *parameters = *Array.wrap(store_option).flatten
|
57
|
-
|
57
|
+
def lookup_store(store = nil, *parameters)
|
58
58
|
case store
|
59
59
|
when Symbol
|
60
|
-
|
60
|
+
options = parameters.extract_options!
|
61
|
+
retrieve_store_class(store).new(*parameters, **options)
|
62
|
+
when Array
|
63
|
+
lookup_store(*store)
|
61
64
|
when nil
|
62
65
|
ActiveSupport::Cache::MemoryStore.new
|
63
66
|
else
|
@@ -78,7 +81,7 @@ module ActiveSupport
|
|
78
81
|
#
|
79
82
|
# The +key+ argument can also respond to +cache_key+ or +to_param+.
|
80
83
|
def expand_cache_key(key, namespace = nil)
|
81
|
-
expanded_cache_key =
|
84
|
+
expanded_cache_key = namespace ? +"#{namespace}/" : +""
|
82
85
|
|
83
86
|
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
|
84
87
|
expanded_cache_key << "#{prefix}/"
|
@@ -155,6 +158,8 @@ module ActiveSupport
|
|
155
158
|
# threshold is configurable with the <tt>:compress_threshold</tt> option,
|
156
159
|
# specified in bytes.
|
157
160
|
class Store
|
161
|
+
DEFAULT_CODER = Marshal
|
162
|
+
|
158
163
|
cattr_accessor :logger, instance_writer: true
|
159
164
|
|
160
165
|
attr_reader :silence, :options
|
@@ -182,6 +187,7 @@ module ActiveSupport
|
|
182
187
|
# namespace for the cache.
|
183
188
|
def initialize(options = nil)
|
184
189
|
@options = options ? options.dup : {}
|
190
|
+
@coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
|
185
191
|
end
|
186
192
|
|
187
193
|
# Silences the logger.
|
@@ -229,6 +235,14 @@ module ActiveSupport
|
|
229
235
|
# ask whether you should force a cache write. Otherwise, it's clearer to
|
230
236
|
# just call <tt>Cache#write</tt>.
|
231
237
|
#
|
238
|
+
# Setting <tt>skip_nil: true</tt> will not cache nil result:
|
239
|
+
#
|
240
|
+
# cache.fetch('foo') { nil }
|
241
|
+
# cache.fetch('bar', skip_nil: true) { nil }
|
242
|
+
# cache.exist?('foo') # => true
|
243
|
+
# cache.exist?('bar') # => false
|
244
|
+
#
|
245
|
+
#
|
232
246
|
# Setting <tt>compress: false</tt> disables compression of the cache entry.
|
233
247
|
#
|
234
248
|
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
|
@@ -303,14 +317,14 @@ module ActiveSupport
|
|
303
317
|
# :bar
|
304
318
|
# end
|
305
319
|
# cache.fetch('foo') # => "bar"
|
306
|
-
def fetch(name, options = nil)
|
320
|
+
def fetch(name, options = nil, &block)
|
307
321
|
if block_given?
|
308
322
|
options = merged_options(options)
|
309
323
|
key = normalize_key(name, options)
|
310
324
|
|
311
325
|
entry = nil
|
312
326
|
instrument(:read, name, options) do |payload|
|
313
|
-
cached_entry = read_entry(key, options) unless options[:force]
|
327
|
+
cached_entry = read_entry(key, **options, event: payload) unless options[:force]
|
314
328
|
entry = handle_expired_entry(cached_entry, key, options)
|
315
329
|
entry = nil if entry && entry.mismatched?(normalize_version(name, options))
|
316
330
|
payload[:super_operation] = :fetch if payload
|
@@ -320,7 +334,7 @@ module ActiveSupport
|
|
320
334
|
if entry
|
321
335
|
get_entry_value(entry, name, options)
|
322
336
|
else
|
323
|
-
save_block_result_to_cache(name, options)
|
337
|
+
save_block_result_to_cache(name, options, &block)
|
324
338
|
end
|
325
339
|
elsif options && options[:force]
|
326
340
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -333,8 +347,9 @@ module ActiveSupport
|
|
333
347
|
# the cache with the given key, then that data is returned. Otherwise,
|
334
348
|
# +nil+ is returned.
|
335
349
|
#
|
336
|
-
# Note, if data was written with the <tt>:expires_in
|
337
|
-
# both of these conditions are applied before
|
350
|
+
# Note, if data was written with the <tt>:expires_in</tt> or
|
351
|
+
# <tt>:version</tt> options, both of these conditions are applied before
|
352
|
+
# the data is returned.
|
338
353
|
#
|
339
354
|
# Options are passed to the underlying cache implementation.
|
340
355
|
def read(name, options = nil)
|
@@ -343,11 +358,11 @@ module ActiveSupport
|
|
343
358
|
version = normalize_version(name, options)
|
344
359
|
|
345
360
|
instrument(:read, name, options) do |payload|
|
346
|
-
entry = read_entry(key, options)
|
361
|
+
entry = read_entry(key, **options, event: payload)
|
347
362
|
|
348
363
|
if entry
|
349
364
|
if entry.expired?
|
350
|
-
delete_entry(key, options)
|
365
|
+
delete_entry(key, **options)
|
351
366
|
payload[:hit] = false if payload
|
352
367
|
nil
|
353
368
|
elsif entry.mismatched?(version)
|
@@ -375,7 +390,7 @@ module ActiveSupport
|
|
375
390
|
options = merged_options(options)
|
376
391
|
|
377
392
|
instrument :read_multi, names, options do |payload|
|
378
|
-
read_multi_entries(names, options).tap do |results|
|
393
|
+
read_multi_entries(names, **options, event: payload).tap do |results|
|
379
394
|
payload[:hits] = results.keys
|
380
395
|
end
|
381
396
|
end
|
@@ -387,10 +402,10 @@ module ActiveSupport
|
|
387
402
|
|
388
403
|
instrument :write_multi, hash, options do |payload|
|
389
404
|
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)))
|
405
|
+
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
391
406
|
end
|
392
407
|
|
393
|
-
write_multi_entries entries, options
|
408
|
+
write_multi_entries entries, **options
|
394
409
|
end
|
395
410
|
end
|
396
411
|
|
@@ -402,8 +417,6 @@ module ActiveSupport
|
|
402
417
|
# to the cache. If you do not want to write the cache when the cache is
|
403
418
|
# not found, use #read_multi.
|
404
419
|
#
|
405
|
-
# Options are passed to the underlying cache implementation.
|
406
|
-
#
|
407
420
|
# Returns a hash with the data for each of the names. For example:
|
408
421
|
#
|
409
422
|
# cache.write("bim", "bam")
|
@@ -413,6 +426,17 @@ module ActiveSupport
|
|
413
426
|
# # => { "bim" => "bam",
|
414
427
|
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
415
428
|
#
|
429
|
+
# Options are passed to the underlying cache implementation. For example:
|
430
|
+
#
|
431
|
+
# cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
|
432
|
+
# "buzz"
|
433
|
+
# end
|
434
|
+
# # => {"fizz"=>"buzz"}
|
435
|
+
# cache.read("fizz")
|
436
|
+
# # => "buzz"
|
437
|
+
# sleep(6)
|
438
|
+
# cache.read("fizz")
|
439
|
+
# # => nil
|
416
440
|
def fetch_multi(*names)
|
417
441
|
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
|
418
442
|
|
@@ -420,18 +444,18 @@ module ActiveSupport
|
|
420
444
|
options = merged_options(options)
|
421
445
|
|
422
446
|
instrument :read_multi, names, options do |payload|
|
423
|
-
read_multi_entries(names, options)
|
424
|
-
|
425
|
-
|
447
|
+
reads = read_multi_entries(names, **options)
|
448
|
+
writes = {}
|
449
|
+
ordered = names.index_with do |name|
|
450
|
+
reads.fetch(name) { writes[name] = yield(name) }
|
451
|
+
end
|
426
452
|
|
427
|
-
|
453
|
+
payload[:hits] = reads.keys
|
454
|
+
payload[:super_operation] = :fetch_multi
|
428
455
|
|
429
|
-
|
430
|
-
results[name] = writes[name] = yield(name)
|
431
|
-
end
|
456
|
+
write_multi(writes, options)
|
432
457
|
|
433
|
-
|
434
|
-
end
|
458
|
+
ordered
|
435
459
|
end
|
436
460
|
end
|
437
461
|
|
@@ -442,8 +466,8 @@ module ActiveSupport
|
|
442
466
|
options = merged_options(options)
|
443
467
|
|
444
468
|
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)
|
469
|
+
entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
470
|
+
write_entry(normalize_key(name, options), entry, **options)
|
447
471
|
end
|
448
472
|
end
|
449
473
|
|
@@ -454,7 +478,19 @@ module ActiveSupport
|
|
454
478
|
options = merged_options(options)
|
455
479
|
|
456
480
|
instrument(:delete, name) do
|
457
|
-
delete_entry(normalize_key(name, options), options)
|
481
|
+
delete_entry(normalize_key(name, options), **options)
|
482
|
+
end
|
483
|
+
end
|
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)
|
458
494
|
end
|
459
495
|
end
|
460
496
|
|
@@ -464,8 +500,8 @@ module ActiveSupport
|
|
464
500
|
def exist?(name, options = nil)
|
465
501
|
options = merged_options(options)
|
466
502
|
|
467
|
-
instrument(:exist?, name) do
|
468
|
-
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)
|
469
505
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
470
506
|
end
|
471
507
|
end
|
@@ -474,7 +510,7 @@ module ActiveSupport
|
|
474
510
|
#
|
475
511
|
# Options are passed to the underlying cache implementation.
|
476
512
|
#
|
477
|
-
#
|
513
|
+
# Some implementations may not support this method.
|
478
514
|
def delete_matched(matcher, options = nil)
|
479
515
|
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
|
480
516
|
end
|
@@ -483,7 +519,7 @@ module ActiveSupport
|
|
483
519
|
#
|
484
520
|
# Options are passed to the underlying cache implementation.
|
485
521
|
#
|
486
|
-
#
|
522
|
+
# Some implementations may not support this method.
|
487
523
|
def increment(name, amount = 1, options = nil)
|
488
524
|
raise NotImplementedError.new("#{self.class.name} does not support increment")
|
489
525
|
end
|
@@ -492,7 +528,7 @@ module ActiveSupport
|
|
492
528
|
#
|
493
529
|
# Options are passed to the underlying cache implementation.
|
494
530
|
#
|
495
|
-
#
|
531
|
+
# Some implementations may not support this method.
|
496
532
|
def decrement(name, amount = 1, options = nil)
|
497
533
|
raise NotImplementedError.new("#{self.class.name} does not support decrement")
|
498
534
|
end
|
@@ -501,7 +537,7 @@ module ActiveSupport
|
|
501
537
|
#
|
502
538
|
# Options are passed to the underlying cache implementation.
|
503
539
|
#
|
504
|
-
#
|
540
|
+
# Some implementations may not support this method.
|
505
541
|
def cleanup(options = nil)
|
506
542
|
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
|
507
543
|
end
|
@@ -511,7 +547,7 @@ module ActiveSupport
|
|
511
547
|
#
|
512
548
|
# The options hash is passed to the underlying cache implementation.
|
513
549
|
#
|
514
|
-
#
|
550
|
+
# Some implementations may not support this method.
|
515
551
|
def clear(options = nil)
|
516
552
|
raise NotImplementedError.new("#{self.class.name} does not support clear")
|
517
553
|
end
|
@@ -538,58 +574,73 @@ module ActiveSupport
|
|
538
574
|
|
539
575
|
# Reads an entry from the cache implementation. Subclasses must implement
|
540
576
|
# this method.
|
541
|
-
def read_entry(key, options)
|
577
|
+
def read_entry(key, **options)
|
542
578
|
raise NotImplementedError.new
|
543
579
|
end
|
544
580
|
|
545
581
|
# Writes an entry to the cache implementation. Subclasses must implement
|
546
582
|
# this method.
|
547
|
-
def write_entry(key, entry, options)
|
583
|
+
def write_entry(key, entry, **options)
|
548
584
|
raise NotImplementedError.new
|
549
585
|
end
|
550
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
|
+
|
551
595
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
552
596
|
# implement this method.
|
553
|
-
def read_multi_entries(names, options)
|
554
|
-
|
555
|
-
|
556
|
-
|
597
|
+
def read_multi_entries(names, **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
|
+
|
557
604
|
version = normalize_version(name, options)
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
elsif entry.mismatched?(version)
|
564
|
-
# Skip mismatched versions
|
565
|
-
else
|
566
|
-
results[name] = entry.value
|
567
|
-
end
|
605
|
+
|
606
|
+
if entry.expired?
|
607
|
+
delete_entry(key, **options)
|
608
|
+
elsif !entry.mismatched?(version)
|
609
|
+
results[name] = entry.value
|
568
610
|
end
|
569
611
|
end
|
570
|
-
results
|
571
612
|
end
|
572
613
|
|
573
614
|
# Writes multiple entries to the cache implementation. Subclasses MAY
|
574
615
|
# implement this method.
|
575
|
-
def write_multi_entries(hash, options)
|
616
|
+
def write_multi_entries(hash, **options)
|
576
617
|
hash.each do |key, entry|
|
577
|
-
write_entry key, entry, options
|
618
|
+
write_entry key, entry, **options
|
578
619
|
end
|
579
620
|
end
|
580
621
|
|
581
622
|
# Deletes an entry from the cache implementation. Subclasses must
|
582
623
|
# implement this method.
|
583
|
-
def delete_entry(key, options)
|
624
|
+
def delete_entry(key, **options)
|
584
625
|
raise NotImplementedError.new
|
585
626
|
end
|
586
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
|
+
|
587
634
|
# Merges the default options with ones specific to a method call.
|
588
635
|
def merged_options(call_options)
|
589
636
|
if call_options
|
590
|
-
options.
|
637
|
+
if options.empty?
|
638
|
+
call_options
|
639
|
+
else
|
640
|
+
options.merge(call_options)
|
641
|
+
end
|
591
642
|
else
|
592
|
-
options
|
643
|
+
options
|
593
644
|
end
|
594
645
|
end
|
595
646
|
|
@@ -616,6 +667,10 @@ module ActiveSupport
|
|
616
667
|
namespace = namespace.call
|
617
668
|
end
|
618
669
|
|
670
|
+
if key && key.encoding != Encoding::UTF_8
|
671
|
+
key = key.dup.force_encoding(Encoding::UTF_8)
|
672
|
+
end
|
673
|
+
|
619
674
|
if namespace
|
620
675
|
"#{namespace}:#{key}"
|
621
676
|
else
|
@@ -632,15 +687,15 @@ module ActiveSupport
|
|
632
687
|
case key
|
633
688
|
when Array
|
634
689
|
if key.size > 1
|
635
|
-
key
|
690
|
+
key.collect { |element| expanded_key(element) }
|
636
691
|
else
|
637
|
-
key
|
692
|
+
expanded_key(key.first)
|
638
693
|
end
|
639
694
|
when Hash
|
640
|
-
key
|
641
|
-
|
642
|
-
|
643
|
-
|
695
|
+
key.collect { |k, v| "#{k}=#{v}" }.sort!
|
696
|
+
else
|
697
|
+
key
|
698
|
+
end.to_param
|
644
699
|
end
|
645
700
|
|
646
701
|
def normalize_version(key, options = nil)
|
@@ -650,24 +705,21 @@ module ActiveSupport
|
|
650
705
|
def expanded_version(key)
|
651
706
|
case
|
652
707
|
when key.respond_to?(:cache_version) then key.cache_version.to_param
|
653
|
-
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
|
654
709
|
when key.respond_to?(:to_a) then expanded_version(key.to_a)
|
655
710
|
end
|
656
711
|
end
|
657
712
|
|
658
713
|
def instrument(operation, key, options = nil)
|
659
|
-
|
714
|
+
if logger && logger.debug? && !silence?
|
715
|
+
logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
|
716
|
+
end
|
660
717
|
|
661
|
-
payload = { key: key }
|
718
|
+
payload = { key: key, store: self.class.name }
|
662
719
|
payload.merge!(options) if options.is_a?(Hash)
|
663
720
|
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
|
664
721
|
end
|
665
722
|
|
666
|
-
def log
|
667
|
-
return unless logger && logger.debug? && !silence?
|
668
|
-
logger.debug(yield)
|
669
|
-
end
|
670
|
-
|
671
723
|
def handle_expired_entry(entry, key, options)
|
672
724
|
if entry && entry.expired?
|
673
725
|
race_ttl = options[:race_condition_ttl].to_i
|
@@ -677,7 +729,7 @@ module ActiveSupport
|
|
677
729
|
entry.expires_at = Time.now + race_ttl
|
678
730
|
write_entry(key, entry, expires_in: race_ttl * 2)
|
679
731
|
else
|
680
|
-
delete_entry(key, options)
|
732
|
+
delete_entry(key, **options)
|
681
733
|
end
|
682
734
|
entry = nil
|
683
735
|
end
|
@@ -685,7 +737,7 @@ module ActiveSupport
|
|
685
737
|
end
|
686
738
|
|
687
739
|
def get_entry_value(entry, name, options)
|
688
|
-
instrument(:fetch_hit, name, options) {}
|
740
|
+
instrument(:fetch_hit, name, options) { }
|
689
741
|
entry.value
|
690
742
|
end
|
691
743
|
|
@@ -694,11 +746,23 @@ module ActiveSupport
|
|
694
746
|
yield(name)
|
695
747
|
end
|
696
748
|
|
697
|
-
write(name, result, options)
|
749
|
+
write(name, result, options) unless result.nil? && options[:skip_nil]
|
698
750
|
result
|
699
751
|
end
|
700
752
|
end
|
701
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
|
+
|
702
766
|
# This class is used to represent cache entries. Cache entries have a value, an optional
|
703
767
|
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
704
768
|
# on the cache. The version is used to support the :version option on the cache for rejecting
|
@@ -749,8 +813,8 @@ module ActiveSupport
|
|
749
813
|
end
|
750
814
|
|
751
815
|
# Returns the size of the cached value. This could be less than
|
752
|
-
# <tt>value.
|
753
|
-
def
|
816
|
+
# <tt>value.bytesize</tt> if the data is compressed.
|
817
|
+
def bytesize
|
754
818
|
case value
|
755
819
|
when NilClass
|
756
820
|
0
|