activesupport 4.2.11.1 → 5.2.4
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 +399 -440
- data/MIT-LICENSE +2 -2
- data/README.rdoc +4 -5
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +7 -5
- data/lib/active_support/benchmarkable.rb +6 -4
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +41 -35
- data/lib/active_support/cache/mem_cache_store.rb +97 -88
- data/lib/active_support/cache/memory_store.rb +27 -30
- data/lib/active_support/cache/null_store.rb +7 -8
- data/lib/active_support/cache/redis_cache_store.rb +461 -0
- data/lib/active_support/cache/strategy/local_cache.rb +67 -34
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
- data/lib/active_support/cache.rb +287 -196
- data/lib/active_support/callbacks.rb +640 -590
- data/lib/active_support/concern.rb +11 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
- data/lib/active_support/concurrency/share_lock.rb +227 -0
- data/lib/active_support/configurable.rb +8 -5
- data/lib/active_support/core_ext/array/access.rb +29 -1
- data/lib/active_support/core_ext/array/conversions.rb +22 -18
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +11 -18
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +9 -6
- data/lib/active_support/core_ext/benchmark.rb +3 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +41 -22
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
- data/lib/active_support/core_ext/class/subclasses.rb +20 -6
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +11 -9
- data/lib/active_support/core_ext/date/conversions.rb +25 -23
- data/lib/active_support/core_ext/date/zones.rb +4 -2
- data/lib/active_support/core_ext/date.rb +6 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +170 -58
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -3
- data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +36 -18
- data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
- data/lib/active_support/core_ext/date_time.rb +7 -5
- data/lib/active_support/core_ext/digest/uuid.rb +7 -5
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +101 -33
- data/lib/active_support/core_ext/file/atomic.rb +38 -31
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/compact.rb +14 -9
- data/lib/active_support/core_ext/hash/conversions.rb +62 -41
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/except.rb +11 -8
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
- data/lib/active_support/core_ext/hash/keys.rb +33 -27
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -8
- data/lib/active_support/core_ext/hash/transform_values.rb +14 -5
- data/lib/active_support/core_ext/hash.rb +11 -9
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +2 -0
- data/lib/active_support/core_ext/integer/time.rb +11 -18
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
- data/lib/active_support/core_ext/kernel/concern.rb +5 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +6 -5
- data/lib/active_support/core_ext/load_error.rb +3 -22
- data/lib/active_support/core_ext/marshal.rb +8 -8
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
- data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
- data/lib/active_support/core_ext/module/concerning.rb +11 -12
- data/lib/active_support/core_ext/module/delegation.rb +99 -29
- data/lib/active_support/core_ext/module/deprecation.rb +4 -2
- data/lib/active_support/core_ext/module/introspection.rb +9 -9
- data/lib/active_support/core_ext/module/reachable.rb +5 -2
- data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +14 -11
- data/lib/active_support/core_ext/name_error.rb +22 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +78 -81
- data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
- data/lib/active_support/core_ext/numeric/time.rb +35 -23
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +27 -2
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
- data/lib/active_support/core_ext/object/duplicable.rb +41 -14
- data/lib/active_support/core_ext/object/inclusion.rb +5 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +49 -19
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +10 -5
- data/lib/active_support/core_ext/object/try.rb +69 -21
- data/lib/active_support/core_ext/object/with_options.rb +16 -3
- data/lib/active_support/core_ext/object.rb +14 -13
- data/lib/active_support/core_ext/range/compare_range.rb +61 -0
- data/lib/active_support/core_ext/range/conversions.rb +27 -7
- data/lib/active_support/core_ext/range/each.rb +19 -17
- data/lib/active_support/core_ext/range/include_range.rb +2 -22
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +6 -0
- data/lib/active_support/core_ext/securerandom.rb +25 -0
- data/lib/active_support/core_ext/string/access.rb +8 -6
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +7 -4
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +6 -5
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +61 -24
- data/lib/active_support/core_ext/string/inquiry.rb +3 -1
- data/lib/active_support/core_ext/string/multibyte.rb +15 -7
- data/lib/active_support/core_ext/string/output_safety.rb +34 -38
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
- data/lib/active_support/core_ext/string/strip.rb +4 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -2
- data/lib/active_support/core_ext/string.rb +15 -13
- data/lib/active_support/core_ext/time/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +85 -51
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +20 -13
- data/lib/active_support/core_ext/time/zones.rb +41 -7
- data/lib/active_support/core_ext/time.rb +7 -6
- data/lib/active_support/core_ext/uri.rb +6 -8
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/current_attributes.rb +195 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/dependencies.rb +152 -161
- data/lib/active_support/deprecation/behaviors.rb +44 -11
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/instance_delegator.rb +17 -2
- data/lib/active_support/deprecation/method_wrappers.rb +66 -20
- data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
- data/lib/active_support/deprecation/reporting.rb +32 -12
- data/lib/active_support/deprecation.rb +12 -9
- data/lib/active_support/descendants_tracker.rb +2 -0
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration/iso8601_parser.rb +125 -0
- data/lib/active_support/duration/iso8601_serializer.rb +55 -0
- data/lib/active_support/duration.rb +307 -35
- data/lib/active_support/encrypted_configuration.rb +49 -0
- data/lib/active_support/encrypted_file.rb +99 -0
- data/lib/active_support/evented_file_update_checker.rb +205 -0
- data/lib/active_support/execution_wrapper.rb +128 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +63 -37
- data/lib/active_support/gem_version.rb +6 -4
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +123 -28
- data/lib/active_support/i18n.rb +8 -6
- data/lib/active_support/i18n_railtie.rb +37 -13
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector/inflections.rb +61 -12
- data/lib/active_support/inflector/methods.rb +163 -136
- data/lib/active_support/inflector/transliterate.rb +48 -27
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/json/decoding.rb +16 -13
- data/lib/active_support/json/encoding.rb +11 -58
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +25 -25
- data/lib/active_support/lazy_load_hooks.rb +50 -20
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +13 -10
- data/lib/active_support/logger.rb +8 -7
- data/lib/active_support/logger_silence.rb +6 -4
- data/lib/active_support/logger_thread_safe_level.rb +7 -5
- data/lib/active_support/message_encryptor.rb +168 -53
- data/lib/active_support/message_verifier.rb +150 -17
- data/lib/active_support/messages/metadata.rb +71 -0
- data/lib/active_support/messages/rotation_configuration.rb +22 -0
- data/lib/active_support/messages/rotator.rb +56 -0
- data/lib/active_support/multibyte/chars.rb +36 -23
- data/lib/active_support/multibyte/unicode.rb +100 -96
- data/lib/active_support/multibyte.rb +4 -2
- data/lib/active_support/notifications/fanout.rb +11 -9
- data/lib/active_support/notifications/instrumenter.rb +27 -7
- data/lib/active_support/notifications.rb +11 -7
- data/lib/active_support/number_helper/number_converter.rb +13 -11
- data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
- data/lib/active_support/number_helper/rounding_helper.rb +66 -0
- data/lib/active_support/number_helper.rb +94 -68
- data/lib/active_support/option_merger.rb +3 -1
- data/lib/active_support/ordered_hash.rb +6 -4
- data/lib/active_support/ordered_options.rb +23 -5
- data/lib/active_support/per_thread_registry.rb +9 -4
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +16 -8
- data/lib/active_support/railtie.rb +43 -9
- data/lib/active_support/reloader.rb +131 -0
- data/lib/active_support/rescuable.rb +108 -53
- data/lib/active_support/security_utils.rb +15 -11
- data/lib/active_support/string_inquirer.rb +11 -3
- data/lib/active_support/subscriber.rb +21 -16
- data/lib/active_support/tagged_logging.rb +14 -11
- data/lib/active_support/test_case.rb +19 -47
- data/lib/active_support/testing/assertions.rb +137 -20
- data/lib/active_support/testing/autorun.rb +4 -2
- data/lib/active_support/testing/constant_lookup.rb +2 -1
- data/lib/active_support/testing/declarative.rb +3 -1
- data/lib/active_support/testing/deprecation.rb +14 -10
- data/lib/active_support/testing/file_fixtures.rb +36 -0
- data/lib/active_support/testing/isolation.rb +34 -25
- data/lib/active_support/testing/method_call_assertions.rb +43 -0
- data/lib/active_support/testing/setup_and_teardown.rb +13 -8
- data/lib/active_support/testing/stream.rb +44 -0
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +81 -15
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +169 -39
- data/lib/active_support/values/time_zone.rb +196 -61
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini/jdom.rb +116 -114
- data/lib/active_support/xml_mini/libxml.rb +16 -13
- data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
- data/lib/active_support/xml_mini/nokogiri.rb +14 -12
- data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
- data/lib/active_support/xml_mini/rexml.rb +11 -9
- data/lib/active_support/xml_mini.rb +37 -37
- data/lib/active_support.rb +12 -11
- metadata +54 -24
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -86
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2005-
|
1
|
+
Copyright (c) 2005-2018 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
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -10,18 +10,18 @@ outside of Rails.
|
|
10
10
|
|
11
11
|
The latest version of Active Support can be installed with RubyGems:
|
12
12
|
|
13
|
-
|
13
|
+
$ gem install activesupport
|
14
14
|
|
15
15
|
Source code can be downloaded as part of the Rails project on GitHub:
|
16
16
|
|
17
|
-
* https://github.com/rails/rails/tree/
|
17
|
+
* https://github.com/rails/rails/tree/5-2-stable/activesupport
|
18
18
|
|
19
19
|
|
20
20
|
== License
|
21
21
|
|
22
22
|
Active Support is released under the MIT license:
|
23
23
|
|
24
|
-
*
|
24
|
+
* https://opensource.org/licenses/MIT
|
25
25
|
|
26
26
|
|
27
27
|
== Support
|
@@ -30,11 +30,10 @@ API documentation is at:
|
|
30
30
|
|
31
31
|
* http://api.rubyonrails.org
|
32
32
|
|
33
|
-
Bug reports
|
33
|
+
Bug reports for the Ruby on Rails project can be filed here:
|
34
34
|
|
35
35
|
* https://github.com/rails/rails/issues
|
36
36
|
|
37
37
|
Feature requests should be discussed on the rails-core mailing list here:
|
38
38
|
|
39
39
|
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
40
|
-
|
data/lib/active_support/all.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
|
5
|
+
# its string-like contents:
|
6
|
+
#
|
7
|
+
# variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
|
8
|
+
#
|
9
|
+
# variants.phone? # => true
|
10
|
+
# variants.tablet? # => true
|
11
|
+
# variants.desktop? # => false
|
12
|
+
class ArrayInquirer < Array
|
13
|
+
# Passes each element of +candidates+ collection to ArrayInquirer collection.
|
14
|
+
# The method returns true if any element from the ArrayInquirer collection
|
15
|
+
# is equal to the stringified or symbolized form of any element in the +candidates+ collection.
|
16
|
+
#
|
17
|
+
# If +candidates+ collection is not given, method returns true.
|
18
|
+
#
|
19
|
+
# variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
|
20
|
+
#
|
21
|
+
# variants.any? # => true
|
22
|
+
# variants.any?(:phone, :tablet) # => true
|
23
|
+
# variants.any?('phone', 'desktop') # => true
|
24
|
+
# variants.any?(:desktop, :watch) # => false
|
25
|
+
def any?(*candidates)
|
26
|
+
if candidates.none?
|
27
|
+
super
|
28
|
+
else
|
29
|
+
candidates.any? do |candidate|
|
30
|
+
include?(candidate.to_sym) || include?(candidate.to_s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def respond_to_missing?(name, include_private = false)
|
37
|
+
(name[-1] == "?") || super
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(name, *args)
|
41
|
+
if name[-1] == "?"
|
42
|
+
any?(name[0..-2])
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveSupport
|
2
4
|
# Backtraces often include many lines that are not relevant for the context
|
3
5
|
# under review. This makes it hard to find the signal amongst the backtrace
|
@@ -12,9 +14,9 @@ module ActiveSupport
|
|
12
14
|
# is to exclude the output of a noisy library from the backtrace, so that you
|
13
15
|
# can focus on the rest.
|
14
16
|
#
|
15
|
-
# bc = BacktraceCleaner.new
|
17
|
+
# bc = ActiveSupport::BacktraceCleaner.new
|
16
18
|
# bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
|
17
|
-
# bc.add_silencer { |line| line =~ /
|
19
|
+
# bc.add_silencer { |line| line =~ /puma|rubygems/ } # skip any lines from puma or rubygems
|
18
20
|
# bc.clean(exception.backtrace) # perform the cleanup
|
19
21
|
#
|
20
22
|
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
|
@@ -25,7 +27,7 @@ module ActiveSupport
|
|
25
27
|
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
|
26
28
|
# These two methods will give you a completely untouched backtrace.
|
27
29
|
#
|
28
|
-
# Inspired by the Quiet Backtrace gem by
|
30
|
+
# Inspired by the Quiet Backtrace gem by thoughtbot.
|
29
31
|
class BacktraceCleaner
|
30
32
|
def initialize
|
31
33
|
@filters, @silencers = [], []
|
@@ -59,8 +61,8 @@ module ActiveSupport
|
|
59
61
|
# Adds a silencer from the block provided. If the silencer returns +true+
|
60
62
|
# for a given line, it will be excluded from the clean backtrace.
|
61
63
|
#
|
62
|
-
# # Will reject all lines that include the word "
|
63
|
-
# backtrace_cleaner.add_silencer { |line| line =~ /
|
64
|
+
# # 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| line =~ /puma/ }
|
64
66
|
def add_silencer(&block)
|
65
67
|
@silencers << block
|
66
68
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/benchmark"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
3
5
|
|
4
6
|
module ActiveSupport
|
5
7
|
module Benchmarkable
|
@@ -38,8 +40,8 @@ module ActiveSupport
|
|
38
40
|
options[:level] ||= :info
|
39
41
|
|
40
42
|
result = nil
|
41
|
-
ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
|
42
|
-
logger.send(options[:level],
|
43
|
+
ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
|
44
|
+
logger.send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
43
45
|
result
|
44
46
|
else
|
45
47
|
yield
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/marshal"
|
4
|
+
require "active_support/core_ext/file/atomic"
|
5
|
+
require "active_support/core_ext/string/conversions"
|
6
|
+
require "uri/common"
|
5
7
|
|
6
8
|
module ActiveSupport
|
7
9
|
module Cache
|
@@ -10,34 +12,35 @@ module ActiveSupport
|
|
10
12
|
# FileStore implements the Strategy::LocalCache strategy which implements
|
11
13
|
# an in-memory cache inside of a block.
|
12
14
|
class FileStore < Store
|
15
|
+
prepend Strategy::LocalCache
|
13
16
|
attr_reader :cache_path
|
14
17
|
|
15
18
|
DIR_FORMATTER = "%03X"
|
16
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)
|
17
20
|
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
|
18
|
-
EXCLUDED_DIRS = [
|
21
|
+
EXCLUDED_DIRS = [".", ".."].freeze
|
22
|
+
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
|
19
23
|
|
20
24
|
def initialize(cache_path, options = nil)
|
21
25
|
super(options)
|
22
26
|
@cache_path = cache_path.to_s
|
23
|
-
extend Strategy::LocalCache
|
24
27
|
end
|
25
28
|
|
26
29
|
# Deletes all items from the cache. In this case it deletes all the entries in the specified
|
27
|
-
# file store directory except for .gitkeep. Be careful which directory is specified in your
|
30
|
+
# file store directory except for .keep or .gitkeep. Be careful which directory is specified in your
|
28
31
|
# config file when using +FileStore+ because everything in that directory will be deleted.
|
29
32
|
def clear(options = nil)
|
30
|
-
root_dirs =
|
31
|
-
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
|
33
|
+
root_dirs = exclude_from(cache_path, EXCLUDED_DIRS + GITKEEP_FILES)
|
34
|
+
FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
|
35
|
+
rescue Errno::ENOENT
|
32
36
|
end
|
33
37
|
|
34
38
|
# Preemptively iterates through all stored keys and removes the ones which have expired.
|
35
39
|
def cleanup(options = nil)
|
36
40
|
options = merged_options(options)
|
37
41
|
search_dir(cache_path) do |fname|
|
38
|
-
|
39
|
-
|
40
|
-
delete_entry(key, options) if entry && entry.expired?
|
42
|
+
entry = read_entry(fname, options)
|
43
|
+
delete_entry(fname, options) if entry && entry.expired?
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
@@ -59,17 +62,16 @@ module ActiveSupport
|
|
59
62
|
matcher = key_matcher(matcher, options)
|
60
63
|
search_dir(cache_path) do |path|
|
61
64
|
key = file_path_key(path)
|
62
|
-
delete_entry(
|
65
|
+
delete_entry(path, options) if key.match(matcher)
|
63
66
|
end
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
67
|
-
|
70
|
+
private
|
68
71
|
|
69
72
|
def read_entry(key, options)
|
70
|
-
|
71
|
-
|
72
|
-
File.open(file_name) { |f| Marshal.load(f) }
|
73
|
+
if File.exist?(key)
|
74
|
+
File.open(key) { |f| Marshal.load(f) }
|
73
75
|
end
|
74
76
|
rescue => e
|
75
77
|
logger.error("FileStoreError (#{e}): #{e.message}") if logger
|
@@ -77,33 +79,30 @@ module ActiveSupport
|
|
77
79
|
end
|
78
80
|
|
79
81
|
def write_entry(key, entry, options)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
|
82
|
+
return false if options[:unless_exist] && File.exist?(key)
|
83
|
+
ensure_cache_path(File.dirname(key))
|
84
|
+
File.atomic_write(key, cache_path) { |f| Marshal.dump(entry, f) }
|
84
85
|
true
|
85
86
|
end
|
86
87
|
|
87
88
|
def delete_entry(key, options)
|
88
|
-
|
89
|
-
if File.exist?(file_name)
|
89
|
+
if File.exist?(key)
|
90
90
|
begin
|
91
|
-
File.delete(
|
92
|
-
delete_empty_directories(File.dirname(
|
91
|
+
File.delete(key)
|
92
|
+
delete_empty_directories(File.dirname(key))
|
93
93
|
true
|
94
94
|
rescue => e
|
95
95
|
# Just in case the error was caused by another process deleting the file first.
|
96
|
-
raise e if File.exist?(
|
96
|
+
raise e if File.exist?(key)
|
97
97
|
false
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
private
|
103
102
|
# Lock a file for a block so only one process can modify it at a time.
|
104
|
-
def lock_file(file_name, &block)
|
103
|
+
def lock_file(file_name, &block)
|
105
104
|
if File.exist?(file_name)
|
106
|
-
File.open(file_name,
|
105
|
+
File.open(file_name, "r+") do |f|
|
107
106
|
begin
|
108
107
|
f.flock File::LOCK_EX
|
109
108
|
yield
|
@@ -117,12 +116,14 @@ module ActiveSupport
|
|
117
116
|
end
|
118
117
|
|
119
118
|
# Translate a key into a file path.
|
120
|
-
def
|
121
|
-
|
122
|
-
|
119
|
+
def normalize_key(key, options)
|
120
|
+
key = super
|
121
|
+
fname = URI.encode_www_form_component(key)
|
122
|
+
|
123
|
+
if fname.size > FILEPATH_MAX_SIZE
|
124
|
+
fname = ActiveSupport::Digest.hexdigest(key)
|
123
125
|
end
|
124
126
|
|
125
|
-
fname = URI.encode_www_form_component(key)
|
126
127
|
hash = Zlib.adler32(fname)
|
127
128
|
hash, dir_1 = hash.divmod(0x1000)
|
128
129
|
dir_2 = hash.modulo(0x1000)
|
@@ -146,7 +147,7 @@ module ActiveSupport
|
|
146
147
|
# Delete empty directories in the cache.
|
147
148
|
def delete_empty_directories(dir)
|
148
149
|
return if File.realpath(dir) == File.realpath(cache_path)
|
149
|
-
if
|
150
|
+
if exclude_from(dir, EXCLUDED_DIRS).empty?
|
150
151
|
Dir.delete(dir) rescue nil
|
151
152
|
delete_empty_directories(File.dirname(dir))
|
152
153
|
end
|
@@ -173,7 +174,7 @@ module ActiveSupport
|
|
173
174
|
# Modifies the amount of an already existing integer value that is stored in the cache.
|
174
175
|
# If the key is not found nothing is done.
|
175
176
|
def modify_value(name, amount, options)
|
176
|
-
file_name =
|
177
|
+
file_name = normalize_key(name, options)
|
177
178
|
|
178
179
|
lock_file(file_name) do
|
179
180
|
options = merged_options(options)
|
@@ -185,6 +186,11 @@ module ActiveSupport
|
|
185
186
|
end
|
186
187
|
end
|
187
188
|
end
|
189
|
+
|
190
|
+
# Exclude entries from source directory
|
191
|
+
def exclude_from(source, excludes)
|
192
|
+
Dir.entries(source).reject { |f| excludes.include?(f) }
|
193
|
+
end
|
188
194
|
end
|
189
195
|
end
|
190
196
|
end
|
@@ -1,18 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
|
-
require
|
4
|
+
require "dalli"
|
3
5
|
rescue LoadError => e
|
4
6
|
$stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
|
5
7
|
raise e
|
6
8
|
end
|
7
9
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require 'active_support/core_ext/array/extract_options'
|
10
|
+
require "active_support/core_ext/marshal"
|
11
|
+
require "active_support/core_ext/array/extract_options"
|
11
12
|
|
12
13
|
module ActiveSupport
|
13
14
|
module Cache
|
14
15
|
# A cache store implementation which stores data in Memcached:
|
15
|
-
#
|
16
|
+
# https://memcached.org
|
16
17
|
#
|
17
18
|
# This is currently the most popular cache store for production websites.
|
18
19
|
#
|
@@ -24,13 +25,52 @@ module ActiveSupport
|
|
24
25
|
# MemCacheStore implements the Strategy::LocalCache strategy which implements
|
25
26
|
# an in-memory cache inside of a block.
|
26
27
|
class MemCacheStore < Store
|
28
|
+
# Provide support for raw values in the local cache strategy.
|
29
|
+
module LocalCacheWithRaw # :nodoc:
|
30
|
+
private
|
31
|
+
def read_entry(key, options)
|
32
|
+
entry = super
|
33
|
+
if options[:raw] && local_cache && entry
|
34
|
+
entry = deserialize_entry(entry.value)
|
35
|
+
end
|
36
|
+
entry
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_entry(key, entry, options)
|
40
|
+
if options[:raw] && local_cache
|
41
|
+
raw_entry = Entry.new(entry.value.to_s)
|
42
|
+
raw_entry.expires_at = entry.expires_at
|
43
|
+
super(key, raw_entry, options)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
prepend Strategy::LocalCache
|
51
|
+
prepend LocalCacheWithRaw
|
52
|
+
|
27
53
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
28
54
|
|
29
|
-
|
55
|
+
# Creates a new Dalli::Client instance with specified addresses and options.
|
56
|
+
# By default address is equal localhost:11211.
|
57
|
+
#
|
58
|
+
# ActiveSupport::Cache::MemCacheStore.build_mem_cache
|
59
|
+
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
|
60
|
+
# ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
|
61
|
+
# # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
|
62
|
+
def self.build_mem_cache(*addresses) # :nodoc:
|
30
63
|
addresses = addresses.flatten
|
31
64
|
options = addresses.extract_options!
|
32
65
|
addresses = ["localhost:11211"] if addresses.empty?
|
33
|
-
|
66
|
+
pool_options = retrieve_pool_options(options)
|
67
|
+
|
68
|
+
if pool_options.empty?
|
69
|
+
Dalli::Client.new(addresses, options)
|
70
|
+
else
|
71
|
+
ensure_connection_pool_added!
|
72
|
+
ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
|
73
|
+
end
|
34
74
|
end
|
35
75
|
|
36
76
|
# Creates a new MemCacheStore object, with the given memcached server
|
@@ -53,82 +93,56 @@ module ActiveSupport
|
|
53
93
|
@data = addresses.first
|
54
94
|
else
|
55
95
|
mem_cache_options = options.dup
|
56
|
-
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
96
|
+
UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
|
57
97
|
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
58
98
|
end
|
59
|
-
|
60
|
-
extend Strategy::LocalCache
|
61
|
-
extend LocalCacheWithRaw
|
62
|
-
end
|
63
|
-
|
64
|
-
# Reads multiple values from the cache using a single call to the
|
65
|
-
# servers for all keys. Options can be passed in the last argument.
|
66
|
-
def read_multi(*names)
|
67
|
-
options = names.extract_options!
|
68
|
-
options = merged_options(options)
|
69
|
-
keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
|
70
|
-
raw_values = @data.get_multi(keys_to_names.keys)
|
71
|
-
values = {}
|
72
|
-
raw_values.each do |key, value|
|
73
|
-
entry = deserialize_entry(value)
|
74
|
-
values[keys_to_names[key]] = entry.value unless entry.expired?
|
75
|
-
end
|
76
|
-
values
|
77
99
|
end
|
78
100
|
|
79
101
|
# Increment a cached value. This method uses the memcached incr atomic
|
80
102
|
# operator and can only be used on values written with the :raw option.
|
81
103
|
# Calling it on a value not stored with :raw will initialize that value
|
82
104
|
# to zero.
|
83
|
-
def increment(name, amount = 1, options = nil)
|
105
|
+
def increment(name, amount = 1, options = nil)
|
84
106
|
options = merged_options(options)
|
85
|
-
instrument(:increment, name, :
|
86
|
-
|
107
|
+
instrument(:increment, name, amount: amount) do
|
108
|
+
rescue_error_with nil do
|
109
|
+
@data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
|
110
|
+
end
|
87
111
|
end
|
88
|
-
rescue Dalli::DalliError => e
|
89
|
-
logger.error("DalliError (#{e}): #{e.message}") if logger
|
90
|
-
nil
|
91
112
|
end
|
92
113
|
|
93
114
|
# Decrement a cached value. This method uses the memcached decr atomic
|
94
115
|
# operator and can only be used on values written with the :raw option.
|
95
116
|
# Calling it on a value not stored with :raw will initialize that value
|
96
117
|
# to zero.
|
97
|
-
def decrement(name, amount = 1, options = nil)
|
118
|
+
def decrement(name, amount = 1, options = nil)
|
98
119
|
options = merged_options(options)
|
99
|
-
instrument(:decrement, name, :
|
100
|
-
|
120
|
+
instrument(:decrement, name, amount: amount) do
|
121
|
+
rescue_error_with nil do
|
122
|
+
@data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
|
123
|
+
end
|
101
124
|
end
|
102
|
-
rescue Dalli::DalliError => e
|
103
|
-
logger.error("DalliError (#{e}): #{e.message}") if logger
|
104
|
-
nil
|
105
125
|
end
|
106
126
|
|
107
127
|
# Clear the entire cache on all memcached servers. This method should
|
108
128
|
# be used with care when shared cache is being used.
|
109
129
|
def clear(options = nil)
|
110
|
-
@data.flush_all
|
111
|
-
rescue Dalli::DalliError => e
|
112
|
-
logger.error("DalliError (#{e}): #{e.message}") if logger
|
113
|
-
nil
|
130
|
+
rescue_error_with(nil) { @data.with { |c| c.flush_all } }
|
114
131
|
end
|
115
132
|
|
116
133
|
# Get the statistics from the memcached servers.
|
117
134
|
def stats
|
118
|
-
@data.stats
|
135
|
+
@data.with { |c| c.stats }
|
119
136
|
end
|
120
137
|
|
121
|
-
|
138
|
+
private
|
122
139
|
# Read an entry from the cache.
|
123
|
-
def read_entry(key, options)
|
124
|
-
deserialize_entry(@data.get(
|
125
|
-
rescue Dalli::DalliError => e
|
126
|
-
logger.error("DalliError (#{e}): #{e.message}") if logger
|
127
|
-
nil
|
140
|
+
def read_entry(key, options)
|
141
|
+
rescue_error_with(nil) { deserialize_entry(@data.with { |c| c.get(key, options) }) }
|
128
142
|
end
|
129
143
|
|
130
144
|
# Write an entry to the cache.
|
131
|
-
def write_entry(key, entry, options)
|
145
|
+
def write_entry(key, entry, options)
|
132
146
|
method = options && options[:unless_exist] ? :add : :set
|
133
147
|
value = options[:raw] ? entry.value.to_s : entry
|
134
148
|
expires_in = options[:expires_in].to_i
|
@@ -136,30 +150,42 @@ module ActiveSupport
|
|
136
150
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
137
151
|
expires_in += 5.minutes
|
138
152
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
false
|
153
|
+
rescue_error_with false do
|
154
|
+
@data.with { |c| c.send(method, key, value, expires_in, options) }
|
155
|
+
end
|
143
156
|
end
|
144
157
|
|
145
|
-
#
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
158
|
+
# Reads multiple entries from the cache implementation.
|
159
|
+
def read_multi_entries(names, options)
|
160
|
+
keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
|
161
|
+
|
162
|
+
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
163
|
+
values = {}
|
164
|
+
|
165
|
+
raw_values.each do |key, value|
|
166
|
+
entry = deserialize_entry(value)
|
167
|
+
|
168
|
+
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
169
|
+
values[keys_to_names[key]] = entry.value
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
values
|
151
174
|
end
|
152
175
|
|
153
|
-
|
176
|
+
# Delete an entry from the cache.
|
177
|
+
def delete_entry(key, options)
|
178
|
+
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
179
|
+
end
|
154
180
|
|
155
181
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
156
182
|
# before applying the regular expression to ensure we are escaping all
|
157
183
|
# characters properly.
|
158
|
-
def
|
159
|
-
key =
|
184
|
+
def normalize_key(key, options)
|
185
|
+
key = super.dup
|
160
186
|
key = key.force_encoding(Encoding::ASCII_8BIT)
|
161
|
-
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
162
|
-
key = "#{key[0, 213]}:md5:#{Digest
|
187
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
188
|
+
key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
163
189
|
key
|
164
190
|
end
|
165
191
|
|
@@ -167,32 +193,15 @@ module ActiveSupport
|
|
167
193
|
if raw_value
|
168
194
|
entry = Marshal.load(raw_value) rescue raw_value
|
169
195
|
entry.is_a?(Entry) ? entry : Entry.new(entry)
|
170
|
-
else
|
171
|
-
nil
|
172
196
|
end
|
173
197
|
end
|
174
198
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
entry = deserialize_entry(entry.value)
|
182
|
-
end
|
183
|
-
entry
|
184
|
-
end
|
185
|
-
|
186
|
-
def write_entry(key, entry, options) # :nodoc:
|
187
|
-
retval = super
|
188
|
-
if options[:raw] && local_cache && retval
|
189
|
-
raw_entry = Entry.new(entry.value.to_s)
|
190
|
-
raw_entry.expires_at = entry.expires_at
|
191
|
-
local_cache.write_entry(key, raw_entry, options)
|
192
|
-
end
|
193
|
-
retval
|
194
|
-
end
|
195
|
-
end
|
199
|
+
def rescue_error_with(fallback)
|
200
|
+
yield
|
201
|
+
rescue Dalli::DalliError => e
|
202
|
+
logger.error("DalliError (#{e}): #{e.message}") if logger
|
203
|
+
fallback
|
204
|
+
end
|
196
205
|
end
|
197
206
|
end
|
198
207
|
end
|