activesupport 4.0.13 → 4.2.11.3
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 +406 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -2
- data/lib/active_support/backtrace_cleaner.rb +8 -8
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache/file_store.rb +32 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -7
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +11 -30
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/cache.rb +75 -41
- data/lib/active_support/callbacks.rb +482 -261
- data/lib/active_support/concern.rb +23 -7
- data/lib/active_support/configurable.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +11 -1
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +29 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
- data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
- data/lib/active_support/core_ext/class/subclasses.rb +0 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +9 -1
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +17 -1
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/compact.rb +24 -0
- data/lib/active_support/core_ext/hash/conversions.rb +9 -8
- data/lib/active_support/core_ext/hash/except.rb +8 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +25 -19
- data/lib/active_support/core_ext/hash/slice.rb +8 -2
- data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
- data/lib/active_support/core_ext/hash.rb +2 -1
- data/lib/active_support/core_ext/integer/time.rb +0 -15
- data/lib/active_support/core_ext/kernel/concern.rb +10 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
- data/lib/active_support/core_ext/kernel.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +4 -1
- data/lib/active_support/core_ext/marshal.rb +8 -5
- data/lib/active_support/core_ext/module/aliasing.rb +2 -2
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +53 -25
- data/lib/active_support/core_ext/module/deprecation.rb +0 -2
- data/lib/active_support/core_ext/module/introspection.rb +0 -16
- data/lib/active_support/core_ext/module/method_transplanting.rb +13 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
- data/lib/active_support/core_ext/numeric/time.rb +4 -29
- data/lib/active_support/core_ext/object/blank.rb +44 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/duplicable.rb +72 -33
- data/lib/active_support/core_ext/object/inclusion.rb +16 -15
- data/lib/active_support/core_ext/object/itself.rb +15 -0
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_query.rb +14 -6
- data/lib/active_support/core_ext/object/try.rb +36 -14
- data/lib/active_support/core_ext/object/with_options.rb +30 -3
- data/lib/active_support/core_ext/object.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +35 -35
- data/lib/active_support/core_ext/string/conversions.rb +10 -9
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +51 -3
- data/lib/active_support/core_ext/string/inflections.rb +15 -10
- data/lib/active_support/core_ext/string/output_safety.rb +97 -33
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +12 -5
- data/lib/active_support/core_ext/time/calculations.rb +47 -68
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/zones.rb +2 -20
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/dependencies/autoload.rb +1 -1
- data/lib/active_support/dependencies.rb +64 -25
- data/lib/active_support/deprecation/behaviors.rb +4 -4
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/duration.rb +55 -11
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/hash_with_indifferent_access.rb +39 -11
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +1 -7
- data/lib/active_support/inflections.rb +6 -1
- data/lib/active_support/inflector/inflections.rb +19 -19
- data/lib/active_support/inflector/methods.rb +66 -25
- data/lib/active_support/json/decoding.rb +15 -22
- data/lib/active_support/json/encoding.rb +125 -286
- data/lib/active_support/key_generator.rb +8 -10
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber/test_helper.rb +1 -1
- data/lib/active_support/logger.rb +51 -1
- data/lib/active_support/logger_silence.rb +7 -4
- data/lib/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/active_support/message_encryptor.rb +14 -6
- data/lib/active_support/message_verifier.rb +16 -12
- data/lib/active_support/multibyte/chars.rb +2 -3
- data/lib/active_support/multibyte/unicode.rb +46 -58
- data/lib/active_support/notifications/fanout.rb +12 -7
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/notifications.rb +11 -6
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +23 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +87 -0
- data/lib/active_support/number_helper.rb +32 -324
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +13 -10
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/subscriber.rb +35 -3
- data/lib/active_support/test_case.rb +52 -19
- data/lib/active_support/testing/assertions.rb +1 -31
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/constant_lookup.rb +1 -5
- data/lib/active_support/testing/declarative.rb +7 -21
- data/lib/active_support/testing/isolation.rb +29 -69
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -2
- data/lib/active_support/testing/time_helpers.rb +134 -0
- data/lib/active_support/time.rb +0 -2
- data/lib/active_support/time_with_zone.rb +60 -40
- data/lib/active_support/values/time_zone.rb +101 -101
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -7
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/libxml.rb +1 -3
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
- data/lib/active_support/xml_mini/nokogiri.rb +1 -3
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
- data/lib/active_support/xml_mini/rexml.rb +7 -8
- data/lib/active_support/xml_mini.rb +33 -15
- data/lib/active_support.rb +27 -2
- metadata +43 -43
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
@@ -1,9 +1,10 @@
|
|
1
|
-
require 'thread_safe'
|
2
1
|
require 'active_support/concern'
|
3
2
|
require 'active_support/descendants_tracker'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
4
|
require 'active_support/core_ext/class/attribute'
|
5
5
|
require 'active_support/core_ext/kernel/reporting'
|
6
6
|
require 'active_support/core_ext/kernel/singleton_class'
|
7
|
+
require 'thread'
|
7
8
|
|
8
9
|
module ActiveSupport
|
9
10
|
# Callbacks are code hooks that are run at key points in an object's life cycle.
|
@@ -70,266 +71,492 @@ module ActiveSupport
|
|
70
71
|
# order.
|
71
72
|
#
|
72
73
|
# If the callback chain was halted, returns +false+. Otherwise returns the
|
73
|
-
# result of the block,
|
74
|
+
# result of the block, +nil+ if no callbacks have been set, or +true+
|
75
|
+
# if callbacks have been set but no block is given.
|
74
76
|
#
|
75
77
|
# run_callbacks :save do
|
76
78
|
# save
|
77
79
|
# end
|
78
80
|
def run_callbacks(kind, &block)
|
79
|
-
|
80
|
-
send(runner_name, &block)
|
81
|
+
send "_run_#{kind}_callbacks", &block
|
81
82
|
end
|
82
83
|
|
83
84
|
private
|
84
85
|
|
85
|
-
|
86
|
+
def __run_callbacks__(callbacks, &block)
|
87
|
+
if callbacks.empty?
|
88
|
+
yield if block_given?
|
89
|
+
else
|
90
|
+
runner = callbacks.compile
|
91
|
+
e = Filters::Environment.new(self, false, nil, block)
|
92
|
+
runner.call(e).value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# A hook invoked every time a before callback is halted.
|
86
97
|
# This can be overridden in AS::Callback implementors in order
|
87
98
|
# to provide better debugging/logging.
|
88
99
|
def halted_callback_hook(filter)
|
89
100
|
end
|
90
101
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
102
|
+
module Conditionals # :nodoc:
|
103
|
+
class Value
|
104
|
+
def initialize(&block)
|
105
|
+
@block = block
|
106
|
+
end
|
107
|
+
def call(target, value); @block.call(value); end
|
108
|
+
end
|
109
|
+
end
|
95
110
|
|
96
|
-
|
97
|
-
|
98
|
-
deprecate_per_key_option(options)
|
99
|
-
normalize_options!(options)
|
111
|
+
module Filters
|
112
|
+
Environment = Struct.new(:target, :halted, :value, :run_block)
|
100
113
|
|
101
|
-
|
102
|
-
|
103
|
-
|
114
|
+
class End
|
115
|
+
def call(env)
|
116
|
+
block = env.run_block
|
117
|
+
env.value = !env.halted && (!block || block.call)
|
118
|
+
env
|
119
|
+
end
|
104
120
|
end
|
121
|
+
ENDING = End.new
|
105
122
|
|
106
|
-
|
107
|
-
|
108
|
-
|
123
|
+
class Before
|
124
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
|
125
|
+
halted_lambda = chain_config[:terminator]
|
126
|
+
|
127
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
128
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
129
|
+
elsif chain_config.key? :terminator
|
130
|
+
halting(callback_sequence, user_callback, halted_lambda, filter)
|
131
|
+
elsif user_conditions.any?
|
132
|
+
conditional(callback_sequence, user_callback, user_conditions)
|
133
|
+
else
|
134
|
+
simple callback_sequence, user_callback
|
135
|
+
end
|
109
136
|
end
|
110
|
-
end
|
111
137
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
138
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
|
139
|
+
callback_sequence.before do |env|
|
140
|
+
target = env.target
|
141
|
+
value = env.value
|
142
|
+
halted = env.halted
|
143
|
+
|
144
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
145
|
+
result = user_callback.call target, value
|
146
|
+
env.halted = halted_lambda.call(target, result)
|
147
|
+
if env.halted
|
148
|
+
target.send :halted_callback_hook, filter
|
149
|
+
end
|
150
|
+
end
|
121
151
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
152
|
+
env
|
153
|
+
end
|
154
|
+
end
|
155
|
+
private_class_method :halting_and_conditional
|
156
|
+
|
157
|
+
def self.halting(callback_sequence, user_callback, halted_lambda, filter)
|
158
|
+
callback_sequence.before do |env|
|
159
|
+
target = env.target
|
160
|
+
value = env.value
|
161
|
+
halted = env.halted
|
162
|
+
|
163
|
+
unless halted
|
164
|
+
result = user_callback.call target, value
|
165
|
+
env.halted = halted_lambda.call(target, result)
|
166
|
+
if env.halted
|
167
|
+
target.send :halted_callback_hook, filter
|
168
|
+
end
|
169
|
+
end
|
126
170
|
|
127
|
-
|
128
|
-
|
129
|
-
|
171
|
+
env
|
172
|
+
end
|
173
|
+
end
|
174
|
+
private_class_method :halting
|
175
|
+
|
176
|
+
def self.conditional(callback_sequence, user_callback, user_conditions)
|
177
|
+
callback_sequence.before do |env|
|
178
|
+
target = env.target
|
179
|
+
value = env.value
|
130
180
|
|
131
|
-
|
132
|
-
|
181
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
182
|
+
user_callback.call target, value
|
183
|
+
end
|
184
|
+
|
185
|
+
env
|
186
|
+
end
|
187
|
+
end
|
188
|
+
private_class_method :conditional
|
189
|
+
|
190
|
+
def self.simple(callback_sequence, user_callback)
|
191
|
+
callback_sequence.before do |env|
|
192
|
+
user_callback.call env.target, env.value
|
193
|
+
|
194
|
+
env
|
195
|
+
end
|
196
|
+
end
|
197
|
+
private_class_method :simple
|
198
|
+
end
|
199
|
+
|
200
|
+
class After
|
201
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
202
|
+
if chain_config[:skip_after_callbacks_if_terminated]
|
203
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
204
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
205
|
+
elsif chain_config.key?(:terminator)
|
206
|
+
halting(callback_sequence, user_callback)
|
207
|
+
elsif user_conditions.any?
|
208
|
+
conditional callback_sequence, user_callback, user_conditions
|
209
|
+
else
|
210
|
+
simple callback_sequence, user_callback
|
211
|
+
end
|
212
|
+
else
|
213
|
+
if user_conditions.any?
|
214
|
+
conditional callback_sequence, user_callback, user_conditions
|
215
|
+
else
|
216
|
+
simple callback_sequence, user_callback
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
222
|
+
callback_sequence.after do |env|
|
223
|
+
target = env.target
|
224
|
+
value = env.value
|
225
|
+
halted = env.halted
|
226
|
+
|
227
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
228
|
+
user_callback.call target, value
|
229
|
+
end
|
230
|
+
|
231
|
+
env
|
232
|
+
end
|
233
|
+
end
|
234
|
+
private_class_method :halting_and_conditional
|
235
|
+
|
236
|
+
def self.halting(callback_sequence, user_callback)
|
237
|
+
callback_sequence.after do |env|
|
238
|
+
unless env.halted
|
239
|
+
user_callback.call env.target, env.value
|
240
|
+
end
|
241
|
+
|
242
|
+
env
|
243
|
+
end
|
244
|
+
end
|
245
|
+
private_class_method :halting
|
246
|
+
|
247
|
+
def self.conditional(callback_sequence, user_callback, user_conditions)
|
248
|
+
callback_sequence.after do |env|
|
249
|
+
target = env.target
|
250
|
+
value = env.value
|
251
|
+
|
252
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
253
|
+
user_callback.call target, value
|
254
|
+
end
|
255
|
+
|
256
|
+
env
|
257
|
+
end
|
258
|
+
end
|
259
|
+
private_class_method :conditional
|
260
|
+
|
261
|
+
def self.simple(callback_sequence, user_callback)
|
262
|
+
callback_sequence.after do |env|
|
263
|
+
user_callback.call env.target, env.value
|
264
|
+
|
265
|
+
env
|
266
|
+
end
|
267
|
+
end
|
268
|
+
private_class_method :simple
|
269
|
+
end
|
270
|
+
|
271
|
+
class Around
|
272
|
+
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
|
273
|
+
if chain_config.key?(:terminator) && user_conditions.any?
|
274
|
+
halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
275
|
+
elsif chain_config.key? :terminator
|
276
|
+
halting(callback_sequence, user_callback)
|
277
|
+
elsif user_conditions.any?
|
278
|
+
conditional(callback_sequence, user_callback, user_conditions)
|
279
|
+
else
|
280
|
+
simple(callback_sequence, user_callback)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
|
285
|
+
callback_sequence.around do |env, &run|
|
286
|
+
target = env.target
|
287
|
+
value = env.value
|
288
|
+
halted = env.halted
|
289
|
+
|
290
|
+
if !halted && user_conditions.all? { |c| c.call(target, value) }
|
291
|
+
user_callback.call(target, value) {
|
292
|
+
env = run.call env
|
293
|
+
env.value
|
294
|
+
}
|
295
|
+
|
296
|
+
env
|
297
|
+
else
|
298
|
+
run.call env
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
private_class_method :halting_and_conditional
|
303
|
+
|
304
|
+
def self.halting(callback_sequence, user_callback)
|
305
|
+
callback_sequence.around do |env, &run|
|
306
|
+
target = env.target
|
307
|
+
value = env.value
|
308
|
+
|
309
|
+
if env.halted
|
310
|
+
run.call env
|
311
|
+
else
|
312
|
+
user_callback.call(target, value) {
|
313
|
+
env = run.call env
|
314
|
+
env.value
|
315
|
+
}
|
316
|
+
env
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
private_class_method :halting
|
321
|
+
|
322
|
+
def self.conditional(callback_sequence, user_callback, user_conditions)
|
323
|
+
callback_sequence.around do |env, &run|
|
324
|
+
target = env.target
|
325
|
+
value = env.value
|
326
|
+
|
327
|
+
if user_conditions.all? { |c| c.call(target, value) }
|
328
|
+
user_callback.call(target, value) {
|
329
|
+
env = run.call env
|
330
|
+
env.value
|
331
|
+
}
|
332
|
+
env
|
333
|
+
else
|
334
|
+
run.call env
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
private_class_method :conditional
|
339
|
+
|
340
|
+
def self.simple(callback_sequence, user_callback)
|
341
|
+
callback_sequence.around do |env, &run|
|
342
|
+
user_callback.call(env.target, env.value) {
|
343
|
+
env = run.call env
|
344
|
+
env.value
|
345
|
+
}
|
346
|
+
env
|
347
|
+
end
|
348
|
+
end
|
349
|
+
private_class_method :simple
|
133
350
|
end
|
351
|
+
end
|
134
352
|
|
135
|
-
|
136
|
-
|
353
|
+
class Callback #:nodoc:#
|
354
|
+
def self.build(chain, filter, kind, options)
|
355
|
+
new chain.name, filter, kind, options, chain.config
|
137
356
|
end
|
138
357
|
|
139
|
-
|
140
|
-
|
358
|
+
attr_accessor :kind, :name
|
359
|
+
attr_reader :chain_config
|
360
|
+
|
361
|
+
def initialize(name, filter, kind, options, chain_config)
|
362
|
+
@chain_config = chain_config
|
363
|
+
@name = name
|
364
|
+
@kind = kind
|
365
|
+
@filter = filter
|
366
|
+
@key = compute_identifier filter
|
367
|
+
@if = Array(options[:if])
|
368
|
+
@unless = Array(options[:unless])
|
141
369
|
end
|
142
370
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
371
|
+
def filter; @key; end
|
372
|
+
def raw_filter; @filter; end
|
373
|
+
|
374
|
+
def merge(chain, new_options)
|
375
|
+
options = {
|
376
|
+
:if => @if.dup,
|
377
|
+
:unless => @unless.dup
|
378
|
+
}
|
379
|
+
|
380
|
+
options[:if].concat Array(new_options.fetch(:unless, []))
|
381
|
+
options[:unless].concat Array(new_options.fetch(:if, []))
|
382
|
+
|
383
|
+
self.class.build chain, @filter, @kind, options
|
146
384
|
end
|
147
385
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
386
|
+
def matches?(_kind, _filter)
|
387
|
+
@kind == _kind && filter == _filter
|
388
|
+
end
|
151
389
|
|
152
|
-
|
390
|
+
def duplicates?(other)
|
391
|
+
case @filter
|
392
|
+
when Symbol, String
|
393
|
+
matches?(other.kind, other.filter)
|
394
|
+
else
|
395
|
+
false
|
396
|
+
end
|
153
397
|
end
|
154
398
|
|
155
399
|
# Wraps code with filter
|
156
|
-
def apply(
|
157
|
-
|
400
|
+
def apply(callback_sequence)
|
401
|
+
user_conditions = conditions_lambdas
|
402
|
+
user_callback = make_lambda @filter
|
403
|
+
|
404
|
+
case kind
|
158
405
|
when :before
|
159
|
-
|
160
|
-
if !halted && #{@compiled_options}
|
161
|
-
# This double assignment is to prevent warnings in 1.9.3 as
|
162
|
-
# the `result` variable is not always used except if the
|
163
|
-
# terminator code refers to it.
|
164
|
-
result = result = #{@filter}
|
165
|
-
halted = (#{chain.config[:terminator]})
|
166
|
-
if halted
|
167
|
-
halted_callback_hook(#{@raw_filter.inspect.inspect})
|
168
|
-
end
|
169
|
-
end
|
170
|
-
#{code}
|
171
|
-
RUBY_EVAL
|
406
|
+
Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
|
172
407
|
when :after
|
173
|
-
|
174
|
-
#{code}
|
175
|
-
if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
|
176
|
-
#{@filter}
|
177
|
-
end
|
178
|
-
RUBY_EVAL
|
408
|
+
Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
|
179
409
|
when :around
|
180
|
-
|
181
|
-
<<-RUBY_EVAL
|
182
|
-
#{name}(halted) do
|
183
|
-
#{code}
|
184
|
-
value
|
185
|
-
end
|
186
|
-
RUBY_EVAL
|
410
|
+
Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
|
187
411
|
end
|
188
412
|
end
|
189
413
|
|
190
414
|
private
|
191
415
|
|
192
|
-
|
193
|
-
|
194
|
-
#
|
195
|
-
# For `set_callback :save, :around, :filter_name, if: :condition':
|
196
|
-
#
|
197
|
-
# def _conditional_callback_save_17
|
198
|
-
# if condition
|
199
|
-
# filter_name do
|
200
|
-
# yield self
|
201
|
-
# end
|
202
|
-
# else
|
203
|
-
# yield self
|
204
|
-
# end
|
205
|
-
# end
|
206
|
-
def define_conditional_callback
|
207
|
-
name = "_conditional_callback_#{@kind}_#{next_id}"
|
208
|
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
209
|
-
def #{name}(halted)
|
210
|
-
if #{@compiled_options} && !halted
|
211
|
-
#{@filter} do
|
212
|
-
yield self
|
213
|
-
end
|
214
|
-
else
|
215
|
-
yield self
|
216
|
-
end
|
217
|
-
end
|
218
|
-
RUBY_EVAL
|
219
|
-
name
|
220
|
-
end
|
221
|
-
|
222
|
-
# Options support the same options as filters themselves (and support
|
223
|
-
# symbols, string, procs, and objects), so compile a conditional
|
224
|
-
# expression based on the options.
|
225
|
-
def recompile_options!
|
226
|
-
conditions = ["true"]
|
227
|
-
|
228
|
-
unless options[:if].empty?
|
229
|
-
conditions << Array(_compile_filter(options[:if]))
|
230
|
-
end
|
231
|
-
|
232
|
-
unless options[:unless].empty?
|
233
|
-
conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
|
234
|
-
end
|
235
|
-
|
236
|
-
@compiled_options = conditions.flatten.join(" && ")
|
416
|
+
def invert_lambda(l)
|
417
|
+
lambda { |*args, &blk| !l.call(*args, &blk) }
|
237
418
|
end
|
238
419
|
|
239
420
|
# Filters support:
|
240
421
|
#
|
241
|
-
# Arrays:: Used in conditions. This is used to specify
|
242
|
-
# multiple conditions. Used internally to
|
243
|
-
# merge conditions from skip_* filters.
|
244
422
|
# Symbols:: A method to call.
|
245
423
|
# Strings:: Some content to evaluate.
|
246
424
|
# Procs:: A proc to call with the object.
|
247
425
|
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
|
248
426
|
#
|
249
|
-
# All of these objects are
|
250
|
-
# the same after this point
|
251
|
-
|
252
|
-
# Arrays:: Merged together into a single filter.
|
253
|
-
# Symbols:: Already methods.
|
254
|
-
# Strings:: class_eval'ed into methods.
|
255
|
-
# Procs:: define_method'ed into methods.
|
256
|
-
# Objects::
|
257
|
-
# a method is created that calls the before_foo method
|
258
|
-
# on the object.
|
259
|
-
def _compile_filter(filter)
|
427
|
+
# All of these objects are converted into a lambda and handled
|
428
|
+
# the same after this point.
|
429
|
+
def make_lambda(filter)
|
260
430
|
case filter
|
261
|
-
when Array
|
262
|
-
filter.map {|f| _compile_filter(f)}
|
263
431
|
when Symbol
|
264
|
-
filter
|
432
|
+
lambda { |target, _, &blk| target.send filter, &blk }
|
265
433
|
when String
|
266
|
-
"
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
434
|
+
l = eval "lambda { |value| #{filter} }"
|
435
|
+
lambda { |target, value| target.instance_exec(value, &l) }
|
436
|
+
when Conditionals::Value then filter
|
437
|
+
when ::Proc
|
438
|
+
if filter.arity > 1
|
439
|
+
return lambda { |target, _, &block|
|
440
|
+
raise ArgumentError unless block
|
441
|
+
target.instance_exec(target, block, &filter)
|
442
|
+
}
|
443
|
+
end
|
271
444
|
|
272
|
-
|
445
|
+
if filter.arity <= 0
|
446
|
+
lambda { |target, _| target.instance_exec(&filter) }
|
447
|
+
else
|
448
|
+
lambda { |target, _| target.instance_exec(target, &filter) }
|
449
|
+
end
|
273
450
|
else
|
274
|
-
|
275
|
-
|
451
|
+
scopes = Array(chain_config[:scope])
|
452
|
+
method_to_call = scopes.map{ |s| public_send(s) }.join("_")
|
276
453
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
def #{method_name}(&blk)
|
283
|
-
#{method_name}_object.send(:#{method_to_call}, self, &blk)
|
284
|
-
end
|
285
|
-
RUBY_EVAL
|
454
|
+
lambda { |target, _, &blk|
|
455
|
+
filter.public_send method_to_call, target, &blk
|
456
|
+
}
|
457
|
+
end
|
458
|
+
end
|
286
459
|
|
287
|
-
|
460
|
+
def compute_identifier(filter)
|
461
|
+
case filter
|
462
|
+
when String, ::Proc
|
463
|
+
filter.object_id
|
464
|
+
else
|
465
|
+
filter
|
288
466
|
end
|
289
467
|
end
|
290
468
|
|
291
|
-
def
|
292
|
-
if
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
469
|
+
def conditions_lambdas
|
470
|
+
@if.map { |c| make_lambda c } +
|
471
|
+
@unless.map { |c| invert_lambda make_lambda c }
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
# Execute before and after filters in a sequence instead of
|
476
|
+
# chaining them with nested lambda calls, see:
|
477
|
+
# https://github.com/rails/rails/issues/18011
|
478
|
+
class CallbackSequence
|
479
|
+
def initialize(&call)
|
480
|
+
@call = call
|
481
|
+
@before = []
|
482
|
+
@after = []
|
483
|
+
end
|
484
|
+
|
485
|
+
def before(&before)
|
486
|
+
@before.unshift(before)
|
487
|
+
self
|
488
|
+
end
|
489
|
+
|
490
|
+
def after(&after)
|
491
|
+
@after.push(after)
|
492
|
+
self
|
493
|
+
end
|
494
|
+
|
495
|
+
def around(&around)
|
496
|
+
CallbackSequence.new do |*args|
|
497
|
+
around.call(*args) {
|
498
|
+
self.call(*args)
|
499
|
+
}
|
307
500
|
end
|
308
501
|
end
|
502
|
+
|
503
|
+
def call(*args)
|
504
|
+
@before.each { |b| b.call(*args) }
|
505
|
+
value = @call.call(*args)
|
506
|
+
@after.each { |a| a.call(*args) }
|
507
|
+
value
|
508
|
+
end
|
309
509
|
end
|
310
510
|
|
311
511
|
# An Array with a compile method.
|
312
|
-
class CallbackChain
|
512
|
+
class CallbackChain #:nodoc:#
|
513
|
+
include Enumerable
|
514
|
+
|
313
515
|
attr_reader :name, :config
|
314
516
|
|
315
517
|
def initialize(name, config)
|
316
518
|
@name = name
|
317
519
|
@config = {
|
318
|
-
:terminator => "false",
|
319
520
|
:scope => [ :kind ]
|
320
521
|
}.merge!(config)
|
522
|
+
@chain = []
|
523
|
+
@callbacks = nil
|
524
|
+
@mutex = Mutex.new
|
525
|
+
end
|
526
|
+
|
527
|
+
def each(&block); @chain.each(&block); end
|
528
|
+
def index(o); @chain.index(o); end
|
529
|
+
def empty?; @chain.empty?; end
|
530
|
+
|
531
|
+
def insert(index, o)
|
532
|
+
@callbacks = nil
|
533
|
+
@chain.insert(index, o)
|
534
|
+
end
|
535
|
+
|
536
|
+
def delete(o)
|
537
|
+
@callbacks = nil
|
538
|
+
@chain.delete(o)
|
539
|
+
end
|
540
|
+
|
541
|
+
def clear
|
542
|
+
@callbacks = nil
|
543
|
+
@chain.clear
|
544
|
+
self
|
545
|
+
end
|
546
|
+
|
547
|
+
def initialize_copy(other)
|
548
|
+
@callbacks = nil
|
549
|
+
@chain = other.chain.dup
|
550
|
+
@mutex = Mutex.new
|
321
551
|
end
|
322
552
|
|
323
553
|
def compile
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
554
|
+
@callbacks || @mutex.synchronize do
|
555
|
+
final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
|
556
|
+
@callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
|
557
|
+
callback.apply callback_sequence
|
558
|
+
end
|
328
559
|
end
|
329
|
-
method << callbacks
|
330
|
-
|
331
|
-
method << "value"
|
332
|
-
method.join("\n")
|
333
560
|
end
|
334
561
|
|
335
562
|
def append(*callbacks)
|
@@ -340,69 +567,43 @@ module ActiveSupport
|
|
340
567
|
callbacks.each { |c| prepend_one(c) }
|
341
568
|
end
|
342
569
|
|
570
|
+
protected
|
571
|
+
def chain; @chain; end
|
572
|
+
|
343
573
|
private
|
344
574
|
|
345
575
|
def append_one(callback)
|
576
|
+
@callbacks = nil
|
346
577
|
remove_duplicates(callback)
|
347
|
-
push(callback)
|
578
|
+
@chain.push(callback)
|
348
579
|
end
|
349
580
|
|
350
581
|
def prepend_one(callback)
|
582
|
+
@callbacks = nil
|
351
583
|
remove_duplicates(callback)
|
352
|
-
unshift(callback)
|
584
|
+
@chain.unshift(callback)
|
353
585
|
end
|
354
586
|
|
355
587
|
def remove_duplicates(callback)
|
356
|
-
|
588
|
+
@callbacks = nil
|
589
|
+
@chain.delete_if { |c| callback.duplicates?(c) }
|
357
590
|
end
|
358
|
-
|
359
591
|
end
|
360
592
|
|
361
593
|
module ClassMethods
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
name = __callback_runner_name(kind)
|
368
|
-
unless object.respond_to?(name, true)
|
369
|
-
str = object.send("_#{kind}_callbacks").compile
|
370
|
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
371
|
-
def #{name}() #{str} end
|
372
|
-
protected :#{name}
|
373
|
-
RUBY_EVAL
|
374
|
-
end
|
375
|
-
name
|
376
|
-
end
|
377
|
-
|
378
|
-
def __reset_runner(symbol)
|
379
|
-
name = __callback_runner_name(symbol)
|
380
|
-
undef_method(name) if method_defined?(name)
|
381
|
-
end
|
382
|
-
|
383
|
-
def __callback_runner_name_cache
|
384
|
-
@__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) }
|
385
|
-
end
|
386
|
-
|
387
|
-
def __generate_callback_runner_name(kind)
|
388
|
-
"_run__#{self.name.hash.abs}__#{kind}__callbacks"
|
389
|
-
end
|
390
|
-
|
391
|
-
def __callback_runner_name(kind)
|
392
|
-
__callback_runner_name_cache[kind]
|
594
|
+
def normalize_callback_params(filters, block) # :nodoc:
|
595
|
+
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
|
596
|
+
options = filters.extract_options!
|
597
|
+
filters.unshift(block) if block
|
598
|
+
[type, filters, options.dup]
|
393
599
|
end
|
394
600
|
|
395
601
|
# This is used internally to append, prepend and skip callbacks to the
|
396
602
|
# CallbackChain.
|
397
|
-
def __update_callbacks(name
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
|
403
|
-
chain = target.send("_#{name}_callbacks")
|
404
|
-
yield target, chain.dup, type, filters, options
|
405
|
-
target.__reset_runner(name)
|
603
|
+
def __update_callbacks(name) #:nodoc:
|
604
|
+
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
|
605
|
+
chain = target.get_callbacks name
|
606
|
+
yield target, chain.dup
|
406
607
|
end
|
407
608
|
end
|
408
609
|
|
@@ -410,7 +611,7 @@ module ActiveSupport
|
|
410
611
|
#
|
411
612
|
# set_callback :save, :before, :before_meth
|
412
613
|
# set_callback :save, :after, :after_meth, if: :condition
|
413
|
-
# set_callback :save, :around, ->(r,
|
614
|
+
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
|
414
615
|
#
|
415
616
|
# The second arguments indicates whether the callback is to be run +:before+,
|
416
617
|
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
|
@@ -421,7 +622,7 @@ module ActiveSupport
|
|
421
622
|
# The callback can be specified as a symbol naming an instance method; as a
|
422
623
|
# proc, lambda, or block; as a string to be instance evaluated; or as an
|
423
624
|
# object that responds to a certain method determined by the <tt>:scope</tt>
|
424
|
-
# argument to +
|
625
|
+
# argument to +define_callbacks+.
|
425
626
|
#
|
426
627
|
# If a proc, lambda, or block is given, its body is evaluated in the context
|
427
628
|
# of the current object. It can also optionally accept the current object as
|
@@ -435,23 +636,24 @@ module ActiveSupport
|
|
435
636
|
#
|
436
637
|
# ===== Options
|
437
638
|
#
|
438
|
-
# * <tt>:if</tt> - A symbol
|
439
|
-
#
|
440
|
-
#
|
441
|
-
#
|
639
|
+
# * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
|
640
|
+
# each naming an instance method or a proc; the callback will be called
|
641
|
+
# only when they all return a true value.
|
642
|
+
# * <tt>:unless</tt> - A symbol, a string or an array of symbols and
|
643
|
+
# strings, each naming an instance method or a proc; the callback will
|
644
|
+
# be called only when they all return a false value.
|
442
645
|
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
|
443
646
|
# existing chain rather than appended.
|
444
647
|
def set_callback(name, *filter_list, &block)
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
end
|
648
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
649
|
+
self_chain = get_callbacks name
|
650
|
+
mapped = filters.map do |filter|
|
651
|
+
Callback.build(self_chain, filter, type, options)
|
652
|
+
end
|
451
653
|
|
654
|
+
__update_callbacks(name) do |target, chain|
|
452
655
|
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
|
453
|
-
|
454
|
-
target.send("_#{name}_callbacks=", chain)
|
656
|
+
target.set_callbacks name, chain
|
455
657
|
end
|
456
658
|
end
|
457
659
|
|
@@ -463,36 +665,34 @@ module ActiveSupport
|
|
463
665
|
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
|
464
666
|
# end
|
465
667
|
def skip_callback(name, *filter_list, &block)
|
466
|
-
|
668
|
+
type, filters, options = normalize_callback_params(filter_list, block)
|
669
|
+
|
670
|
+
__update_callbacks(name) do |target, chain|
|
467
671
|
filters.each do |filter|
|
468
672
|
filter = chain.find {|c| c.matches?(type, filter) }
|
469
673
|
|
470
674
|
if filter && options.any?
|
471
|
-
new_filter = filter.
|
675
|
+
new_filter = filter.merge(chain, options)
|
472
676
|
chain.insert(chain.index(filter), new_filter)
|
473
|
-
new_filter.recompile!(options)
|
474
677
|
end
|
475
678
|
|
476
679
|
chain.delete(filter)
|
477
680
|
end
|
478
|
-
target.
|
681
|
+
target.set_callbacks name, chain
|
479
682
|
end
|
480
683
|
end
|
481
684
|
|
482
685
|
# Remove all set callbacks for the given event.
|
483
|
-
def reset_callbacks(
|
484
|
-
callbacks =
|
686
|
+
def reset_callbacks(name)
|
687
|
+
callbacks = get_callbacks name
|
485
688
|
|
486
689
|
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
487
|
-
chain = target.
|
690
|
+
chain = target.get_callbacks(name).dup
|
488
691
|
callbacks.each { |c| chain.delete(c) }
|
489
|
-
target.
|
490
|
-
target.__reset_runner(symbol)
|
692
|
+
target.set_callbacks name, chain
|
491
693
|
end
|
492
694
|
|
493
|
-
self.
|
494
|
-
|
495
|
-
__reset_runner(symbol)
|
695
|
+
self.set_callbacks name, callbacks.dup.clear
|
496
696
|
end
|
497
697
|
|
498
698
|
# Define sets of events in the object life cycle that support callbacks.
|
@@ -504,10 +704,11 @@ module ActiveSupport
|
|
504
704
|
#
|
505
705
|
# * <tt>:terminator</tt> - Determines when a before filter will halt the
|
506
706
|
# callback chain, preventing following callbacks from being called and
|
507
|
-
# the event from being triggered. This
|
508
|
-
# result of the callback
|
707
|
+
# the event from being triggered. This should be a lambda to be executed.
|
708
|
+
# The current object and the return result of the callback will be called
|
709
|
+
# with the lambda.
|
509
710
|
#
|
510
|
-
# define_callbacks :validate, terminator:
|
711
|
+
# define_callbacks :validate, terminator: ->(target, result) { result == false }
|
511
712
|
#
|
512
713
|
# In this example, if any before validate callbacks returns +false+,
|
513
714
|
# other callbacks are not executed. Defaults to +false+, meaning no value
|
@@ -562,13 +763,33 @@ module ActiveSupport
|
|
562
763
|
# define_callbacks :save, scope: [:name]
|
563
764
|
#
|
564
765
|
# would call <tt>Audit#save</tt>.
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
766
|
+
#
|
767
|
+
# NOTE: +method_name+ passed to `define_model_callbacks` must not end with
|
768
|
+
# `!`, `?` or `=`.
|
769
|
+
def define_callbacks(*names)
|
770
|
+
options = names.extract_options!
|
771
|
+
|
772
|
+
names.each do |name|
|
773
|
+
class_attribute "_#{name}_callbacks", instance_writer: false
|
774
|
+
set_callbacks name, CallbackChain.new(name, options)
|
775
|
+
|
776
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
777
|
+
def _run_#{name}_callbacks(&block)
|
778
|
+
__run_callbacks__(_#{name}_callbacks, &block)
|
779
|
+
end
|
780
|
+
RUBY
|
570
781
|
end
|
571
782
|
end
|
783
|
+
|
784
|
+
protected
|
785
|
+
|
786
|
+
def get_callbacks(name)
|
787
|
+
send "_#{name}_callbacks"
|
788
|
+
end
|
789
|
+
|
790
|
+
def set_callbacks(name, callbacks)
|
791
|
+
send "_#{name}_callbacks=", callbacks
|
792
|
+
end
|
572
793
|
end
|
573
794
|
end
|
574
795
|
end
|