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