1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Support double-yield inside an around callback

It's questionable whether this is a good thing -- it forces any later/
inner callback to handle multiple invocations, along with the actual
wrapped action. But it worked prior to 871ca21f6a,
so we shouldn't break it unintentionally.
This commit is contained in:
Matthew Draper 2016-12-15 19:23:10 +10:30
parent cfc126e150
commit 833ea903a9
2 changed files with 77 additions and 18 deletions

View file

@ -109,16 +109,22 @@ module ActiveSupport
invoke_sequence = Proc.new do
skipped = nil
while true
current, next_sequence = next_sequence, next_sequence.nested
current = next_sequence
current.invoke_before(env)
if current.final?
env.value = !env.halted && (!block_given? || yield)
elsif current.skip?(env)
(skipped ||= []) << current
next_sequence = next_sequence.nested
next
else
target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
target.send(method, *arguments, &block)
next_sequence = next_sequence.nested
begin
target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
target.send(method, *arguments, &block)
ensure
next_sequence = current
end
end
current.invoke_after(env)
skipped.pop.invoke_after(env) while skipped && skipped.first

View file

@ -224,10 +224,51 @@ module CallbacksTest
define_callbacks :save
end
class AroundPerson < MySuper
class MySlate < MySuper
attr_reader :history
attr_accessor :save_fails
def initialize
@history = []
end
def save
run_callbacks :save do
raise "inside save" if save_fails
@history << "running"
end
end
def no; false; end
def yes; true; end
def method_missing(sym, *)
case sym
when /^log_(.*)/
@history << $1
nil
when /^wrap_(.*)/
@history << "wrap_#$1"
yield
@history << "unwrap_#$1"
nil
when /^double_(.*)/
@history << "first_#$1"
yield
@history << "second_#$1"
yield
@history << "third_#$1"
else
super
end
end
def respond_to_missing?(sym)
sym =~ /^(log|wrap)_/ || super
end
end
class AroundPerson < MySlate
set_callback :save, :before, :nope, if: :no
set_callback :save, :before, :nope, unless: :yes
set_callback :save, :after, :tweedle
@ -242,9 +283,6 @@ module CallbacksTest
set_callback :save, :around, :w0tno, if: :no
set_callback :save, :around, :tweedle_deedle
def no; false; end
def yes; true; end
def nope
@history << "boom"
end
@ -283,17 +321,6 @@ module CallbacksTest
yield
@history << "tweedle deedle post"
end
def initialize
@history = []
end
def save
run_callbacks :save do
raise "inside save" if save_fails
@history << "running"
end
end
end
class AroundPersonResult < MySuper
@ -408,6 +435,32 @@ module CallbacksTest
end
end
class DoubleYieldTest < ActiveSupport::TestCase
class DoubleYieldModel < MySlate
set_callback :save, :around, :wrap_outer
set_callback :save, :around, :double_trouble
set_callback :save, :around, :wrap_inner
end
def test_double_save
double = DoubleYieldModel.new
double.save
assert_equal [
"wrap_outer",
"first_trouble",
"wrap_inner",
"running",
"unwrap_inner",
"second_trouble",
"wrap_inner",
"running",
"unwrap_inner",
"third_trouble",
"unwrap_outer",
], double.history
end
end
class CallStackTest < ActiveSupport::TestCase
def test_tidy_call_stack
around = AroundPerson.new