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