activesupport 5.0.7.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 +7 -0
- data/CHANGELOG.md +1013 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +39 -0
- data/lib/active_support.rb +99 -0
- data/lib/active_support/all.rb +3 -0
- data/lib/active_support/array_inquirer.rb +44 -0
- data/lib/active_support/backtrace_cleaner.rb +103 -0
- data/lib/active_support/benchmarkable.rb +49 -0
- data/lib/active_support/builder.rb +6 -0
- data/lib/active_support/cache.rb +701 -0
- data/lib/active_support/cache/file_store.rb +204 -0
- data/lib/active_support/cache/mem_cache_store.rb +207 -0
- data/lib/active_support/cache/memory_store.rb +167 -0
- data/lib/active_support/cache/null_store.rb +41 -0
- data/lib/active_support/cache/strategy/local_cache.rb +172 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/callbacks.rb +791 -0
- data/lib/active_support/concern.rb +142 -0
- data/lib/active_support/concurrency/latch.rb +26 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +148 -0
- data/lib/active_support/core_ext.rb +4 -0
- data/lib/active_support/core_ext/array.rb +7 -0
- data/lib/active_support/core_ext/array/access.rb +90 -0
- data/lib/active_support/core_ext/array/conversions.rb +211 -0
- data/lib/active_support/core_ext/array/extract_options.rb +29 -0
- data/lib/active_support/core_ext/array/grouping.rb +107 -0
- data/lib/active_support/core_ext/array/inquiry.rb +17 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/active_support/core_ext/array/wrap.rb +46 -0
- data/lib/active_support/core_ext/benchmark.rb +14 -0
- data/lib/active_support/core_ext/big_decimal.rb +1 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/class.rb +2 -0
- data/lib/active_support/core_ext/class/attribute.rb +128 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -0
- data/lib/active_support/core_ext/class/subclasses.rb +41 -0
- data/lib/active_support/core_ext/date.rb +5 -0
- data/lib/active_support/core_ext/date/acts_like.rb +8 -0
- data/lib/active_support/core_ext/date/blank.rb +12 -0
- data/lib/active_support/core_ext/date/calculations.rb +143 -0
- data/lib/active_support/core_ext/date/conversions.rb +95 -0
- data/lib/active_support/core_ext/date/zones.rb +6 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time.rb +5 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +14 -0
- data/lib/active_support/core_ext/date_time/blank.rb +12 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +199 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +105 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +146 -0
- data/lib/active_support/core_ext/file.rb +1 -0
- data/lib/active_support/core_ext/file/atomic.rb +68 -0
- data/lib/active_support/core_ext/hash.rb +9 -0
- data/lib/active_support/core_ext/hash/compact.rb +24 -0
- data/lib/active_support/core_ext/hash/conversions.rb +262 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +38 -0
- data/lib/active_support/core_ext/hash/except.rb +22 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +23 -0
- data/lib/active_support/core_ext/hash/keys.rb +170 -0
- data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -0
- data/lib/active_support/core_ext/hash/slice.rb +48 -0
- data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
- data/lib/active_support/core_ext/integer.rb +3 -0
- data/lib/active_support/core_ext/integer/inflections.rb +29 -0
- data/lib/active_support/core_ext/integer/multiple.rb +10 -0
- data/lib/active_support/core_ext/integer/time.rb +29 -0
- data/lib/active_support/core_ext/kernel.rb +4 -0
- data/lib/active_support/core_ext/kernel/agnostics.rb +11 -0
- data/lib/active_support/core_ext/kernel/concern.rb +12 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +3 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +43 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +6 -0
- data/lib/active_support/core_ext/load_error.rb +31 -0
- data/lib/active_support/core_ext/marshal.rb +22 -0
- data/lib/active_support/core_ext/module.rb +12 -0
- data/lib/active_support/core_ext/module/aliasing.rb +74 -0
- data/lib/active_support/core_ext/module/anonymous.rb +28 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +36 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +216 -0
- data/lib/active_support/core_ext/module/deprecation.rb +23 -0
- data/lib/active_support/core_ext/module/introspection.rb +68 -0
- data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
- data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
- data/lib/active_support/core_ext/module/reachable.rb +8 -0
- data/lib/active_support/core_ext/module/remove_method.rb +35 -0
- data/lib/active_support/core_ext/name_error.rb +31 -0
- data/lib/active_support/core_ext/numeric.rb +4 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +64 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +144 -0
- data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
- data/lib/active_support/core_ext/numeric/time.rb +74 -0
- data/lib/active_support/core_ext/object.rb +14 -0
- data/lib/active_support/core_ext/object/acts_like.rb +10 -0
- data/lib/active_support/core_ext/object/blank.rb +143 -0
- data/lib/active_support/core_ext/object/conversions.rb +4 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
- data/lib/active_support/core_ext/object/duplicable.rb +124 -0
- data/lib/active_support/core_ext/object/inclusion.rb +27 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +28 -0
- data/lib/active_support/core_ext/object/json.rb +205 -0
- data/lib/active_support/core_ext/object/to_param.rb +1 -0
- data/lib/active_support/core_ext/object/to_query.rb +84 -0
- data/lib/active_support/core_ext/object/try.rb +146 -0
- data/lib/active_support/core_ext/object/with_options.rb +69 -0
- data/lib/active_support/core_ext/range.rb +4 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -0
- data/lib/active_support/core_ext/range/each.rb +21 -0
- data/lib/active_support/core_ext/range/include_range.rb +23 -0
- data/lib/active_support/core_ext/range/overlaps.rb +8 -0
- data/lib/active_support/core_ext/regexp.rb +5 -0
- data/lib/active_support/core_ext/securerandom.rb +23 -0
- data/lib/active_support/core_ext/string.rb +13 -0
- data/lib/active_support/core_ext/string/access.rb +104 -0
- data/lib/active_support/core_ext/string/behavior.rb +6 -0
- data/lib/active_support/core_ext/string/conversions.rb +57 -0
- data/lib/active_support/core_ext/string/exclude.rb +11 -0
- data/lib/active_support/core_ext/string/filters.rb +102 -0
- data/lib/active_support/core_ext/string/indent.rb +43 -0
- data/lib/active_support/core_ext/string/inflections.rb +244 -0
- data/lib/active_support/core_ext/string/inquiry.rb +13 -0
- data/lib/active_support/core_ext/string/multibyte.rb +53 -0
- data/lib/active_support/core_ext/string/output_safety.rb +260 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -0
- data/lib/active_support/core_ext/string/strip.rb +23 -0
- data/lib/active_support/core_ext/string/zones.rb +14 -0
- data/lib/active_support/core_ext/struct.rb +3 -0
- data/lib/active_support/core_ext/time.rb +5 -0
- data/lib/active_support/core_ext/time/acts_like.rb +8 -0
- data/lib/active_support/core_ext/time/calculations.rb +290 -0
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time/conversions.rb +67 -0
- data/lib/active_support/core_ext/time/marshal.rb +3 -0
- data/lib/active_support/core_ext/time/zones.rb +111 -0
- data/lib/active_support/core_ext/uri.rb +24 -0
- data/lib/active_support/dependencies.rb +755 -0
- data/lib/active_support/dependencies/autoload.rb +77 -0
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/deprecation.rb +43 -0
- data/lib/active_support/deprecation/behaviors.rb +90 -0
- data/lib/active_support/deprecation/instance_delegator.rb +37 -0
- data/lib/active_support/deprecation/method_wrappers.rb +70 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +149 -0
- data/lib/active_support/deprecation/reporting.rb +112 -0
- data/lib/active_support/descendants_tracker.rb +60 -0
- data/lib/active_support/duration.rb +235 -0
- data/lib/active_support/duration/iso8601_parser.rb +122 -0
- data/lib/active_support/duration/iso8601_serializer.rb +51 -0
- data/lib/active_support/evented_file_update_checker.rb +199 -0
- data/lib/active_support/execution_wrapper.rb +126 -0
- data/lib/active_support/executor.rb +6 -0
- data/lib/active_support/file_update_checker.rb +157 -0
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/gzip.rb +36 -0
- data/lib/active_support/hash_with_indifferent_access.rb +329 -0
- data/lib/active_support/i18n.rb +13 -0
- data/lib/active_support/i18n_railtie.rb +115 -0
- data/lib/active_support/inflections.rb +70 -0
- data/lib/active_support/inflector.rb +7 -0
- data/lib/active_support/inflector/inflections.rb +242 -0
- data/lib/active_support/inflector/methods.rb +390 -0
- data/lib/active_support/inflector/transliterate.rb +112 -0
- data/lib/active_support/json.rb +2 -0
- data/lib/active_support/json/decoding.rb +74 -0
- data/lib/active_support/json/encoding.rb +127 -0
- data/lib/active_support/key_generator.rb +71 -0
- data/lib/active_support/lazy_load_hooks.rb +76 -0
- data/lib/active_support/locale/en.yml +135 -0
- data/lib/active_support/log_subscriber.rb +109 -0
- data/lib/active_support/log_subscriber/test_helper.rb +104 -0
- data/lib/active_support/logger.rb +106 -0
- data/lib/active_support/logger_silence.rb +28 -0
- data/lib/active_support/logger_thread_safe_level.rb +31 -0
- data/lib/active_support/message_encryptor.rb +114 -0
- data/lib/active_support/message_verifier.rb +134 -0
- data/lib/active_support/multibyte.rb +21 -0
- data/lib/active_support/multibyte/chars.rb +231 -0
- data/lib/active_support/multibyte/unicode.rb +413 -0
- data/lib/active_support/notifications.rb +212 -0
- data/lib/active_support/notifications/fanout.rb +157 -0
- data/lib/active_support/notifications/instrumenter.rb +91 -0
- data/lib/active_support/number_helper.rb +368 -0
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
- data/lib/active_support/option_merger.rb +25 -0
- data/lib/active_support/ordered_hash.rb +48 -0
- data/lib/active_support/ordered_options.rb +81 -0
- data/lib/active_support/per_thread_registry.rb +58 -0
- data/lib/active_support/proxy_object.rb +13 -0
- data/lib/active_support/rails.rb +27 -0
- data/lib/active_support/railtie.rb +51 -0
- data/lib/active_support/reloader.rb +129 -0
- data/lib/active_support/rescuable.rb +173 -0
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/string_inquirer.rb +26 -0
- data/lib/active_support/subscriber.rb +120 -0
- data/lib/active_support/tagged_logging.rb +77 -0
- data/lib/active_support/test_case.rb +88 -0
- data/lib/active_support/testing/assertions.rb +99 -0
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/constant_lookup.rb +50 -0
- data/lib/active_support/testing/declarative.rb +26 -0
- data/lib/active_support/testing/deprecation.rb +36 -0
- data/lib/active_support/testing/file_fixtures.rb +34 -0
- data/lib/active_support/testing/isolation.rb +115 -0
- data/lib/active_support/testing/method_call_assertions.rb +41 -0
- data/lib/active_support/testing/setup_and_teardown.rb +50 -0
- data/lib/active_support/testing/stream.rb +42 -0
- data/lib/active_support/testing/tagged_logging.rb +25 -0
- data/lib/active_support/testing/time_helpers.rb +136 -0
- data/lib/active_support/time.rb +18 -0
- data/lib/active_support/time_with_zone.rb +511 -0
- data/lib/active_support/values/time_zone.rb +484 -0
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +8 -0
- data/lib/active_support/xml_mini.rb +209 -0
- data/lib/active_support/xml_mini/jdom.rb +181 -0
- data/lib/active_support/xml_mini/libxml.rb +77 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +82 -0
- data/lib/active_support/xml_mini/nokogiri.rb +81 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +85 -0
- data/lib/active_support/xml_mini/rexml.rb +128 -0
- metadata +350 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module Cache
|
3
|
+
# A cache store implementation which doesn't actually store anything. Useful in
|
4
|
+
# development and test environments where you don't want caching turned on but
|
5
|
+
# need to go through the caching interface.
|
6
|
+
#
|
7
|
+
# This cache does implement the local cache strategy, so values will actually
|
8
|
+
# be cached inside blocks that utilize this strategy. See
|
9
|
+
# ActiveSupport::Cache::Strategy::LocalCache for more details.
|
10
|
+
class NullStore < Store
|
11
|
+
prepend Strategy::LocalCache
|
12
|
+
|
13
|
+
def clear(options = nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cleanup(options = nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
def increment(name, amount = 1, options = nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
def decrement(name, amount = 1, options = nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_matched(matcher, options = nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def read_entry(key, options) # :nodoc:
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_entry(key, entry, options) # :nodoc:
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_entry(key, options) # :nodoc:
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'active_support/core_ext/object/duplicable'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'active_support/per_thread_registry'
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
module Cache
|
7
|
+
module Strategy
|
8
|
+
# Caches that implement LocalCache will be backed by an in-memory cache for the
|
9
|
+
# duration of a block. Repeated calls to the cache for the same key will hit the
|
10
|
+
# in-memory cache for faster access.
|
11
|
+
module LocalCache
|
12
|
+
autoload :Middleware, 'active_support/cache/strategy/local_cache_middleware'
|
13
|
+
|
14
|
+
# Class for storing and registering the local caches.
|
15
|
+
class LocalCacheRegistry # :nodoc:
|
16
|
+
extend ActiveSupport::PerThreadRegistry
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@registry = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_for(local_cache_key)
|
23
|
+
@registry[local_cache_key]
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_cache_for(local_cache_key, value)
|
27
|
+
@registry[local_cache_key] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.set_cache_for(l, v); instance.set_cache_for l, v; end
|
31
|
+
def self.cache_for(l); instance.cache_for l; end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Simple memory backed cache. This cache is not thread safe and is intended only
|
35
|
+
# for serving as a temporary memory cache for a single thread.
|
36
|
+
class LocalStore < Store
|
37
|
+
def initialize
|
38
|
+
super
|
39
|
+
@data = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Don't allow synchronizing since it isn't thread safe.
|
43
|
+
def synchronize # :nodoc:
|
44
|
+
yield
|
45
|
+
end
|
46
|
+
|
47
|
+
def clear(options = nil)
|
48
|
+
@data.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_entry(key, options)
|
52
|
+
@data[key]
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_entry(key, value, options)
|
56
|
+
@data[key] = value
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_entry(key, options)
|
61
|
+
!!@data.delete(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def fetch_entry(key, options = nil) # :nodoc:
|
65
|
+
@data.fetch(key) { @data[key] = yield }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Use a local cache for the duration of block.
|
70
|
+
def with_local_cache
|
71
|
+
use_temporary_local_cache(LocalStore.new) { yield }
|
72
|
+
end
|
73
|
+
# Middleware class can be inserted as a Rack handler to be local cache for the
|
74
|
+
# duration of request.
|
75
|
+
def middleware
|
76
|
+
@middleware ||= Middleware.new(
|
77
|
+
"ActiveSupport::Cache::Strategy::LocalCache",
|
78
|
+
local_cache_key)
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear(options = nil) # :nodoc:
|
82
|
+
return super unless cache = local_cache
|
83
|
+
cache.clear(options)
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def cleanup(options = nil) # :nodoc:
|
88
|
+
return super unless cache = local_cache
|
89
|
+
cache.clear(options)
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
93
|
+
def increment(name, amount = 1, options = nil) # :nodoc:
|
94
|
+
return super unless local_cache
|
95
|
+
value = bypass_local_cache{super}
|
96
|
+
write_cache_value(name, value, options)
|
97
|
+
value
|
98
|
+
end
|
99
|
+
|
100
|
+
def decrement(name, amount = 1, options = nil) # :nodoc:
|
101
|
+
return super unless local_cache
|
102
|
+
value = bypass_local_cache{super}
|
103
|
+
write_cache_value(name, value, options)
|
104
|
+
value
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
def read_entry(key, options) # :nodoc:
|
109
|
+
if cache = local_cache
|
110
|
+
cache.fetch_entry(key) { super }
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def write_entry(key, entry, options) # :nodoc:
|
117
|
+
local_cache.write_entry(key, entry, options) if local_cache
|
118
|
+
super
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete_entry(key, options) # :nodoc:
|
122
|
+
local_cache.delete_entry(key, options) if local_cache
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_cache_value(value, name, amount, options) # :nodoc:
|
127
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
128
|
+
`set_cache_value` is deprecated and will be removed from Rails 5.1.
|
129
|
+
Please use `write_cache_value` instead.
|
130
|
+
MESSAGE
|
131
|
+
write_cache_value name, value, options
|
132
|
+
end
|
133
|
+
|
134
|
+
def write_cache_value(name, value, options) # :nodoc:
|
135
|
+
name = normalize_key(name, options)
|
136
|
+
cache = local_cache
|
137
|
+
cache.mute do
|
138
|
+
if value
|
139
|
+
cache.write(name, value, options)
|
140
|
+
else
|
141
|
+
cache.delete(name, options)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def local_cache_key
|
149
|
+
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
|
150
|
+
end
|
151
|
+
|
152
|
+
def local_cache
|
153
|
+
LocalCacheRegistry.cache_for(local_cache_key)
|
154
|
+
end
|
155
|
+
|
156
|
+
def bypass_local_cache
|
157
|
+
use_temporary_local_cache(nil) { yield }
|
158
|
+
end
|
159
|
+
|
160
|
+
def use_temporary_local_cache(temporary_cache)
|
161
|
+
save_cache = LocalCacheRegistry.cache_for(local_cache_key)
|
162
|
+
begin
|
163
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
|
164
|
+
yield
|
165
|
+
ensure
|
166
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rack/body_proxy'
|
2
|
+
require 'rack/utils'
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
module Cache
|
6
|
+
module Strategy
|
7
|
+
module LocalCache
|
8
|
+
|
9
|
+
#--
|
10
|
+
# This class wraps up local storage for middlewares. Only the middleware method should
|
11
|
+
# construct them.
|
12
|
+
class Middleware # :nodoc:
|
13
|
+
attr_reader :name, :local_cache_key
|
14
|
+
|
15
|
+
def initialize(name, local_cache_key)
|
16
|
+
@name = name
|
17
|
+
@local_cache_key = local_cache_key
|
18
|
+
@app = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def new(app)
|
22
|
+
@app = app
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(env)
|
27
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
|
28
|
+
response = @app.call(env)
|
29
|
+
response[2] = ::Rack::BodyProxy.new(response[2]) do
|
30
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
|
31
|
+
end
|
32
|
+
cleanup_on_body_close = true
|
33
|
+
response
|
34
|
+
rescue Rack::Utils::InvalidParameterError
|
35
|
+
[400, {}, []]
|
36
|
+
ensure
|
37
|
+
LocalCacheRegistry.set_cache_for(local_cache_key, nil) unless
|
38
|
+
cleanup_on_body_close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,791 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/descendants_tracker'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
require 'active_support/core_ext/kernel/reporting'
|
6
|
+
require 'active_support/core_ext/kernel/singleton_class'
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
8
|
+
require 'active_support/core_ext/string/filters'
|
9
|
+
require 'active_support/deprecation'
|
10
|
+
require 'thread'
|
11
|
+
|
12
|
+
module ActiveSupport
|
13
|
+
# Callbacks are code hooks that are run at key points in an object's life cycle.
|
14
|
+
# The typical use case is to have a base class define a set of callbacks
|
15
|
+
# relevant to the other functionality it supplies, so that subclasses can
|
16
|
+
# install callbacks that enhance or modify the base functionality without
|
17
|
+
# needing to override or redefine methods of the base class.
|
18
|
+
#
|
19
|
+
# Mixing in this module allows you to define the events in the object's
|
20
|
+
# life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
|
21
|
+
# set the instance methods, procs, or callback objects to be called (via
|
22
|
+
# +ClassMethods.set_callback+), and run the installed callbacks at the
|
23
|
+
# appropriate times (via +run_callbacks+).
|
24
|
+
#
|
25
|
+
# Three kinds of callbacks are supported: before callbacks, run before a
|
26
|
+
# certain event; after callbacks, run after the event; and around callbacks,
|
27
|
+
# blocks that surround the event, triggering it when they yield. Callback code
|
28
|
+
# can be contained in instance methods, procs or lambdas, or callback objects
|
29
|
+
# that respond to certain predetermined methods. See +ClassMethods.set_callback+
|
30
|
+
# for details.
|
31
|
+
#
|
32
|
+
# class Record
|
33
|
+
# include ActiveSupport::Callbacks
|
34
|
+
# define_callbacks :save
|
35
|
+
#
|
36
|
+
# def save
|
37
|
+
# run_callbacks :save do
|
38
|
+
# puts "- save"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class PersonRecord < Record
|
44
|
+
# set_callback :save, :before, :saving_message
|
45
|
+
# def saving_message
|
46
|
+
# puts "saving..."
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# set_callback :save, :after do |object|
|
50
|
+
# puts "saved"
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# person = PersonRecord.new
|
55
|
+
# person.save
|
56
|
+
#
|
57
|
+
# Output:
|
58
|
+
# saving...
|
59
|
+
# - save
|
60
|
+
# saved
|
61
|
+
module Callbacks
|
62
|
+
extend Concern
|
63
|
+
|
64
|
+
included do
|
65
|
+
extend ActiveSupport::DescendantsTracker
|
66
|
+
end
|
67
|
+
|
68
|
+
CALLBACK_FILTER_TYPES = [:before, :after, :around]
|
69
|
+
|
70
|
+
# If true, Active Record and Active Model callbacks returning +false+ will
|
71
|
+
# halt the entire callback chain and display a deprecation message.
|
72
|
+
# If false, callback chains will only be halted by calling +throw :abort+.
|
73
|
+
# Defaults to +true+.
|
74
|
+
mattr_accessor(:halt_and_display_warning_on_return_false, instance_writer: false) { true }
|
75
|
+
|
76
|
+
# Runs the callbacks for the given event.
|
77
|
+
#
|
78
|
+
# Calls the before and around callbacks in the order they were set, yields
|
79
|
+
# the block (if given one), and then runs the after callbacks in reverse
|
80
|
+
# order.
|
81
|
+
#
|
82
|
+
# If the callback chain was halted, returns +false+. Otherwise returns the
|
83
|
+
# result of the block, +nil+ if no callbacks have been set, or +true+
|
84
|
+
# if callbacks have been set but no block is given.
|
85
|
+
#
|
86
|
+
# run_callbacks :save do
|
87
|
+
# save
|
88
|
+
# end
|
89
|
+
def run_callbacks(kind, &block)
|
90
|
+
send "_run_#{kind}_callbacks", &block
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def __run_callbacks__(callbacks, &block)
|
96
|
+
if callbacks.empty?
|
97
|
+
yield if block_given?
|
98
|
+
else
|
99
|
+
runner = callbacks.compile
|
100
|
+
e = Filters::Environment.new(self, false, nil, block)
|
101
|
+
runner.call(e).value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# A hook invoked every time a before callback is halted.
|
106
|
+
# This can be overridden in AS::Callback implementors in order
|
107
|
+
# to provide better debugging/logging.
|
108
|
+
def halted_callback_hook(filter)
|
109
|
+
end
|
110
|
+
|
111
|
+
module Conditionals # :nodoc:
|
112
|
+
class Value
|
113
|
+
def initialize(&block)
|
114
|
+
@block = block
|
115
|
+
end
|
116
|
+
def call(target, value); @block.call(value); end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module Filters
|
121
|
+
Environment = Struct.new(:target, :halted, :value, :run_block)
|
122
|
+
|
123
|
+
class End
|
124
|
+
def call(env)
|
125
|
+
block = env.run_block
|
126
|
+
env.value = !env.halted && (!block || block.call)
|
127
|
+
env
|
128
|
+
end
|
129
|
+
end
|
130
|
+
ENDING = End.new
|
131
|
+
|
132
|
+
class Before
|
133
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
|
134
|
+
halted_lambda = chain_config[:terminator]
|
135
|
+
|
136
|
+
if user_conditions.any?
|
137
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
138
|
+
else
|
139
|
+
halting(callback_sequence, user_callback, halted_lambda, filter)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
144
|
+
callback_sequence.before do |env|
|
145
|
+
target = env.target
|
146
|
+
value = env.value
|
147
|
+
halted = env.halted
|
148
|
+
|
149
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
150
|
+
result_lambda = -> { user_callback.call target, value }
|
151
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
152
|
+
if env.halted
|
153
|
+
target.send :halted_callback_hook, filter
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
env
|
158
|
+
end
|
159
|
+
end
|
160
|
+
private_class_method :halting_and_conditional
|
161
|
+
|
162
|
+
def self.halting(callback_sequence, user_callback, halted_lambda, filter)
|
163
|
+
callback_sequence.before do |env|
|
164
|
+
target = env.target
|
165
|
+
value = env.value
|
166
|
+
halted = env.halted
|
167
|
+
|
168
|
+
unless halted
|
169
|
+
result_lambda = -> { user_callback.call target, value }
|
170
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
171
|
+
|
172
|
+
if env.halted
|
173
|
+
target.send :halted_callback_hook, filter
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
env
|
178
|
+
end
|
179
|
+
end
|
180
|
+
private_class_method :halting
|
181
|
+
end
|
182
|
+
|
183
|
+
class After
|
184
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
185
|
+
if chain_config[:skip_after_callbacks_if_terminated]
|
186
|
+
if user_conditions.any?
|
187
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
188
|
+
else
|
189
|
+
halting(callback_sequence, user_callback)
|
190
|
+
end
|
191
|
+
else
|
192
|
+
if user_conditions.any?
|
193
|
+
conditional callback_sequence, user_callback, user_conditions
|
194
|
+
else
|
195
|
+
simple callback_sequence, user_callback
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
201
|
+
callback_sequence.after do |env|
|
202
|
+
target = env.target
|
203
|
+
value = env.value
|
204
|
+
halted = env.halted
|
205
|
+
|
206
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
207
|
+
user_callback.call target, value
|
208
|
+
end
|
209
|
+
|
210
|
+
env
|
211
|
+
end
|
212
|
+
end
|
213
|
+
private_class_method :halting_and_conditional
|
214
|
+
|
215
|
+
def self.halting(callback_sequence, user_callback)
|
216
|
+
callback_sequence.after do |env|
|
217
|
+
unless env.halted
|
218
|
+
user_callback.call env.target, env.value
|
219
|
+
end
|
220
|
+
|
221
|
+
env
|
222
|
+
end
|
223
|
+
end
|
224
|
+
private_class_method :halting
|
225
|
+
|
226
|
+
def self.conditional(callback_sequence, user_callback, user_conditions)
|
227
|
+
callback_sequence.after do |env|
|
228
|
+
target = env.target
|
229
|
+
value = env.value
|
230
|
+
|
231
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
232
|
+
user_callback.call target, value
|
233
|
+
end
|
234
|
+
|
235
|
+
env
|
236
|
+
end
|
237
|
+
end
|
238
|
+
private_class_method :conditional
|
239
|
+
|
240
|
+
def self.simple(callback_sequence, user_callback)
|
241
|
+
callback_sequence.after do |env|
|
242
|
+
user_callback.call env.target, env.value
|
243
|
+
|
244
|
+
env
|
245
|
+
end
|
246
|
+
end
|
247
|
+
private_class_method :simple
|
248
|
+
end
|
249
|
+
|
250
|
+
class Around
|
251
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
252
|
+
if user_conditions.any?
|
253
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
254
|
+
else
|
255
|
+
halting(callback_sequence, user_callback)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
260
|
+
callback_sequence.around do |env, &run|
|
261
|
+
target = env.target
|
262
|
+
value = env.value
|
263
|
+
halted = env.halted
|
264
|
+
|
265
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
266
|
+
user_callback.call(target, value) {
|
267
|
+
run.call.value
|
268
|
+
}
|
269
|
+
env
|
270
|
+
else
|
271
|
+
run.call
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
private_class_method :halting_and_conditional
|
276
|
+
|
277
|
+
def self.halting(callback_sequence, user_callback)
|
278
|
+
callback_sequence.around do |env, &run|
|
279
|
+
target = env.target
|
280
|
+
value = env.value
|
281
|
+
|
282
|
+
if env.halted
|
283
|
+
run.call
|
284
|
+
else
|
285
|
+
user_callback.call(target, value) {
|
286
|
+
run.call.value
|
287
|
+
}
|
288
|
+
env
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
private_class_method :halting
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class Callback #:nodoc:#
|
297
|
+
def self.build(chain, filter, kind, options)
|
298
|
+
if filter.is_a?(String)
|
299
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
300
|
+
Passing string to define callback is deprecated and will be removed
|
301
|
+
in Rails 5.1 without replacement.
|
302
|
+
MSG
|
303
|
+
end
|
304
|
+
|
305
|
+
new chain.name, filter, kind, options, chain.config
|
306
|
+
end
|
307
|
+
|
308
|
+
attr_accessor :kind, :name
|
309
|
+
attr_reader :chain_config
|
310
|
+
|
311
|
+
def initialize(name, filter, kind, options, chain_config)
|
312
|
+
@chain_config = chain_config
|
313
|
+
@name = name
|
314
|
+
@kind = kind
|
315
|
+
@filter = filter
|
316
|
+
@key = compute_identifier filter
|
317
|
+
@if = Array(options[:if])
|
318
|
+
@unless = Array(options[:unless])
|
319
|
+
end
|
320
|
+
|
321
|
+
def filter; @key; end
|
322
|
+
def raw_filter; @filter; end
|
323
|
+
|
324
|
+
def merge_conditional_options(chain, if_option:, unless_option:)
|
325
|
+
options = {
|
326
|
+
:if => @if.dup,
|
327
|
+
:unless => @unless.dup
|
328
|
+
}
|
329
|
+
|
330
|
+
options[:if].concat Array(unless_option)
|
331
|
+
options[:unless].concat Array(if_option)
|
332
|
+
|
333
|
+
self.class.build chain, @filter, @kind, options
|
334
|
+
end
|
335
|
+
|
336
|
+
def matches?(_kind, _filter)
|
337
|
+
@kind == _kind && filter == _filter
|
338
|
+
end
|
339
|
+
|
340
|
+
def duplicates?(other)
|
341
|
+
case @filter
|
342
|
+
when Symbol, String
|
343
|
+
matches?(other.kind, other.filter)
|
344
|
+
else
|
345
|
+
false
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Wraps code with filter
|
350
|
+
def apply(callback_sequence)
|
351
|
+
user_conditions = conditions_lambdas
|
352
|
+
user_callback = make_lambda @filter
|
353
|
+
|
354
|
+
case kind
|
355
|
+
when :before
|
356
|
+
Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
|
357
|
+
when :after
|
358
|
+
Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
|
359
|
+
when :around
|
360
|
+
Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
private
|
365
|
+
|
366
|
+
def invert_lambda(l)
|
367
|
+
lambda { |*args, &blk| !l.call(*args, &blk) }
|
368
|
+
end
|
369
|
+
|
370
|
+
# Filters support:
|
371
|
+
#
|
372
|
+
# Symbols:: A method to call.
|
373
|
+
# Strings:: Some content to evaluate.
|
374
|
+
# Procs:: A proc to call with the object.
|
375
|
+
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
|
376
|
+
#
|
377
|
+
# All of these objects are converted into a lambda and handled
|
378
|
+
# the same after this point.
|
379
|
+
def make_lambda(filter)
|
380
|
+
case filter
|
381
|
+
when Symbol
|
382
|
+
lambda { |target, _, &blk| target.send filter, &blk }
|
383
|
+
when String
|
384
|
+
l = eval "lambda { |value| #{filter} }"
|
385
|
+
lambda { |target, value| target.instance_exec(value, &l) }
|
386
|
+
when Conditionals::Value then filter
|
387
|
+
when ::Proc
|
388
|
+
if filter.arity > 1
|
389
|
+
return lambda { |target, _, &block|
|
390
|
+
raise ArgumentError unless block
|
391
|
+
target.instance_exec(target, block, &filter)
|
392
|
+
}
|
393
|
+
end
|
394
|
+
|
395
|
+
if filter.arity <= 0
|
396
|
+
lambda { |target, _| target.instance_exec(&filter) }
|
397
|
+
else
|
398
|
+
lambda { |target, _| target.instance_exec(target, &filter) }
|
399
|
+
end
|
400
|
+
else
|
401
|
+
scopes = Array(chain_config[:scope])
|
402
|
+
method_to_call = scopes.map{ |s| public_send(s) }.join("_")
|
403
|
+
|
404
|
+
lambda { |target, _, &blk|
|
405
|
+
filter.public_send method_to_call, target, &blk
|
406
|
+
}
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def compute_identifier(filter)
|
411
|
+
case filter
|
412
|
+
when String, ::Proc
|
413
|
+
filter.object_id
|
414
|
+
else
|
415
|
+
filter
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def conditions_lambdas
|
420
|
+
@if.map { |c| make_lambda c } +
|
421
|
+
@unless.map { |c| invert_lambda make_lambda c }
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Execute before and after filters in a sequence instead of
|
426
|
+
# chaining them with nested lambda calls, see:
|
427
|
+
# https://github.com/rails/rails/issues/18011
|
428
|
+
class CallbackSequence
|
429
|
+
def initialize(&call)
|
430
|
+
@call = call
|
431
|
+
@before = []
|
432
|
+
@after = []
|
433
|
+
end
|
434
|
+
|
435
|
+
def before(&before)
|
436
|
+
@before.unshift(before)
|
437
|
+
self
|
438
|
+
end
|
439
|
+
|
440
|
+
def after(&after)
|
441
|
+
@after.push(after)
|
442
|
+
self
|
443
|
+
end
|
444
|
+
|
445
|
+
def around(&around)
|
446
|
+
CallbackSequence.new do |arg|
|
447
|
+
around.call(arg) {
|
448
|
+
self.call(arg)
|
449
|
+
}
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def call(arg)
|
454
|
+
@before.each { |b| b.call(arg) }
|
455
|
+
value = @call.call(arg)
|
456
|
+
@after.each { |a| a.call(arg) }
|
457
|
+
value
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
# An Array with a compile method.
|
462
|
+
class CallbackChain #:nodoc:#
|
463
|
+
include Enumerable
|
464
|
+
|
465
|
+
attr_reader :name, :config
|
466
|
+
|
467
|
+
def initialize(name, config)
|
468
|
+
@name = name
|
469
|
+
@config = {
|
470
|
+
scope: [:kind],
|
471
|
+
terminator: default_terminator
|
472
|
+
}.merge!(config)
|
473
|
+
@chain = []
|
474
|
+
@callbacks = nil
|
475
|
+
@mutex = Mutex.new
|
476
|
+
end
|
477
|
+
|
478
|
+
def each(&block); @chain.each(&block); end
|
479
|
+
def index(o); @chain.index(o); end
|
480
|
+
def empty?; @chain.empty?; end
|
481
|
+
|
482
|
+
def insert(index, o)
|
483
|
+
@callbacks = nil
|
484
|
+
@chain.insert(index, o)
|
485
|
+
end
|
486
|
+
|
487
|
+
def delete(o)
|
488
|
+
@callbacks = nil
|
489
|
+
@chain.delete(o)
|
490
|
+
end
|
491
|
+
|
492
|
+
def clear
|
493
|
+
@callbacks = nil
|
494
|
+
@chain.clear
|
495
|
+
self
|
496
|
+
end
|
497
|
+
|
498
|
+
def initialize_copy(other)
|
499
|
+
@callbacks = nil
|
500
|
+
@chain = other.chain.dup
|
501
|
+
@mutex = Mutex.new
|
502
|
+
end
|
503
|
+
|
504
|
+
def compile
|
505
|
+
@callbacks || @mutex.synchronize do
|
506
|
+
final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
|
507
|
+
@callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
|
508
|
+
callback.apply callback_sequence
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def append(*callbacks)
|
514
|
+
callbacks.each { |c| append_one(c) }
|
515
|
+
end
|
516
|
+
|
517
|
+
def prepend(*callbacks)
|
518
|
+
callbacks.each { |c| prepend_one(c) }
|
519
|
+
end
|
520
|
+
|
521
|
+
protected
|
522
|
+
def chain; @chain; end
|
523
|
+
|
524
|
+
private
|
525
|
+
|
526
|
+
def append_one(callback)
|
527
|
+
@callbacks = nil
|
528
|
+
remove_duplicates(callback)
|
529
|
+
@chain.push(callback)
|
530
|
+
end
|
531
|
+
|
532
|
+
def prepend_one(callback)
|
533
|
+
@callbacks = nil
|
534
|
+
remove_duplicates(callback)
|
535
|
+
@chain.unshift(callback)
|
536
|
+
end
|
537
|
+
|
538
|
+
def remove_duplicates(callback)
|
539
|
+
@callbacks = nil
|
540
|
+
@chain.delete_if { |c| callback.duplicates?(c) }
|
541
|
+
end
|
542
|
+
|
543
|
+
def default_terminator
|
544
|
+
Proc.new do |target, result_lambda|
|
545
|
+
terminate = true
|
546
|
+
catch(:abort) do
|
547
|
+
result_lambda.call if result_lambda.is_a?(Proc)
|
548
|
+
terminate = false
|
549
|
+
end
|
550
|
+
terminate
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
module ClassMethods
|
556
|
+
def normalize_callback_params(filters, block) # :nodoc:
|
557
|
+
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
558
|
+
options = filters.extract_options!
|
559
|
+
filters.unshift(block) if block
|
560
|
+
[type, filters, options.dup]
|
561
|
+
end
|
562
|
+
|
563
|
+
# This is used internally to append, prepend and skip callbacks to the
|
564
|
+
# CallbackChain.
|
565
|
+
def __update_callbacks(name) #:nodoc:
|
566
|
+
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
|
567
|
+
chain = target.get_callbacks name
|
568
|
+
yield target, chain.dup
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
# Install a callback for the given event.
|
573
|
+
#
|
574
|
+
# set_callback :save, :before, :before_method
|
575
|
+
# set_callback :save, :after, :after_method, if: :condition
|
576
|
+
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
|
577
|
+
#
|
578
|
+
# The second argument indicates whether the callback is to be run +:before+,
|
579
|
+
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
|
580
|
+
# means the first example above can also be written as:
|
581
|
+
#
|
582
|
+
# set_callback :save, :before_method
|
583
|
+
#
|
584
|
+
# The callback can be specified as a symbol naming an instance method; as a
|
585
|
+
# proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
|
586
|
+
# object that responds to a certain method determined by the <tt>:scope</tt>
|
587
|
+
# argument to +define_callbacks+.
|
588
|
+
#
|
589
|
+
# If a proc, lambda, or block is given, its body is evaluated in the context
|
590
|
+
# of the current object. It can also optionally accept the current object as
|
591
|
+
# an argument.
|
592
|
+
#
|
593
|
+
# Before and around callbacks are called in the order that they are set;
|
594
|
+
# after callbacks are called in the reverse order.
|
595
|
+
#
|
596
|
+
# Around callbacks can access the return value from the event, if it
|
597
|
+
# wasn't halted, from the +yield+ call.
|
598
|
+
#
|
599
|
+
# ===== Options
|
600
|
+
#
|
601
|
+
# * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
|
602
|
+
# each naming an instance method or a proc; the callback will be called
|
603
|
+
# only when they all return a true value.
|
604
|
+
# * <tt>:unless</tt> - A symbol, a string or an array of symbols and
|
605
|
+
# strings, each naming an instance method or a proc; the callback will
|
606
|
+
# be called only when they all return a false value.
|
607
|
+
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
|
608
|
+
# existing chain rather than appended.
|
609
|
+
def set_callback(name, *filter_list, &block)
|
610
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
611
|
+
self_chain = get_callbacks name
|
612
|
+
mapped = filters.map do |filter|
|
613
|
+
Callback.build(self_chain, filter, type, options)
|
614
|
+
end
|
615
|
+
|
616
|
+
__update_callbacks(name) do |target, chain|
|
617
|
+
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
|
618
|
+
target.set_callbacks name, chain
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
|
623
|
+
# <tt>:unless</tt> options may be passed in order to control when the
|
624
|
+
# callback is skipped.
|
625
|
+
#
|
626
|
+
# class Writer < Person
|
627
|
+
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
|
628
|
+
# end
|
629
|
+
#
|
630
|
+
# An <tt>ArgumentError</tt> will be raised if the callback has not
|
631
|
+
# already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
|
632
|
+
def skip_callback(name, *filter_list, &block)
|
633
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
634
|
+
options[:raise] = true unless options.key?(:raise)
|
635
|
+
|
636
|
+
__update_callbacks(name) do |target, chain|
|
637
|
+
filters.each do |filter|
|
638
|
+
callback = chain.find {|c| c.matches?(type, filter) }
|
639
|
+
|
640
|
+
if !callback && options[:raise]
|
641
|
+
raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
|
642
|
+
end
|
643
|
+
|
644
|
+
if callback && (options.key?(:if) || options.key?(:unless))
|
645
|
+
new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
|
646
|
+
chain.insert(chain.index(callback), new_callback)
|
647
|
+
end
|
648
|
+
|
649
|
+
chain.delete(callback)
|
650
|
+
end
|
651
|
+
target.set_callbacks name, chain
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
# Remove all set callbacks for the given event.
|
656
|
+
def reset_callbacks(name)
|
657
|
+
callbacks = get_callbacks name
|
658
|
+
|
659
|
+
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
660
|
+
chain = target.get_callbacks(name).dup
|
661
|
+
callbacks.each { |c| chain.delete(c) }
|
662
|
+
target.set_callbacks name, chain
|
663
|
+
end
|
664
|
+
|
665
|
+
self.set_callbacks name, callbacks.dup.clear
|
666
|
+
end
|
667
|
+
|
668
|
+
# Define sets of events in the object life cycle that support callbacks.
|
669
|
+
#
|
670
|
+
# define_callbacks :validate
|
671
|
+
# define_callbacks :initialize, :save, :destroy
|
672
|
+
#
|
673
|
+
# ===== Options
|
674
|
+
#
|
675
|
+
# * <tt>:terminator</tt> - Determines when a before filter will halt the
|
676
|
+
# callback chain, preventing following before and around callbacks from
|
677
|
+
# being called and the event from being triggered.
|
678
|
+
# This should be a lambda to be executed.
|
679
|
+
# The current object and the result lambda of the callback will be provided
|
680
|
+
# to the terminator lambda.
|
681
|
+
#
|
682
|
+
# define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
|
683
|
+
#
|
684
|
+
# In this example, if any before validate callbacks returns +false+,
|
685
|
+
# any successive before and around callback is not executed.
|
686
|
+
#
|
687
|
+
# The default terminator halts the chain when a callback throws +:abort+.
|
688
|
+
#
|
689
|
+
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
|
690
|
+
# callbacks should be terminated by the <tt>:terminator</tt> option. By
|
691
|
+
# default after callbacks are executed no matter if callback chain was
|
692
|
+
# terminated or not. This option makes sense only when <tt>:terminator</tt>
|
693
|
+
# option is specified.
|
694
|
+
#
|
695
|
+
# * <tt>:scope</tt> - Indicates which methods should be executed when an
|
696
|
+
# object is used as a callback.
|
697
|
+
#
|
698
|
+
# class Audit
|
699
|
+
# def before(caller)
|
700
|
+
# puts 'Audit: before is called'
|
701
|
+
# end
|
702
|
+
#
|
703
|
+
# def before_save(caller)
|
704
|
+
# puts 'Audit: before_save is called'
|
705
|
+
# end
|
706
|
+
# end
|
707
|
+
#
|
708
|
+
# class Account
|
709
|
+
# include ActiveSupport::Callbacks
|
710
|
+
#
|
711
|
+
# define_callbacks :save
|
712
|
+
# set_callback :save, :before, Audit.new
|
713
|
+
#
|
714
|
+
# def save
|
715
|
+
# run_callbacks :save do
|
716
|
+
# puts 'save in main'
|
717
|
+
# end
|
718
|
+
# end
|
719
|
+
# end
|
720
|
+
#
|
721
|
+
# In the above case whenever you save an account the method
|
722
|
+
# <tt>Audit#before</tt> will be called. On the other hand
|
723
|
+
#
|
724
|
+
# define_callbacks :save, scope: [:kind, :name]
|
725
|
+
#
|
726
|
+
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
|
727
|
+
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
|
728
|
+
# case "kind" is "before" and "name" is "save". In this context +:kind+
|
729
|
+
# and +:name+ have special meanings: +:kind+ refers to the kind of
|
730
|
+
# callback (before/after/around) and +:name+ refers to the method on
|
731
|
+
# which callbacks are being defined.
|
732
|
+
#
|
733
|
+
# A declaration like
|
734
|
+
#
|
735
|
+
# define_callbacks :save, scope: [:name]
|
736
|
+
#
|
737
|
+
# would call <tt>Audit#save</tt>.
|
738
|
+
#
|
739
|
+
# NOTE: +method_name+ passed to `define_model_callbacks` must not end with
|
740
|
+
# `!`, `?` or `=`.
|
741
|
+
def define_callbacks(*names)
|
742
|
+
options = names.extract_options!
|
743
|
+
|
744
|
+
names.each do |name|
|
745
|
+
class_attribute "_#{name}_callbacks", instance_writer: false
|
746
|
+
set_callbacks name, CallbackChain.new(name, options)
|
747
|
+
|
748
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
749
|
+
def _run_#{name}_callbacks(&block)
|
750
|
+
__run_callbacks__(_#{name}_callbacks, &block)
|
751
|
+
end
|
752
|
+
RUBY
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
protected
|
757
|
+
|
758
|
+
def get_callbacks(name) # :nodoc:
|
759
|
+
send "_#{name}_callbacks"
|
760
|
+
end
|
761
|
+
|
762
|
+
def set_callbacks(name, callbacks) # :nodoc:
|
763
|
+
send "_#{name}_callbacks=", callbacks
|
764
|
+
end
|
765
|
+
|
766
|
+
def deprecated_false_terminator # :nodoc:
|
767
|
+
Proc.new do |target, result_lambda|
|
768
|
+
terminate = true
|
769
|
+
catch(:abort) do
|
770
|
+
result = result_lambda.call if result_lambda.is_a?(Proc)
|
771
|
+
if Callbacks.halt_and_display_warning_on_return_false && result == false
|
772
|
+
display_deprecation_warning_for_false_terminator
|
773
|
+
else
|
774
|
+
terminate = false
|
775
|
+
end
|
776
|
+
end
|
777
|
+
terminate
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
private
|
782
|
+
|
783
|
+
def display_deprecation_warning_for_false_terminator
|
784
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
785
|
+
Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
|
786
|
+
To explicitly halt the callback chain, please use `throw :abort` instead.
|
787
|
+
MSG
|
788
|
+
end
|
789
|
+
end
|
790
|
+
end
|
791
|
+
end
|