activesupport 5.0.7.2 → 5.1.7
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 +464 -694
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support.rb +8 -4
- data/lib/active_support/all.rb +3 -3
- data/lib/active_support/array_inquirer.rb +7 -5
- data/lib/active_support/backtrace_cleaner.rb +4 -4
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache.rb +41 -48
- data/lib/active_support/cache/file_store.rb +11 -20
- data/lib/active_support/cache/mem_cache_store.rb +30 -40
- data/lib/active_support/cache/memory_store.rb +13 -13
- data/lib/active_support/cache/null_store.rb +4 -4
- data/lib/active_support/cache/strategy/local_cache.rb +13 -22
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -5
- data/lib/active_support/callbacks.rb +649 -584
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
- data/lib/active_support/concurrency/share_lock.rb +20 -21
- data/lib/active_support/configurable.rb +5 -5
- data/lib/active_support/core_ext.rb +1 -2
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/array/access.rb +1 -1
- data/lib/active_support/core_ext/array/conversions.rb +15 -15
- data/lib/active_support/core_ext/array/grouping.rb +1 -1
- data/lib/active_support/core_ext/array/inquiry.rb +1 -1
- data/lib/active_support/core_ext/array/prepend_and_append.rb +1 -1
- data/lib/active_support/core_ext/benchmark.rb +1 -1
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +4 -6
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +5 -5
- data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +18 -4
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date/acts_like.rb +1 -1
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +8 -8
- data/lib/active_support/core_ext/date/conversions.rb +12 -12
- data/lib/active_support/core_ext/date/zones.rb +2 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -22
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_and_time/zones.rb +7 -8
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/date_time/acts_like.rb +2 -2
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +20 -10
- data/lib/active_support/core_ext/date_time/compatibility.rb +2 -2
- data/lib/active_support/core_ext/date_time/conversions.rb +12 -12
- data/lib/active_support/core_ext/digest/uuid.rb +4 -4
- data/lib/active_support/core_ext/enumerable.rb +23 -12
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/file/atomic.rb +4 -4
- data/lib/active_support/core_ext/hash.rb +9 -9
- data/lib/active_support/core_ext/hash/compact.rb +12 -9
- data/lib/active_support/core_ext/hash/conversions.rb +36 -37
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -2
- data/lib/active_support/core_ext/hash/keys.rb +6 -6
- data/lib/active_support/core_ext/hash/reverse_merge.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +4 -4
- data/lib/active_support/core_ext/hash/transform_values.rb +1 -0
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/integer/inflections.rb +1 -1
- data/lib/active_support/core_ext/integer/time.rb +2 -2
- data/lib/active_support/core_ext/kernel.rb +4 -4
- data/lib/active_support/core_ext/kernel/concern.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +1 -1
- data/lib/active_support/core_ext/load_error.rb +1 -18
- data/lib/active_support/core_ext/module.rb +11 -12
- data/lib/active_support/core_ext/module/aliasing.rb +3 -48
- data/lib/active_support/core_ext/module/attr_internal.rb +4 -4
- data/lib/active_support/core_ext/module/attribute_accessors.rb +11 -5
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +20 -13
- data/lib/active_support/core_ext/module/concerning.rb +1 -1
- data/lib/active_support/core_ext/module/delegation.rb +85 -16
- data/lib/active_support/core_ext/module/introspection.rb +3 -11
- data/lib/active_support/core_ext/module/reachable.rb +2 -2
- data/lib/active_support/core_ext/numeric.rb +4 -4
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -9
- data/lib/active_support/core_ext/numeric/inquiry.rb +21 -21
- data/lib/active_support/core_ext/numeric/time.rb +5 -5
- data/lib/active_support/core_ext/object.rb +12 -12
- data/lib/active_support/core_ext/object/blank.rb +3 -1
- data/lib/active_support/core_ext/object/conversions.rb +4 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +34 -4
- data/lib/active_support/core_ext/object/inclusion.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +26 -12
- data/lib/active_support/core_ext/object/to_param.rb +1 -1
- data/lib/active_support/core_ext/object/to_query.rb +8 -5
- data/lib/active_support/core_ext/object/try.rb +1 -1
- data/lib/active_support/core_ext/object/with_options.rb +12 -1
- data/lib/active_support/core_ext/range.rb +4 -4
- data/lib/active_support/core_ext/range/conversions.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +4 -0
- data/lib/active_support/core_ext/securerandom.rb +3 -3
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/string/access.rb +6 -6
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/indent.rb +4 -4
- data/lib/active_support/core_ext/string/inflections.rb +10 -14
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +19 -20
- data/lib/active_support/core_ext/string/strip.rb +1 -1
- data/lib/active_support/core_ext/string/zones.rb +2 -2
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/core_ext/time/acts_like.rb +1 -1
- data/lib/active_support/core_ext/time/calculations.rb +46 -29
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/zones.rb +3 -3
- data/lib/active_support/core_ext/uri.rb +2 -2
- data/lib/active_support/dependencies.rb +45 -46
- data/lib/active_support/dependencies/interlock.rb +1 -1
- data/lib/active_support/deprecation.rb +9 -8
- data/lib/active_support/deprecation/behaviors.rb +3 -3
- data/lib/active_support/deprecation/constant_accessor.rb +50 -0
- data/lib/active_support/deprecation/instance_delegator.rb +2 -2
- data/lib/active_support/deprecation/method_wrappers.rb +10 -3
- data/lib/active_support/deprecation/proxy_wrappers.rb +6 -4
- data/lib/active_support/deprecation/reporting.rb +7 -7
- data/lib/active_support/duration.rb +221 -28
- data/lib/active_support/duration/iso8601_parser.rb +66 -65
- data/lib/active_support/duration/iso8601_serializer.rb +11 -9
- data/lib/active_support/evented_file_update_checker.rb +59 -55
- data/lib/active_support/execution_wrapper.rb +3 -3
- data/lib/active_support/executor.rb +1 -1
- data/lib/active_support/file_update_checker.rb +54 -50
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +40 -28
- data/lib/active_support/i18n.rb +5 -5
- data/lib/active_support/i18n_railtie.rb +14 -9
- data/lib/active_support/inflections.rb +11 -11
- data/lib/active_support/inflector.rb +5 -5
- data/lib/active_support/inflector/inflections.rb +11 -9
- data/lib/active_support/inflector/methods.rb +52 -51
- data/lib/active_support/inflector/transliterate.rb +8 -11
- data/lib/active_support/json.rb +2 -2
- data/lib/active_support/json/decoding.rb +3 -3
- data/lib/active_support/json/encoding.rb +8 -7
- data/lib/active_support/key_generator.rb +17 -17
- data/lib/active_support/lazy_load_hooks.rb +2 -2
- data/lib/active_support/log_subscriber.rb +9 -7
- data/lib/active_support/log_subscriber/test_helper.rb +9 -9
- data/lib/active_support/logger.rb +3 -3
- data/lib/active_support/logger_silence.rb +3 -3
- data/lib/active_support/logger_thread_safe_level.rb +1 -1
- data/lib/active_support/message_encryptor.rb +77 -35
- data/lib/active_support/message_verifier.rb +7 -7
- data/lib/active_support/multibyte.rb +2 -2
- data/lib/active_support/multibyte/chars.rb +23 -21
- data/lib/active_support/multibyte/unicode.rb +68 -89
- data/lib/active_support/notifications.rb +7 -5
- data/lib/active_support/notifications/fanout.rb +3 -3
- data/lib/active_support/notifications/instrumenter.rb +5 -5
- data/lib/active_support/number_helper.rb +5 -4
- data/lib/active_support/number_helper/number_converter.rb +11 -11
- data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +8 -10
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -11
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -3
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -32
- data/lib/active_support/number_helper/rounding_helper.rb +64 -0
- data/lib/active_support/option_merger.rb +1 -1
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +6 -4
- data/lib/active_support/per_thread_registry.rb +5 -5
- data/lib/active_support/rails.rb +12 -6
- data/lib/active_support/railtie.rb +3 -3
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +6 -6
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +8 -2
- data/lib/active_support/subscriber.rb +9 -5
- data/lib/active_support/tagged_logging.rb +4 -4
- data/lib/active_support/test_case.rb +12 -29
- data/lib/active_support/testing/assertions.rb +100 -2
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/constant_lookup.rb +0 -1
- data/lib/active_support/testing/declarative.rb +1 -1
- data/lib/active_support/testing/deprecation.rb +3 -2
- data/lib/active_support/testing/isolation.rb +15 -22
- data/lib/active_support/testing/method_call_assertions.rb +1 -1
- data/lib/active_support/testing/setup_and_teardown.rb +2 -2
- data/lib/active_support/testing/stream.rb +28 -28
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +45 -11
- data/lib/active_support/time.rb +12 -12
- data/lib/active_support/time_with_zone.rb +16 -14
- data/lib/active_support/values/time_zone.rb +100 -31
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini.rb +34 -36
- data/lib/active_support/xml_mini/jdom.rb +112 -112
- data/lib/active_support/xml_mini/libxml.rb +12 -11
- data/lib/active_support/xml_mini/libxmlsax.rb +13 -14
- data/lib/active_support/xml_mini/nokogiri.rb +10 -10
- data/lib/active_support/xml_mini/nokogirisax.rb +12 -13
- data/lib/active_support/xml_mini/rexml.rb +9 -9
- metadata +8 -9
- data/lib/active_support/concurrency/latch.rb +0 -26
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
- data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
- data/lib/active_support/core_ext/struct.rb +0 -3
- data/lib/active_support/core_ext/time/marshal.rb +0 -3
@@ -1,13 +1,13 @@
|
|
1
1
|
begin
|
2
|
-
require
|
2
|
+
require "dalli"
|
3
3
|
rescue LoadError => e
|
4
4
|
$stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
|
5
5
|
raise e
|
6
6
|
end
|
7
7
|
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
8
|
+
require "digest/md5"
|
9
|
+
require "active_support/core_ext/marshal"
|
10
|
+
require "active_support/core_ext/array/extract_options"
|
11
11
|
|
12
12
|
module ActiveSupport
|
13
13
|
module Cache
|
@@ -26,24 +26,24 @@ module ActiveSupport
|
|
26
26
|
class MemCacheStore < Store
|
27
27
|
# Provide support for raw values in the local cache strategy.
|
28
28
|
module LocalCacheWithRaw # :nodoc:
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
private
|
30
|
+
def read_entry(key, options)
|
31
|
+
entry = super
|
32
|
+
if options[:raw] && local_cache && entry
|
33
|
+
entry = deserialize_entry(entry.value)
|
34
|
+
end
|
35
|
+
entry
|
34
36
|
end
|
35
|
-
entry
|
36
|
-
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
def write_entry(key, entry, options)
|
39
|
+
if options[:raw] && local_cache
|
40
|
+
raw_entry = Entry.new(entry.value.to_s)
|
41
|
+
raw_entry.expires_at = entry.expires_at
|
42
|
+
super(key, raw_entry, options)
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
45
46
|
end
|
46
|
-
end
|
47
47
|
end
|
48
48
|
|
49
49
|
prepend Strategy::LocalCache
|
@@ -85,7 +85,7 @@ module ActiveSupport
|
|
85
85
|
@data = addresses.first
|
86
86
|
else
|
87
87
|
mem_cache_options = options.dup
|
88
|
-
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
88
|
+
UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
|
89
89
|
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
90
90
|
end
|
91
91
|
end
|
@@ -96,7 +96,7 @@ module ActiveSupport
|
|
96
96
|
options = names.extract_options!
|
97
97
|
options = merged_options(options)
|
98
98
|
|
99
|
-
keys_to_names = Hash[names.map{|name| [normalize_key(name, options), name]}]
|
99
|
+
keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
|
100
100
|
raw_values = @data.get_multi(keys_to_names.keys)
|
101
101
|
values = {}
|
102
102
|
raw_values.each do |key, value|
|
@@ -110,9 +110,9 @@ module ActiveSupport
|
|
110
110
|
# operator and can only be used on values written with the :raw option.
|
111
111
|
# Calling it on a value not stored with :raw will initialize that value
|
112
112
|
# to zero.
|
113
|
-
def increment(name, amount = 1, options = nil)
|
113
|
+
def increment(name, amount = 1, options = nil)
|
114
114
|
options = merged_options(options)
|
115
|
-
instrument(:increment, name, :
|
115
|
+
instrument(:increment, name, amount: amount) do
|
116
116
|
rescue_error_with nil do
|
117
117
|
@data.incr(normalize_key(name, options), amount)
|
118
118
|
end
|
@@ -123,9 +123,9 @@ module ActiveSupport
|
|
123
123
|
# operator and can only be used on values written with the :raw option.
|
124
124
|
# Calling it on a value not stored with :raw will initialize that value
|
125
125
|
# to zero.
|
126
|
-
def decrement(name, amount = 1, options = nil)
|
126
|
+
def decrement(name, amount = 1, options = nil)
|
127
127
|
options = merged_options(options)
|
128
|
-
instrument(:decrement, name, :
|
128
|
+
instrument(:decrement, name, amount: amount) do
|
129
129
|
rescue_error_with nil do
|
130
130
|
@data.decr(normalize_key(name, options), amount)
|
131
131
|
end
|
@@ -143,14 +143,14 @@ module ActiveSupport
|
|
143
143
|
@data.stats
|
144
144
|
end
|
145
145
|
|
146
|
-
|
146
|
+
private
|
147
147
|
# Read an entry from the cache.
|
148
|
-
def read_entry(key, options)
|
148
|
+
def read_entry(key, options)
|
149
149
|
rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) }
|
150
150
|
end
|
151
151
|
|
152
152
|
# Write an entry to the cache.
|
153
|
-
def write_entry(key, entry, options)
|
153
|
+
def write_entry(key, entry, options)
|
154
154
|
method = options && options[:unless_exist] ? :add : :set
|
155
155
|
value = options[:raw] ? entry.value.to_s : entry
|
156
156
|
expires_in = options[:expires_in].to_i
|
@@ -164,31 +164,21 @@ module ActiveSupport
|
|
164
164
|
end
|
165
165
|
|
166
166
|
# Delete an entry from the cache.
|
167
|
-
def delete_entry(key, options)
|
167
|
+
def delete_entry(key, options)
|
168
168
|
rescue_error_with(false) { @data.delete(key) }
|
169
169
|
end
|
170
170
|
|
171
|
-
private
|
172
|
-
|
173
171
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
174
172
|
# before applying the regular expression to ensure we are escaping all
|
175
173
|
# characters properly.
|
176
174
|
def normalize_key(key, options)
|
177
175
|
key = super.dup
|
178
176
|
key = key.force_encoding(Encoding::ASCII_8BIT)
|
179
|
-
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
177
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
180
178
|
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
|
181
179
|
key
|
182
180
|
end
|
183
181
|
|
184
|
-
def escape_key(key)
|
185
|
-
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
186
|
-
`escape_key` is deprecated and will be removed from Rails 5.1.
|
187
|
-
Please use `normalize_key` which will return a fully resolved key or nothing.
|
188
|
-
MESSAGE
|
189
|
-
key
|
190
|
-
end
|
191
|
-
|
192
182
|
def deserialize_entry(raw_value)
|
193
183
|
if raw_value
|
194
184
|
entry = Marshal.load(raw_value) rescue raw_value
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
1
|
+
require "monitor"
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Cache
|
5
5
|
# A cache store implementation which stores everything into memory in the
|
6
6
|
# same process. If you're running multiple Ruby on Rails server processes
|
7
|
-
# (which is the case if you're using
|
7
|
+
# (which is the case if you're using Phusion Passenger or puma clustered mode),
|
8
8
|
# then this means that Rails server process instances won't be able
|
9
9
|
# to share cache data with each other and this may not be the most
|
10
10
|
# appropriate cache in that scenario.
|
@@ -28,6 +28,7 @@ module ActiveSupport
|
|
28
28
|
@pruning = false
|
29
29
|
end
|
30
30
|
|
31
|
+
# Delete all data stored in a given cache store.
|
31
32
|
def clear(options = nil)
|
32
33
|
synchronize do
|
33
34
|
@data.clear
|
@@ -39,8 +40,8 @@ module ActiveSupport
|
|
39
40
|
# Preemptively iterates through all stored keys and removes the ones which have expired.
|
40
41
|
def cleanup(options = nil)
|
41
42
|
options = merged_options(options)
|
42
|
-
instrument(:cleanup, :
|
43
|
-
keys = synchronize{ @data.keys }
|
43
|
+
instrument(:cleanup, size: @data.size) do
|
44
|
+
keys = synchronize { @data.keys }
|
44
45
|
keys.each do |key|
|
45
46
|
entry = @data[key]
|
46
47
|
delete_entry(key, options) if entry && entry.expired?
|
@@ -56,8 +57,8 @@ module ActiveSupport
|
|
56
57
|
begin
|
57
58
|
start_time = Time.now
|
58
59
|
cleanup
|
59
|
-
instrument(:prune, target_size, :
|
60
|
-
keys = synchronize{ @key_access.keys.sort{|a,b| @key_access[a].to_f <=> @key_access[b].to_f} }
|
60
|
+
instrument(:prune, target_size, from: @cache_size) do
|
61
|
+
keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
|
61
62
|
keys.each do |key|
|
62
63
|
delete_entry(key, options)
|
63
64
|
return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
|
@@ -83,6 +84,7 @@ module ActiveSupport
|
|
83
84
|
modify_value(name, -amount, options)
|
84
85
|
end
|
85
86
|
|
87
|
+
# Deletes cache entries if the cache key matches a given pattern.
|
86
88
|
def delete_matched(matcher, options = nil)
|
87
89
|
options = merged_options(options)
|
88
90
|
instrument(:delete_matched, matcher.inspect) do
|
@@ -104,15 +106,15 @@ module ActiveSupport
|
|
104
106
|
@monitor.synchronize(&block)
|
105
107
|
end
|
106
108
|
|
107
|
-
|
109
|
+
private
|
108
110
|
|
109
111
|
PER_ENTRY_OVERHEAD = 240
|
110
112
|
|
111
|
-
def cached_size(key, entry)
|
113
|
+
def cached_size(key, entry)
|
112
114
|
key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
|
113
115
|
end
|
114
116
|
|
115
|
-
def read_entry(key, options)
|
117
|
+
def read_entry(key, options)
|
116
118
|
entry = @data[key]
|
117
119
|
synchronize do
|
118
120
|
if entry
|
@@ -124,7 +126,7 @@ module ActiveSupport
|
|
124
126
|
entry
|
125
127
|
end
|
126
128
|
|
127
|
-
def write_entry(key, entry, options)
|
129
|
+
def write_entry(key, entry, options)
|
128
130
|
entry.dup_value!
|
129
131
|
synchronize do
|
130
132
|
old_entry = @data[key]
|
@@ -141,7 +143,7 @@ module ActiveSupport
|
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
|
-
def delete_entry(key, options)
|
146
|
+
def delete_entry(key, options)
|
145
147
|
synchronize do
|
146
148
|
@key_access.delete(key)
|
147
149
|
entry = @data.delete(key)
|
@@ -150,8 +152,6 @@ module ActiveSupport
|
|
150
152
|
end
|
151
153
|
end
|
152
154
|
|
153
|
-
private
|
154
|
-
|
155
155
|
def modify_value(name, amount, options)
|
156
156
|
synchronize do
|
157
157
|
options = merged_options(options)
|
@@ -25,15 +25,15 @@ module ActiveSupport
|
|
25
25
|
def delete_matched(matcher, options = nil)
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
def read_entry(key, options)
|
28
|
+
private
|
29
|
+
def read_entry(key, options)
|
30
30
|
end
|
31
31
|
|
32
|
-
def write_entry(key, entry, options)
|
32
|
+
def write_entry(key, entry, options)
|
33
33
|
true
|
34
34
|
end
|
35
35
|
|
36
|
-
def delete_entry(key, options)
|
36
|
+
def delete_entry(key, options)
|
37
37
|
false
|
38
38
|
end
|
39
39
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "active_support/core_ext/object/duplicable"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
require "active_support/per_thread_registry"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
6
|
module Cache
|
@@ -9,7 +9,7 @@ module ActiveSupport
|
|
9
9
|
# duration of a block. Repeated calls to the cache for the same key will hit the
|
10
10
|
# in-memory cache for faster access.
|
11
11
|
module LocalCache
|
12
|
-
autoload :Middleware,
|
12
|
+
autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
|
13
13
|
|
14
14
|
# Class for storing and registering the local caches.
|
15
15
|
class LocalCacheRegistry # :nodoc:
|
@@ -70,6 +70,7 @@ module ActiveSupport
|
|
70
70
|
def with_local_cache
|
71
71
|
use_temporary_local_cache(LocalStore.new) { yield }
|
72
72
|
end
|
73
|
+
|
73
74
|
# Middleware class can be inserted as a Rack handler to be local cache for the
|
74
75
|
# duration of request.
|
75
76
|
def middleware
|
@@ -92,20 +93,20 @@ module ActiveSupport
|
|
92
93
|
|
93
94
|
def increment(name, amount = 1, options = nil) # :nodoc:
|
94
95
|
return super unless local_cache
|
95
|
-
value = bypass_local_cache{super}
|
96
|
+
value = bypass_local_cache { super }
|
96
97
|
write_cache_value(name, value, options)
|
97
98
|
value
|
98
99
|
end
|
99
100
|
|
100
101
|
def decrement(name, amount = 1, options = nil) # :nodoc:
|
101
102
|
return super unless local_cache
|
102
|
-
value = bypass_local_cache{super}
|
103
|
+
value = bypass_local_cache { super }
|
103
104
|
write_cache_value(name, value, options)
|
104
105
|
value
|
105
106
|
end
|
106
107
|
|
107
|
-
|
108
|
-
def read_entry(key, options)
|
108
|
+
private
|
109
|
+
def read_entry(key, options)
|
109
110
|
if cache = local_cache
|
110
111
|
cache.fetch_entry(key) { super }
|
111
112
|
else
|
@@ -113,25 +114,17 @@ module ActiveSupport
|
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
116
|
-
def write_entry(key, entry, options)
|
117
|
+
def write_entry(key, entry, options)
|
117
118
|
local_cache.write_entry(key, entry, options) if local_cache
|
118
119
|
super
|
119
120
|
end
|
120
121
|
|
121
|
-
def delete_entry(key, options)
|
122
|
+
def delete_entry(key, options)
|
122
123
|
local_cache.delete_entry(key, options) if local_cache
|
123
124
|
super
|
124
125
|
end
|
125
126
|
|
126
|
-
def
|
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:
|
127
|
+
def write_cache_value(name, value, options)
|
135
128
|
name = normalize_key(name, options)
|
136
129
|
cache = local_cache
|
137
130
|
cache.mute do
|
@@ -143,10 +136,8 @@ module ActiveSupport
|
|
143
136
|
end
|
144
137
|
end
|
145
138
|
|
146
|
-
private
|
147
|
-
|
148
139
|
def local_cache_key
|
149
|
-
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/,
|
140
|
+
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
|
150
141
|
end
|
151
142
|
|
152
143
|
def local_cache
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rack/body_proxy"
|
2
|
+
require "rack/utils"
|
3
3
|
|
4
4
|
module ActiveSupport
|
5
5
|
module Cache
|
6
6
|
module Strategy
|
7
7
|
module LocalCache
|
8
|
-
|
9
8
|
#--
|
10
9
|
# This class wraps up local storage for middlewares. Only the middleware method should
|
11
10
|
# construct them.
|
@@ -13,9 +12,9 @@ module ActiveSupport
|
|
13
12
|
attr_reader :name, :local_cache_key
|
14
13
|
|
15
14
|
def initialize(name, local_cache_key)
|
16
|
-
@name
|
15
|
+
@name = name
|
17
16
|
@local_cache_key = local_cache_key
|
18
|
-
@app
|
17
|
+
@app = nil
|
19
18
|
end
|
20
19
|
|
21
20
|
def new(app)
|
@@ -1,13 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require 'thread'
|
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/string/filters"
|
8
|
+
require "active_support/deprecation"
|
9
|
+
require "thread"
|
11
10
|
|
12
11
|
module ActiveSupport
|
13
12
|
# Callbacks are code hooks that are run at key points in an object's life cycle.
|
@@ -63,16 +62,12 @@ module ActiveSupport
|
|
63
62
|
|
64
63
|
included do
|
65
64
|
extend ActiveSupport::DescendantsTracker
|
65
|
+
class_attribute :__callbacks, instance_writer: false
|
66
|
+
self.__callbacks ||= {}
|
66
67
|
end
|
67
68
|
|
68
69
|
CALLBACK_FILTER_TYPES = [:before, :after, :around]
|
69
70
|
|
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
71
|
# Runs the callbacks for the given event.
|
77
72
|
#
|
78
73
|
# Calls the before and around callbacks in the order they were set, yields
|
@@ -86,706 +81,776 @@ module ActiveSupport
|
|
86
81
|
# run_callbacks :save do
|
87
82
|
# save
|
88
83
|
# end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
84
|
+
#
|
85
|
+
#--
|
86
|
+
#
|
87
|
+
# As this method is used in many places, and often wraps large portions of
|
88
|
+
# user code, it has an additional design goal of minimizing its impact on
|
89
|
+
# the visible call stack. An exception from inside a :before or :after
|
90
|
+
# callback can be as noisy as it likes -- but when control has passed
|
91
|
+
# smoothly through and into the supplied block, we want as little evidence
|
92
|
+
# as possible that we were here.
|
93
|
+
def run_callbacks(kind)
|
94
|
+
callbacks = __callbacks[kind.to_sym]
|
94
95
|
|
95
|
-
def __run_callbacks__(callbacks, &block)
|
96
96
|
if callbacks.empty?
|
97
97
|
yield if block_given?
|
98
98
|
else
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
99
|
+
env = Filters::Environment.new(self, false, nil)
|
100
|
+
next_sequence = callbacks.compile
|
101
|
+
|
102
|
+
invoke_sequence = Proc.new do
|
103
|
+
skipped = nil
|
104
|
+
while true
|
105
|
+
current = next_sequence
|
106
|
+
current.invoke_before(env)
|
107
|
+
if current.final?
|
108
|
+
env.value = !env.halted && (!block_given? || yield)
|
109
|
+
elsif current.skip?(env)
|
110
|
+
(skipped ||= []) << current
|
111
|
+
next_sequence = next_sequence.nested
|
112
|
+
next
|
113
|
+
else
|
114
|
+
next_sequence = next_sequence.nested
|
115
|
+
begin
|
116
|
+
target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
|
117
|
+
target.send(method, *arguments, &block)
|
118
|
+
ensure
|
119
|
+
next_sequence = current
|
120
|
+
end
|
121
|
+
end
|
122
|
+
current.invoke_after(env)
|
123
|
+
skipped.pop.invoke_after(env) while skipped && skipped.first
|
124
|
+
break env.value
|
125
|
+
end
|
126
|
+
end
|
110
127
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
128
|
+
# Common case: no 'around' callbacks defined
|
129
|
+
if next_sequence.final?
|
130
|
+
next_sequence.invoke_before(env)
|
131
|
+
env.value = !env.halted && (!block_given? || yield)
|
132
|
+
next_sequence.invoke_after(env)
|
133
|
+
env.value
|
134
|
+
else
|
135
|
+
invoke_sequence.call
|
115
136
|
end
|
116
|
-
def call(target, value); @block.call(value); end
|
117
137
|
end
|
118
138
|
end
|
119
139
|
|
120
|
-
|
121
|
-
Environment = Struct.new(:target, :halted, :value, :run_block)
|
140
|
+
private
|
122
141
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
env
|
128
|
-
end
|
142
|
+
# A hook invoked every time a before callback is halted.
|
143
|
+
# This can be overridden in ActiveSupport::Callbacks implementors in order
|
144
|
+
# to provide better debugging/logging.
|
145
|
+
def halted_callback_hook(filter)
|
129
146
|
end
|
130
|
-
ENDING = End.new
|
131
147
|
|
132
|
-
|
133
|
-
|
134
|
-
|
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)
|
148
|
+
module Conditionals # :nodoc:
|
149
|
+
class Value
|
150
|
+
def initialize(&block)
|
151
|
+
@block = block
|
140
152
|
end
|
153
|
+
def call(target, value); @block.call(value); end
|
141
154
|
end
|
155
|
+
end
|
142
156
|
|
143
|
-
|
144
|
-
|
145
|
-
target = env.target
|
146
|
-
value = env.value
|
147
|
-
halted = env.halted
|
157
|
+
module Filters
|
158
|
+
Environment = Struct.new(:target, :halted, :value)
|
148
159
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
160
|
+
class Before
|
161
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
|
162
|
+
halted_lambda = chain_config[:terminator]
|
163
|
+
|
164
|
+
if user_conditions.any?
|
165
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
166
|
+
else
|
167
|
+
halting(callback_sequence, user_callback, halted_lambda, filter)
|
155
168
|
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
172
|
+
callback_sequence.before do |env|
|
173
|
+
target = env.target
|
174
|
+
value = env.value
|
175
|
+
halted = env.halted
|
176
|
+
|
177
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
178
|
+
result_lambda = -> { user_callback.call target, value }
|
179
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
180
|
+
if env.halted
|
181
|
+
target.send :halted_callback_hook, filter
|
182
|
+
end
|
183
|
+
end
|
156
184
|
|
157
|
-
|
185
|
+
env
|
186
|
+
end
|
158
187
|
end
|
159
|
-
|
160
|
-
private_class_method :halting_and_conditional
|
188
|
+
private_class_method :halting_and_conditional
|
161
189
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
190
|
+
def self.halting(callback_sequence, user_callback, halted_lambda, filter)
|
191
|
+
callback_sequence.before do |env|
|
192
|
+
target = env.target
|
193
|
+
value = env.value
|
194
|
+
halted = env.halted
|
167
195
|
|
168
|
-
|
169
|
-
|
170
|
-
|
196
|
+
unless halted
|
197
|
+
result_lambda = -> { user_callback.call target, value }
|
198
|
+
env.halted = halted_lambda.call(target, result_lambda)
|
171
199
|
|
172
|
-
|
173
|
-
|
200
|
+
if env.halted
|
201
|
+
target.send :halted_callback_hook, filter
|
202
|
+
end
|
174
203
|
end
|
175
|
-
end
|
176
204
|
|
177
|
-
|
205
|
+
env
|
206
|
+
end
|
178
207
|
end
|
208
|
+
private_class_method :halting
|
179
209
|
end
|
180
|
-
private_class_method :halting
|
181
|
-
end
|
182
210
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
211
|
+
class After
|
212
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
213
|
+
if chain_config[:skip_after_callbacks_if_terminated]
|
214
|
+
if user_conditions.any?
|
215
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
216
|
+
else
|
217
|
+
halting(callback_sequence, user_callback)
|
218
|
+
end
|
188
219
|
else
|
189
|
-
|
220
|
+
if user_conditions.any?
|
221
|
+
conditional callback_sequence, user_callback, user_conditions
|
222
|
+
else
|
223
|
+
simple callback_sequence, user_callback
|
224
|
+
end
|
190
225
|
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
229
|
+
callback_sequence.after do |env|
|
230
|
+
target = env.target
|
231
|
+
value = env.value
|
232
|
+
halted = env.halted
|
233
|
+
|
234
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
235
|
+
user_callback.call target, value
|
236
|
+
end
|
237
|
+
|
238
|
+
env
|
196
239
|
end
|
197
240
|
end
|
198
|
-
|
241
|
+
private_class_method :halting_and_conditional
|
199
242
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
243
|
+
def self.halting(callback_sequence, user_callback)
|
244
|
+
callback_sequence.after do |env|
|
245
|
+
unless env.halted
|
246
|
+
user_callback.call env.target, env.value
|
247
|
+
end
|
205
248
|
|
206
|
-
|
207
|
-
user_callback.call target, value
|
249
|
+
env
|
208
250
|
end
|
251
|
+
end
|
252
|
+
private_class_method :halting
|
253
|
+
|
254
|
+
def self.conditional(callback_sequence, user_callback, user_conditions)
|
255
|
+
callback_sequence.after do |env|
|
256
|
+
target = env.target
|
257
|
+
value = env.value
|
258
|
+
|
259
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
260
|
+
user_callback.call target, value
|
261
|
+
end
|
209
262
|
|
210
|
-
|
263
|
+
env
|
264
|
+
end
|
211
265
|
end
|
212
|
-
|
213
|
-
private_class_method :halting_and_conditional
|
266
|
+
private_class_method :conditional
|
214
267
|
|
215
|
-
|
216
|
-
|
217
|
-
unless env.halted
|
268
|
+
def self.simple(callback_sequence, user_callback)
|
269
|
+
callback_sequence.after do |env|
|
218
270
|
user_callback.call env.target, env.value
|
219
|
-
end
|
220
271
|
|
221
|
-
|
272
|
+
env
|
273
|
+
end
|
222
274
|
end
|
275
|
+
private_class_method :simple
|
223
276
|
end
|
224
|
-
|
277
|
+
end
|
225
278
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
279
|
+
class Callback #:nodoc:#
|
280
|
+
def self.build(chain, filter, kind, options)
|
281
|
+
if filter.is_a?(String)
|
282
|
+
raise ArgumentError, <<-MSG.squish
|
283
|
+
Passing string to define a callback is not supported. See the `.set_callback`
|
284
|
+
documentation to see supported values.
|
285
|
+
MSG
|
286
|
+
end
|
230
287
|
|
231
|
-
|
232
|
-
|
233
|
-
end
|
288
|
+
new chain.name, filter, kind, options, chain.config
|
289
|
+
end
|
234
290
|
|
235
|
-
|
236
|
-
|
291
|
+
attr_accessor :kind, :name
|
292
|
+
attr_reader :chain_config
|
293
|
+
|
294
|
+
def initialize(name, filter, kind, options, chain_config)
|
295
|
+
@chain_config = chain_config
|
296
|
+
@name = name
|
297
|
+
@kind = kind
|
298
|
+
@filter = filter
|
299
|
+
@key = compute_identifier filter
|
300
|
+
@if = Array(options[:if])
|
301
|
+
@unless = Array(options[:unless])
|
237
302
|
end
|
238
|
-
private_class_method :conditional
|
239
303
|
|
240
|
-
def
|
241
|
-
|
242
|
-
user_callback.call env.target, env.value
|
304
|
+
def filter; @key; end
|
305
|
+
def raw_filter; @filter; end
|
243
306
|
|
244
|
-
|
245
|
-
|
307
|
+
def merge_conditional_options(chain, if_option:, unless_option:)
|
308
|
+
options = {
|
309
|
+
if: @if.dup,
|
310
|
+
unless: @unless.dup
|
311
|
+
}
|
312
|
+
|
313
|
+
options[:if].concat Array(unless_option)
|
314
|
+
options[:unless].concat Array(if_option)
|
315
|
+
|
316
|
+
self.class.build chain, @filter, @kind, options
|
246
317
|
end
|
247
|
-
private_class_method :simple
|
248
|
-
end
|
249
318
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
319
|
+
def matches?(_kind, _filter)
|
320
|
+
@kind == _kind && filter == _filter
|
321
|
+
end
|
322
|
+
|
323
|
+
def duplicates?(other)
|
324
|
+
case @filter
|
325
|
+
when Symbol, String
|
326
|
+
matches?(other.kind, other.filter)
|
254
327
|
else
|
255
|
-
|
328
|
+
false
|
256
329
|
end
|
257
330
|
end
|
258
331
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
run.call
|
272
|
-
end
|
332
|
+
# Wraps code with filter
|
333
|
+
def apply(callback_sequence)
|
334
|
+
user_conditions = conditions_lambdas
|
335
|
+
user_callback = CallTemplate.build(@filter, self)
|
336
|
+
|
337
|
+
case kind
|
338
|
+
when :before
|
339
|
+
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
|
340
|
+
when :after
|
341
|
+
Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
|
342
|
+
when :around
|
343
|
+
callback_sequence.around(user_callback, user_conditions)
|
273
344
|
end
|
274
345
|
end
|
275
|
-
private_class_method :halting_and_conditional
|
276
346
|
|
277
|
-
def
|
278
|
-
|
279
|
-
|
280
|
-
value = env.value
|
347
|
+
def current_scopes
|
348
|
+
Array(chain_config[:scope]).map { |s| public_send(s) }
|
349
|
+
end
|
281
350
|
|
282
|
-
|
283
|
-
|
351
|
+
private
|
352
|
+
def compute_identifier(filter)
|
353
|
+
case filter
|
354
|
+
when String, ::Proc
|
355
|
+
filter.object_id
|
284
356
|
else
|
285
|
-
|
286
|
-
run.call.value
|
287
|
-
}
|
288
|
-
env
|
357
|
+
filter
|
289
358
|
end
|
290
359
|
end
|
291
|
-
|
292
|
-
|
360
|
+
|
361
|
+
def conditions_lambdas
|
362
|
+
@if.map { |c| CallTemplate.build(c, self).make_lambda } +
|
363
|
+
@unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
|
364
|
+
end
|
293
365
|
end
|
294
|
-
end
|
295
366
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
367
|
+
# A future invocation of user-supplied code (either as a callback,
|
368
|
+
# or a condition filter).
|
369
|
+
class CallTemplate # :nodoc:
|
370
|
+
def initialize(target, method, arguments, block)
|
371
|
+
@override_target = target
|
372
|
+
@method_name = method
|
373
|
+
@arguments = arguments
|
374
|
+
@override_block = block
|
303
375
|
end
|
304
376
|
|
305
|
-
|
306
|
-
|
377
|
+
# Return the parts needed to make this call, with the given
|
378
|
+
# input values.
|
379
|
+
#
|
380
|
+
# Returns an array of the form:
|
381
|
+
#
|
382
|
+
# [target, block, method, *arguments]
|
383
|
+
#
|
384
|
+
# This array can be used as such:
|
385
|
+
#
|
386
|
+
# target.send(method, *arguments, &block)
|
387
|
+
#
|
388
|
+
# The actual invocation is left up to the caller to minimize
|
389
|
+
# call stack pollution.
|
390
|
+
def expand(target, value, block)
|
391
|
+
result = @arguments.map { |arg|
|
392
|
+
case arg
|
393
|
+
when :value; value
|
394
|
+
when :target; target
|
395
|
+
when :block; block || raise(ArgumentError)
|
396
|
+
end
|
397
|
+
}
|
307
398
|
|
308
|
-
|
309
|
-
|
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
|
399
|
+
result.unshift @method_name
|
400
|
+
result.unshift @override_block || block
|
401
|
+
result.unshift @override_target || target
|
320
402
|
|
321
|
-
|
322
|
-
|
403
|
+
# target, block, method, *arguments = result
|
404
|
+
# target.send(method, *arguments, &block)
|
405
|
+
result
|
406
|
+
end
|
323
407
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
408
|
+
# Return a lambda that will make this call when given the input
|
409
|
+
# values.
|
410
|
+
def make_lambda
|
411
|
+
lambda do |target, value, &block|
|
412
|
+
target, block, method, *arguments = expand(target, value, block)
|
413
|
+
target.send(method, *arguments, &block)
|
414
|
+
end
|
415
|
+
end
|
329
416
|
|
330
|
-
|
331
|
-
|
417
|
+
# Return a lambda that will make this call when given the input
|
418
|
+
# values, but then return the boolean inverse of that result.
|
419
|
+
def inverted_lambda
|
420
|
+
lambda do |target, value, &block|
|
421
|
+
target, block, method, *arguments = expand(target, value, block)
|
422
|
+
! target.send(method, *arguments, &block)
|
423
|
+
end
|
424
|
+
end
|
332
425
|
|
333
|
-
|
334
|
-
|
426
|
+
# Filters support:
|
427
|
+
#
|
428
|
+
# Symbols:: A method to call.
|
429
|
+
# Strings:: Some content to evaluate.
|
430
|
+
# Procs:: A proc to call with the object.
|
431
|
+
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
|
432
|
+
#
|
433
|
+
# All of these objects are converted into a CallTemplate and handled
|
434
|
+
# the same after this point.
|
435
|
+
def self.build(filter, callback)
|
436
|
+
case filter
|
437
|
+
when Symbol
|
438
|
+
new(nil, filter, [], nil)
|
439
|
+
when String
|
440
|
+
new(nil, :instance_exec, [:value], compile_lambda(filter))
|
441
|
+
when Conditionals::Value
|
442
|
+
new(filter, :call, [:target, :value], nil)
|
443
|
+
when ::Proc
|
444
|
+
if filter.arity > 1
|
445
|
+
new(nil, :instance_exec, [:target, :block], filter)
|
446
|
+
elsif filter.arity > 0
|
447
|
+
new(nil, :instance_exec, [:target], filter)
|
448
|
+
else
|
449
|
+
new(nil, :instance_exec, [], filter)
|
450
|
+
end
|
451
|
+
else
|
452
|
+
method_to_call = callback.current_scopes.join("_")
|
335
453
|
|
336
|
-
|
337
|
-
|
338
|
-
|
454
|
+
new(filter, method_to_call, [:target], nil)
|
455
|
+
end
|
456
|
+
end
|
339
457
|
|
340
|
-
|
341
|
-
|
342
|
-
when Symbol, String
|
343
|
-
matches?(other.kind, other.filter)
|
344
|
-
else
|
345
|
-
false
|
458
|
+
def self.compile_lambda(filter)
|
459
|
+
eval("lambda { |value| #{filter} }")
|
346
460
|
end
|
347
461
|
end
|
348
462
|
|
349
|
-
#
|
350
|
-
|
351
|
-
|
352
|
-
|
463
|
+
# Execute before and after filters in a sequence instead of
|
464
|
+
# chaining them with nested lambda calls, see:
|
465
|
+
# https://github.com/rails/rails/issues/18011
|
466
|
+
class CallbackSequence # :nodoc:
|
467
|
+
def initialize(nested = nil, call_template = nil, user_conditions = nil)
|
468
|
+
@nested = nested
|
469
|
+
@call_template = call_template
|
470
|
+
@user_conditions = user_conditions
|
353
471
|
|
354
|
-
|
355
|
-
|
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)
|
472
|
+
@before = []
|
473
|
+
@after = []
|
361
474
|
end
|
362
|
-
end
|
363
475
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
end
|
476
|
+
def before(&before)
|
477
|
+
@before.unshift(before)
|
478
|
+
self
|
479
|
+
end
|
369
480
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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("_")
|
481
|
+
def after(&after)
|
482
|
+
@after.push(after)
|
483
|
+
self
|
484
|
+
end
|
403
485
|
|
404
|
-
|
405
|
-
|
406
|
-
}
|
486
|
+
def around(call_template, user_conditions)
|
487
|
+
CallbackSequence.new(self, call_template, user_conditions)
|
407
488
|
end
|
408
|
-
end
|
409
489
|
|
410
|
-
|
411
|
-
|
412
|
-
when String, ::Proc
|
413
|
-
filter.object_id
|
414
|
-
else
|
415
|
-
filter
|
490
|
+
def skip?(arg)
|
491
|
+
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
|
416
492
|
end
|
417
|
-
end
|
418
493
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
end
|
423
|
-
end
|
494
|
+
def nested
|
495
|
+
@nested
|
496
|
+
end
|
424
497
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
class CallbackSequence
|
429
|
-
def initialize(&call)
|
430
|
-
@call = call
|
431
|
-
@before = []
|
432
|
-
@after = []
|
433
|
-
end
|
498
|
+
def final?
|
499
|
+
!@call_template
|
500
|
+
end
|
434
501
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
end
|
502
|
+
def expand_call_template(arg, block)
|
503
|
+
@call_template.expand(arg.target, arg.value, block)
|
504
|
+
end
|
439
505
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
end
|
506
|
+
def invoke_before(arg)
|
507
|
+
@before.each { |b| b.call(arg) }
|
508
|
+
end
|
444
509
|
|
445
|
-
|
446
|
-
|
447
|
-
around.call(arg) {
|
448
|
-
self.call(arg)
|
449
|
-
}
|
510
|
+
def invoke_after(arg)
|
511
|
+
@after.each { |a| a.call(arg) }
|
450
512
|
end
|
451
513
|
end
|
452
514
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
@after.each { |a| a.call(arg) }
|
457
|
-
value
|
458
|
-
end
|
459
|
-
end
|
515
|
+
# An Array with a compile method.
|
516
|
+
class CallbackChain #:nodoc:#
|
517
|
+
include Enumerable
|
460
518
|
|
461
|
-
|
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
|
519
|
+
attr_reader :name, :config
|
477
520
|
|
478
|
-
|
479
|
-
|
480
|
-
|
521
|
+
def initialize(name, config)
|
522
|
+
@name = name
|
523
|
+
@config = {
|
524
|
+
scope: [:kind],
|
525
|
+
terminator: default_terminator
|
526
|
+
}.merge!(config)
|
527
|
+
@chain = []
|
528
|
+
@callbacks = nil
|
529
|
+
@mutex = Mutex.new
|
530
|
+
end
|
481
531
|
|
482
|
-
|
483
|
-
@
|
484
|
-
@chain.
|
485
|
-
end
|
532
|
+
def each(&block); @chain.each(&block); end
|
533
|
+
def index(o); @chain.index(o); end
|
534
|
+
def empty?; @chain.empty?; end
|
486
535
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
536
|
+
def insert(index, o)
|
537
|
+
@callbacks = nil
|
538
|
+
@chain.insert(index, o)
|
539
|
+
end
|
491
540
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
end
|
541
|
+
def delete(o)
|
542
|
+
@callbacks = nil
|
543
|
+
@chain.delete(o)
|
544
|
+
end
|
497
545
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
546
|
+
def clear
|
547
|
+
@callbacks = nil
|
548
|
+
@chain.clear
|
549
|
+
self
|
550
|
+
end
|
551
|
+
|
552
|
+
def initialize_copy(other)
|
553
|
+
@callbacks = nil
|
554
|
+
@chain = other.chain.dup
|
555
|
+
@mutex = Mutex.new
|
556
|
+
end
|
503
557
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
558
|
+
def compile
|
559
|
+
@callbacks || @mutex.synchronize do
|
560
|
+
final_sequence = CallbackSequence.new
|
561
|
+
@callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
|
562
|
+
callback.apply callback_sequence
|
563
|
+
end
|
509
564
|
end
|
510
565
|
end
|
511
|
-
end
|
512
566
|
|
513
|
-
|
514
|
-
|
515
|
-
|
567
|
+
def append(*callbacks)
|
568
|
+
callbacks.each { |c| append_one(c) }
|
569
|
+
end
|
516
570
|
|
517
|
-
|
518
|
-
|
519
|
-
|
571
|
+
def prepend(*callbacks)
|
572
|
+
callbacks.each { |c| prepend_one(c) }
|
573
|
+
end
|
520
574
|
|
521
|
-
|
522
|
-
|
575
|
+
protected
|
576
|
+
def chain; @chain; end
|
523
577
|
|
524
|
-
|
578
|
+
private
|
525
579
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
def prepend_one(callback)
|
533
|
-
@callbacks = nil
|
534
|
-
remove_duplicates(callback)
|
535
|
-
@chain.unshift(callback)
|
536
|
-
end
|
580
|
+
def append_one(callback)
|
581
|
+
@callbacks = nil
|
582
|
+
remove_duplicates(callback)
|
583
|
+
@chain.push(callback)
|
584
|
+
end
|
537
585
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
586
|
+
def prepend_one(callback)
|
587
|
+
@callbacks = nil
|
588
|
+
remove_duplicates(callback)
|
589
|
+
@chain.unshift(callback)
|
590
|
+
end
|
542
591
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
catch(:abort) do
|
547
|
-
result_lambda.call if result_lambda.is_a?(Proc)
|
548
|
-
terminate = false
|
592
|
+
def remove_duplicates(callback)
|
593
|
+
@callbacks = nil
|
594
|
+
@chain.delete_if { |c| callback.duplicates?(c) }
|
549
595
|
end
|
550
|
-
terminate
|
551
|
-
end
|
552
|
-
end
|
553
|
-
end
|
554
596
|
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
597
|
+
def default_terminator
|
598
|
+
Proc.new do |target, result_lambda|
|
599
|
+
terminate = true
|
600
|
+
catch(:abort) do
|
601
|
+
result_lambda.call if result_lambda.is_a?(Proc)
|
602
|
+
terminate = false
|
603
|
+
end
|
604
|
+
terminate
|
605
|
+
end
|
606
|
+
end
|
561
607
|
end
|
562
608
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
609
|
+
module ClassMethods
|
610
|
+
def normalize_callback_params(filters, block) # :nodoc:
|
611
|
+
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
612
|
+
options = filters.extract_options!
|
613
|
+
filters.unshift(block) if block
|
614
|
+
[type, filters, options.dup]
|
569
615
|
end
|
570
|
-
end
|
571
616
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
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
|
617
|
+
# This is used internally to append, prepend and skip callbacks to the
|
618
|
+
# CallbackChain.
|
619
|
+
def __update_callbacks(name) #:nodoc:
|
620
|
+
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
|
621
|
+
chain = target.get_callbacks name
|
622
|
+
yield target, chain.dup
|
623
|
+
end
|
619
624
|
end
|
620
|
-
end
|
621
625
|
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
626
|
+
# Install a callback for the given event.
|
627
|
+
#
|
628
|
+
# set_callback :save, :before, :before_method
|
629
|
+
# set_callback :save, :after, :after_method, if: :condition
|
630
|
+
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
|
631
|
+
#
|
632
|
+
# The second argument indicates whether the callback is to be run +:before+,
|
633
|
+
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
|
634
|
+
# means the first example above can also be written as:
|
635
|
+
#
|
636
|
+
# set_callback :save, :before_method
|
637
|
+
#
|
638
|
+
# The callback can be specified as a symbol naming an instance method; as a
|
639
|
+
# proc, lambda, or block; or as an object that responds to a certain method
|
640
|
+
# determined by the <tt>:scope</tt> argument to +define_callbacks+.
|
641
|
+
#
|
642
|
+
# If a proc, lambda, or block is given, its body is evaluated in the context
|
643
|
+
# of the current object. It can also optionally accept the current object as
|
644
|
+
# an argument.
|
645
|
+
#
|
646
|
+
# Before and around callbacks are called in the order that they are set;
|
647
|
+
# after callbacks are called in the reverse order.
|
648
|
+
#
|
649
|
+
# Around callbacks can access the return value from the event, if it
|
650
|
+
# wasn't halted, from the +yield+ call.
|
651
|
+
#
|
652
|
+
# ===== Options
|
653
|
+
#
|
654
|
+
# * <tt>:if</tt> - A symbol, a string (deprecated) or an array of symbols,
|
655
|
+
# each naming an instance method or a proc; the callback will be called
|
656
|
+
# only when they all return a true value.
|
657
|
+
# * <tt>:unless</tt> - A symbol, a string (deprecated) or an array of symbols,
|
658
|
+
# each naming an instance method or a proc; the callback will be called
|
659
|
+
# only when they all return a false value.
|
660
|
+
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
|
661
|
+
# existing chain rather than appended.
|
662
|
+
def set_callback(name, *filter_list, &block)
|
663
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
664
|
+
|
665
|
+
if options[:if].is_a?(String) || options[:unless].is_a?(String)
|
666
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
667
|
+
Passing string to be evaluated in :if and :unless conditional
|
668
|
+
options is deprecated and will be removed in Rails 5.2 without
|
669
|
+
replacement. Pass a symbol for an instance method, or a lambda,
|
670
|
+
proc or block, instead.
|
671
|
+
MSG
|
672
|
+
end
|
643
673
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
674
|
+
self_chain = get_callbacks name
|
675
|
+
mapped = filters.map do |filter|
|
676
|
+
Callback.build(self_chain, filter, type, options)
|
677
|
+
end
|
648
678
|
|
649
|
-
|
679
|
+
__update_callbacks(name) do |target, chain|
|
680
|
+
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
|
681
|
+
target.set_callbacks name, chain
|
650
682
|
end
|
651
|
-
target.set_callbacks name, chain
|
652
683
|
end
|
653
|
-
end
|
654
684
|
|
655
|
-
|
656
|
-
|
657
|
-
|
685
|
+
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
|
686
|
+
# <tt>:unless</tt> options may be passed in order to control when the
|
687
|
+
# callback is skipped.
|
688
|
+
#
|
689
|
+
# class Writer < Person
|
690
|
+
# skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
|
691
|
+
# end
|
692
|
+
#
|
693
|
+
# An <tt>ArgumentError</tt> will be raised if the callback has not
|
694
|
+
# already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
|
695
|
+
def skip_callback(name, *filter_list, &block)
|
696
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
697
|
+
|
698
|
+
if options[:if].is_a?(String) || options[:unless].is_a?(String)
|
699
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
700
|
+
Passing string to :if and :unless conditional options is deprecated
|
701
|
+
and will be removed in Rails 5.2 without replacement.
|
702
|
+
MSG
|
703
|
+
end
|
658
704
|
|
659
|
-
|
660
|
-
chain = target.get_callbacks(name).dup
|
661
|
-
callbacks.each { |c| chain.delete(c) }
|
662
|
-
target.set_callbacks name, chain
|
663
|
-
end
|
705
|
+
options[:raise] = true unless options.key?(:raise)
|
664
706
|
|
665
|
-
|
666
|
-
|
707
|
+
__update_callbacks(name) do |target, chain|
|
708
|
+
filters.each do |filter|
|
709
|
+
callback = chain.find { |c| c.matches?(type, filter) }
|
710
|
+
|
711
|
+
if !callback && options[:raise]
|
712
|
+
raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
|
713
|
+
end
|
714
|
+
|
715
|
+
if callback && (options.key?(:if) || options.key?(:unless))
|
716
|
+
new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
|
717
|
+
chain.insert(chain.index(callback), new_callback)
|
718
|
+
end
|
667
719
|
|
668
|
-
|
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)
|
720
|
+
chain.delete(callback)
|
751
721
|
end
|
752
|
-
|
722
|
+
target.set_callbacks name, chain
|
723
|
+
end
|
753
724
|
end
|
754
|
-
end
|
755
725
|
|
756
|
-
|
726
|
+
# Remove all set callbacks for the given event.
|
727
|
+
def reset_callbacks(name)
|
728
|
+
callbacks = get_callbacks name
|
757
729
|
|
758
|
-
|
759
|
-
|
760
|
-
|
730
|
+
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
731
|
+
chain = target.get_callbacks(name).dup
|
732
|
+
callbacks.each { |c| chain.delete(c) }
|
733
|
+
target.set_callbacks name, chain
|
734
|
+
end
|
761
735
|
|
762
|
-
|
763
|
-
|
764
|
-
end
|
736
|
+
set_callbacks(name, callbacks.dup.clear)
|
737
|
+
end
|
765
738
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
739
|
+
# Define sets of events in the object life cycle that support callbacks.
|
740
|
+
#
|
741
|
+
# define_callbacks :validate
|
742
|
+
# define_callbacks :initialize, :save, :destroy
|
743
|
+
#
|
744
|
+
# ===== Options
|
745
|
+
#
|
746
|
+
# * <tt>:terminator</tt> - Determines when a before filter will halt the
|
747
|
+
# callback chain, preventing following before and around callbacks from
|
748
|
+
# being called and the event from being triggered.
|
749
|
+
# This should be a lambda to be executed.
|
750
|
+
# The current object and the result lambda of the callback will be provided
|
751
|
+
# to the terminator lambda.
|
752
|
+
#
|
753
|
+
# define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
|
754
|
+
#
|
755
|
+
# In this example, if any before validate callbacks returns +false+,
|
756
|
+
# any successive before and around callback is not executed.
|
757
|
+
#
|
758
|
+
# The default terminator halts the chain when a callback throws +:abort+.
|
759
|
+
#
|
760
|
+
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
|
761
|
+
# callbacks should be terminated by the <tt>:terminator</tt> option. By
|
762
|
+
# default after callbacks are executed no matter if callback chain was
|
763
|
+
# terminated or not. This option makes sense only when <tt>:terminator</tt>
|
764
|
+
# option is specified.
|
765
|
+
#
|
766
|
+
# * <tt>:scope</tt> - Indicates which methods should be executed when an
|
767
|
+
# object is used as a callback.
|
768
|
+
#
|
769
|
+
# class Audit
|
770
|
+
# def before(caller)
|
771
|
+
# puts 'Audit: before is called'
|
772
|
+
# end
|
773
|
+
#
|
774
|
+
# def before_save(caller)
|
775
|
+
# puts 'Audit: before_save is called'
|
776
|
+
# end
|
777
|
+
# end
|
778
|
+
#
|
779
|
+
# class Account
|
780
|
+
# include ActiveSupport::Callbacks
|
781
|
+
#
|
782
|
+
# define_callbacks :save
|
783
|
+
# set_callback :save, :before, Audit.new
|
784
|
+
#
|
785
|
+
# def save
|
786
|
+
# run_callbacks :save do
|
787
|
+
# puts 'save in main'
|
788
|
+
# end
|
789
|
+
# end
|
790
|
+
# end
|
791
|
+
#
|
792
|
+
# In the above case whenever you save an account the method
|
793
|
+
# <tt>Audit#before</tt> will be called. On the other hand
|
794
|
+
#
|
795
|
+
# define_callbacks :save, scope: [:kind, :name]
|
796
|
+
#
|
797
|
+
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
|
798
|
+
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
|
799
|
+
# case "kind" is "before" and "name" is "save". In this context +:kind+
|
800
|
+
# and +:name+ have special meanings: +:kind+ refers to the kind of
|
801
|
+
# callback (before/after/around) and +:name+ refers to the method on
|
802
|
+
# which callbacks are being defined.
|
803
|
+
#
|
804
|
+
# A declaration like
|
805
|
+
#
|
806
|
+
# define_callbacks :save, scope: [:name]
|
807
|
+
#
|
808
|
+
# would call <tt>Audit#save</tt>.
|
809
|
+
#
|
810
|
+
# ===== Notes
|
811
|
+
#
|
812
|
+
# +names+ passed to +define_callbacks+ must not end with
|
813
|
+
# <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
|
814
|
+
#
|
815
|
+
# Calling +define_callbacks+ multiple times with the same +names+ will
|
816
|
+
# overwrite previous callbacks registered with +set_callback+.
|
817
|
+
def define_callbacks(*names)
|
818
|
+
options = names.extract_options!
|
819
|
+
|
820
|
+
names.each do |name|
|
821
|
+
name = name.to_sym
|
822
|
+
|
823
|
+
set_callbacks name, CallbackChain.new(name, options)
|
824
|
+
|
825
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
826
|
+
def _run_#{name}_callbacks(&block)
|
827
|
+
run_callbacks #{name.inspect}, &block
|
828
|
+
end
|
829
|
+
|
830
|
+
def self._#{name}_callbacks
|
831
|
+
get_callbacks(#{name.inspect})
|
832
|
+
end
|
833
|
+
|
834
|
+
def self._#{name}_callbacks=(value)
|
835
|
+
set_callbacks(#{name.inspect}, value)
|
836
|
+
end
|
837
|
+
|
838
|
+
def _#{name}_callbacks
|
839
|
+
__callbacks[#{name.inspect}]
|
840
|
+
end
|
841
|
+
RUBY
|
776
842
|
end
|
777
|
-
terminate
|
778
843
|
end
|
779
|
-
end
|
780
844
|
|
781
|
-
|
845
|
+
protected
|
846
|
+
|
847
|
+
def get_callbacks(name) # :nodoc:
|
848
|
+
__callbacks[name.to_sym]
|
849
|
+
end
|
782
850
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
To explicitly halt the callback chain, please use `throw :abort` instead.
|
787
|
-
MSG
|
851
|
+
def set_callbacks(name, callbacks) # :nodoc:
|
852
|
+
self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
|
853
|
+
end
|
788
854
|
end
|
789
|
-
end
|
790
855
|
end
|
791
856
|
end
|