From 7d2e4a6bd2ee86d49ef08ae16f850bf4dbfaf2fe Mon Sep 17 00:00:00 2001 From: Matt Jonas Date: Tue, 26 Mar 2024 16:39:33 -0700 Subject: [PATCH] In an Organizer, skip an Interactor's callbacks if the Interactor is skipped Previously, if a deferred Interactor had a conditional (ie; if or unless) attached to it, it's deferred after_perform callbacks were NOT skipped. This is because when using deferment...an Organizer assumes responsibility for executing the after_perform callbacks. When I implemented the Organizer logic that runs the deferred after_perform callbacks, I failed to account for conditionals. This commit passes the calling Organizer in with the call to execute the deferred after_perform callbacks. This is used to check the conditional and see if the callback should be run or skipped. Now, if an Interactor is skipped, it's deferred after_perform callbacks will be too. --- .../organizer/interactor_interface.rb | 4 +- .../interactor_interface_collection.rb | 2 +- lib/active_interactor/organizer/perform.rb | 2 +- ...onditionally_organized_interactors_spec.rb | 136 ++++++++++++++++++ 4 files changed, 141 insertions(+), 3 deletions(-) diff --git a/lib/active_interactor/organizer/interactor_interface.rb b/lib/active_interactor/organizer/interactor_interface.rb index 34ced30..76478e7 100644 --- a/lib/active_interactor/organizer/interactor_interface.rb +++ b/lib/active_interactor/organizer/interactor_interface.rb @@ -88,8 +88,10 @@ def execute_inplace_callback(target, callback) end # Executes after_perform callbacks that have been deferred on the interactor - def execute_deferred_after_perform_callbacks(context) + def execute_deferred_after_perform_callbacks(context, organizer) return unless deferred_after_perform_callbacks.present? + return if check_conditionals(organizer, :if) == false + return if check_conditionals(organizer, :unless) == true interactor = interactor_class.new(context) env = ActiveSupport::Callbacks::Filters::Environment.new(interactor, false, nil) diff --git a/lib/active_interactor/organizer/interactor_interface_collection.rb b/lib/active_interactor/organizer/interactor_interface_collection.rb index 669a1f7..4131bc4 100644 --- a/lib/active_interactor/organizer/interactor_interface_collection.rb +++ b/lib/active_interactor/organizer/interactor_interface_collection.rb @@ -64,7 +64,7 @@ def execute_deferred_after_perform_callbacks(context) if interface.interactor_class <= ActiveInteractor::Organizer::Base context.merge!(interface.interactor_class.organized.execute_deferred_after_perform_callbacks(context)) else - context.merge!(interface.execute_deferred_after_perform_callbacks(context)) + context.merge!(interface.execute_deferred_after_perform_callbacks(context, self)) end end end diff --git a/lib/active_interactor/organizer/perform.rb b/lib/active_interactor/organizer/perform.rb index 2234795..5d43751 100644 --- a/lib/active_interactor/organizer/perform.rb +++ b/lib/active_interactor/organizer/perform.rb @@ -75,7 +75,7 @@ def run_deferred_callbacks(interface) end def run_deferred_callbacks_on_interactor(interface, context) - context.merge!(interface.execute_deferred_after_perform_callbacks(context)) + context.merge!(interface.execute_deferred_after_perform_callbacks(context, self)) end def run_deferred_callbacks_on_children(organizer_interface, context) diff --git a/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb b/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb index 48f1e2c..e9cd41f 100644 --- a/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb +++ b/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb @@ -208,6 +208,40 @@ def test_method expect_any_instance_of(test_interactor_2).to receive(:perform) subject end + + context 'when the first interactor has a deferred callback' do + let!(:test_interactor_1) do + build_interactor('TestInteractor1') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end + + context 'when the second interactor has a deferred callback' do + let!(:test_interactor_2) do + build_interactor('TestInteractor2') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end end end @@ -247,6 +281,40 @@ def test_method expect_any_instance_of(test_interactor_2).to receive(:perform) subject end + + context 'when the first interactor has a deferred callback' do + let!(:test_interactor_1) do + build_interactor('TestInteractor1') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected not to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_nil + end + end + + context 'when the second interactor has a deferred callback' do + let!(:test_interactor_2) do + build_interactor('TestInteractor2') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end end end @@ -286,6 +354,40 @@ def test_method expect_any_instance_of(test_interactor_2).to receive(:perform) subject end + + context 'when the first interactor has a deferred callback' do + let!(:test_interactor_1) do + build_interactor('TestInteractor1') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_nil + end + end + + context 'when the second interactor has a deferred callback' do + let!(:test_interactor_2) do + build_interactor('TestInteractor2') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end end end @@ -321,6 +423,40 @@ def test_method expect_any_instance_of(test_interactor_2).to receive(:perform) subject end + + context 'when the first interactor has a deferred callback' do + let!(:test_interactor_1) do + build_interactor('TestInteractor1') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end + + context 'when the second interactor has a deferred callback' do + let!(:test_interactor_2) do + build_interactor('TestInteractor2') do + defer_after_callbacks_when_organized + + after_perform do + context.after_perform_was_invoked = true + end + end + end + + it 'is expected to invoke the deferred callback' do + result = subject + expect(result.after_perform_was_invoked).to be_truthy + end + end end end end