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