diff --git a/lib/active_interactor/interactor/worker.rb b/lib/active_interactor/interactor/worker.rb index 644acdd..7130ec5 100644 --- a/lib/active_interactor/interactor/worker.rb +++ b/lib/active_interactor/interactor/worker.rb @@ -60,10 +60,16 @@ def execute_context! end def execute_context_with_callbacks! - interactor.run_callbacks :perform do + result = interactor.run_callbacks :perform do execute_context_with_validation_check! @context = interactor.finalize_context! end + + if context&.success? && interactor.respond_to?(:run_after_perform_callbacks_on_children) + interactor.run_after_perform_callbacks_on_children + end + + result end def execute_context_with_validation! diff --git a/lib/active_interactor/organizer/callbacks.rb b/lib/active_interactor/organizer/callbacks.rb index d436164..b7eb698 100644 --- a/lib/active_interactor/organizer/callbacks.rb +++ b/lib/active_interactor/organizer/callbacks.rb @@ -141,133 +141,11 @@ def around_each_perform(*filters, &block) def before_each_perform(*filters, &block) set_callback(:each_perform, :before, *filters, &block) end - - # Define a callback to call after all {Organizer::Organize::ClassMethods#organized organized} - # {ActiveInteractor::Base interactors'} {Interactor::Perform#perform #perform} methods have been called. - # - # @since v1.2.0 - # - # @example - # class MyInteractor1 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor1' - # end - # end - # - # class MyInteractor2 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor2' - # end - # end - # - # class MyOrganizer < ActiveInteractor::Organizer - # after_all_perform :print_done - # - # organized MyInteractor1, MyInteractor2 - # - # private - # - # def print_done - # puts 'Done' - # end - # end - # - # MyOrganizer.perform - # "MyInteractor1" - # "MyInteractor2" - # "Done" - # #=> - def after_all_perform(*filters, &block) - set_callback(:all_perform, :after, *filters, &block) - end - - # Define a callback to call around all {Organizer::Organize::ClassMethods#organized organized} - # {ActiveInteractor::Base interactors'} {Interactor::Perform#perform #perform} method calls. - # - # @since v1.2.0 - # - # @example - # class MyInteractor1 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor1' - # sleep(1) - # end - # end - # - # class MyInteractor2 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor2' - # sleep(1) - # end - # end - # - # class MyOrganizer < ActiveInteractor::Organizer - # around_all_perform :print_time - # - # organized MyInteractor1, MyInteractor2 - # - # private - # - # def print_time - # puts Time.now.utc - # yield - # puts Time.now.utc - # end - # end - # - # MyOrganizer.perform - # "2019-04-01 00:00:00 UTC" - # "MyInteractor1" - # "MyInteractor2" - # "2019-04-01 00:00:02 UTC" - # #=> - def around_all_perform(*filters, &block) - set_callback(:all_perform, :around, *filters, &block) - end - - # Define a callback to call before all {Organizer::Organize::ClassMethods#organized organized} - # {ActiveInteractor::Base interactors'} {Interactor::Perform#perform #perform} methods have been called. - # - # @since v1.2.0 - # - # @example - # class MyInteractor1 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor1' - # end - # end - # - # class MyInteractor2 < ActiveInteractor::Base - # def perform - # puts 'MyInteractor2' - # end - # end - # - # class MyOrganizer < ActiveInteractor::Organizer - # before_all_perform :print_starting - # - # organized MyInteractor1, MyInteractor2 - # - # private - # - # def print_starting - # puts 'Starting' - # end - # end - # - # MyOrganizer.perform - # "Starting" - # "MyInteractor1" - # "MyInteractor2" - # #=> - def before_all_perform(*filters, &block) - set_callback(:all_perform, :before, *filters, &block) - end end def self.included(base) base.class_eval do - define_callbacks :each_perform, :all_perform + define_callbacks :each_perform end end end diff --git a/lib/active_interactor/organizer/interactor_interface.rb b/lib/active_interactor/organizer/interactor_interface.rb index a51f46b..ddd442f 100644 --- a/lib/active_interactor/organizer/interactor_interface.rb +++ b/lib/active_interactor/organizer/interactor_interface.rb @@ -101,13 +101,15 @@ def execute_deferred_after_perform_callbacks(context) def init_deferred_after_perform_callbacks after_callbacks_deferred = interactor_class.present? && interactor_class.after_callbacks_deferred_when_organized - @deferred_after_perform_callbacks = after_callbacks_deferred ? interactor_class._perform_callbacks : nil + @deferred_after_perform_callbacks = (interactor_class._perform_callbacks if after_callbacks_deferred) end def skip_deferred_after_perform_callbacks return unless deferred_after_perform_callbacks.present? deferred_after_perform_callbacks.each do |callback| + next unless callback.kind == :after && callback.name == :perform + interactor_class.skip_callback(:perform, :after, callback.filter, raise: false) end end diff --git a/lib/active_interactor/organizer/perform.rb b/lib/active_interactor/organizer/perform.rb index c690107..493a508 100644 --- a/lib/active_interactor/organizer/perform.rb +++ b/lib/active_interactor/organizer/perform.rb @@ -46,14 +46,15 @@ def self.included(base) # {Interactor::Perform#perform #perform}. An {Base organizer} is expected not to define its own # {Interactor::Perform#perform #perform} method in favor of this default implementation. def perform - run_callbacks :all_perform do - if self.class.parallel - perform_in_parallel - else - perform_in_order - end + self.class.parallel ? perform_in_parallel : perform_in_order + end + + def run_after_perform_callbacks_on_children + self.class.organized.each do |interface| + next unless interface.interactor_class.after_callbacks_deferred_when_organized + + context.merge!(interface.execute_deferred_after_perform_callbacks(context)) end - run_after_perform_callbacks_on_interactors if context.success? end private @@ -100,14 +101,6 @@ def perform_in_parallel end merge_contexts(results.map(&:value)) end - - def run_after_perform_callbacks_on_interactors - self.class.organized.each do |interface| - next unless interface.interactor_class.after_callbacks_deferred_when_organized - - context.merge!(interface.execute_deferred_after_perform_callbacks(context)) - end - end end end end diff --git a/spec/integration/an_organizer_containing_organizer_with_after_callbacks_deferred_spec.rb b/spec/integration/an_organizer_containing_organizer_with_after_callbacks_deferred_spec.rb index a269c5d..dbb6467 100644 --- a/spec/integration/an_organizer_containing_organizer_with_after_callbacks_deferred_spec.rb +++ b/spec/integration/an_organizer_containing_organizer_with_after_callbacks_deferred_spec.rb @@ -8,15 +8,16 @@ defer_after_callbacks_when_organized after_perform do - context.after_perform_1a = context.after_perform_1b + 1 + context.steps << 'after_perform_1a' end after_perform do - context.after_perform_1b = context.after_perform_2 + 1 + context.steps << 'after_perform_1b' end def perform - context.perform_1 = 1 + context.steps = [] + context.steps << 'perform_1' end end end @@ -24,11 +25,11 @@ def perform let!(:test_interactor_2) do build_interactor('TestInteractor2') do after_perform do - context.after_perform_2 = context.perform_2 + 1 + context.steps << 'after_perform_2' end def perform - context.perform_2 = context.after_perform_3a + 1 + context.steps << 'perform_2' end end end @@ -38,15 +39,15 @@ def perform defer_after_callbacks_when_organized after_perform do - context.after_perform_3a = context.after_perform_3b + 1 + context.steps << 'after_perform_3a' end after_perform do - context.after_perform_3b = context.after_perform_4a + 1 + context.steps << 'after_perform_3b' end def perform - context.perform_3 = context.perform_1 + 1 + context.steps << 'perform_3' end end end @@ -54,15 +55,15 @@ def perform let!(:test_interactor_4) do build_interactor('TestInteractor4') do after_perform do - context.after_perform_4a = context.after_perform_4b + 1 + context.steps << 'after_perform_4a' end after_perform do - context.after_perform_4b = context.perform_4 + 1 + context.steps << 'after_perform_4b' end def perform - context.perform_4 = context.perform_3 + 1 + context.steps << 'perform_4' end end end @@ -72,11 +73,11 @@ def perform defer_after_callbacks_when_organized after_perform do - context.after_perform_5a = context.after_perform_5b + 1 + context.steps << 'after_perform_5a' end after_perform do - context.after_perform_5b = context.after_perform_1a + 1 + context.steps << 'after_perform_5b' end organize TestInteractor3, TestInteractor4 @@ -107,19 +108,21 @@ def perform it { is_expected.to be_a interactor_class.context_class } it { is_expected.to be_successful } it { is_expected.to have_attributes( - perform_1: 1, - perform_3: 2, - perform_4: 3, - after_perform_4b: 4, - after_perform_4a: 5, - after_perform_3b: 6, - after_perform_3a: 7, - perform_2: 8, - after_perform_2: 9, - after_perform_1b: 10, - after_perform_1a: 11, - after_perform_5b: 12, - after_perform_5a: 13, + steps: [ + 'perform_1', + 'perform_3', + 'perform_4', + 'after_perform_4b', + 'after_perform_4a', + 'after_perform_3b', + 'after_perform_3a', + 'perform_2', + 'after_perform_2', + 'after_perform_1b', + 'after_perform_1a', + 'after_perform_5b', + 'after_perform_5a', + ] ) } end end diff --git a/spec/integration/an_organizer_with_after_callbacks_deferred_spec.rb b/spec/integration/an_organizer_with_after_callbacks_deferred_spec.rb index 0827327..3188245 100644 --- a/spec/integration/an_organizer_with_after_callbacks_deferred_spec.rb +++ b/spec/integration/an_organizer_with_after_callbacks_deferred_spec.rb @@ -8,15 +8,16 @@ defer_after_callbacks_when_organized after_perform do - context.after_perform_1a = context.after_perform_1b + 1 + context.steps << 'after_perform_1a' end after_perform do - context.after_perform_1b = context.after_perform_3a + 1 + context.steps << 'after_perform_1b' end def perform - context.perform_1 = 1 + context.steps = [] + context.steps << 'perform_1' end end end @@ -26,11 +27,11 @@ def perform defer_after_callbacks_when_organized after_perform do - context.after_perform_2 = context.after_perform_1a + 1 + context.steps << 'after_perform_2' end def perform - context.perform_2 = context.perform_1 + 1 + context.steps << 'perform_2' end end end @@ -38,15 +39,15 @@ def perform let!(:test_interactor_3) do build_interactor('TestInteractor3') do after_perform do - context.after_perform_3a = context.after_perform_3b + 1 + context.steps << 'after_perform_3a' end after_perform do - context.after_perform_3b = context.perform_3 + 1 + context.steps << 'after_perform_3b' end def perform - context.perform_3 = context.perform_2 + 1 + context.steps << 'perform_3' end end end @@ -75,14 +76,16 @@ def perform it { is_expected.to be_a interactor_class.context_class } it { is_expected.to be_successful } it { is_expected.to have_attributes( - perform_1: 1, - perform_2: 2, - perform_3: 3, - after_perform_3b: 4, - after_perform_3a: 5, - after_perform_1b: 6, - after_perform_1a: 7, - after_perform_2: 8, + steps: [ + 'perform_1', + 'perform_2', + 'perform_3', + 'after_perform_3b', + 'after_perform_3a', + 'after_perform_1b', + 'after_perform_1a', + 'after_perform_2', + ] ) } context 'when last interactor fails' do @@ -103,13 +106,10 @@ def perform subject { interactor_class.perform} it { is_expected.to have_attributes( - perform_1: 1, - perform_2: 2, - )} - - it { is_expected.to_not respond_to( - :after_perform_1a, - :after_perform_1b, + steps: [ + 'perform_1', + 'perform_2' + ] )} end @@ -123,7 +123,8 @@ def perform end def perform - context.perform_1 = 1 + context.steps = [] + context.steps << 'perform_1' end end end @@ -137,17 +138,13 @@ def perform subject { interactor_class.perform} it { is_expected.to have_attributes( - perform_1: 1, - perform_2: 2, - perform_3: 3, - after_perform_3b: 4, - after_perform_3a: 5, - )} - - it { is_expected.to_not respond_to( - :after_perform_1a, - :after_perform_1b, - :after_perform_2, + steps: [ + 'perform_1', + 'perform_2', + 'perform_3', + 'after_perform_3b', + 'after_perform_3a', + ] )} end end diff --git a/spec/integration/an_organizer_with_all_perform_callbacks.rb b/spec/integration/an_organizer_with_all_perform_callbacks.rb deleted file mode 100644 index ce34b0c..0000000 --- a/spec/integration/an_organizer_with_all_perform_callbacks.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'An organizer with around all callbacks', type: :integration do - let!(:test_interactor_1) do - build_interactor('TestInteractor1') do - before_perform do - context.before_perform_1 = context.around_all_perform_start + 1 - end - - after_perform do - context.after_perform_1 = context.around_perform_1_end + 1 - end - - around_perform :around - def around - context.around_perform_1_start = context.before_perform_1 + 1 - yield - context.around_perform_1_end = context.perform_1 + 1 - end - - def perform - context.perform_1 = context.around_perform_1_start + 1 - end - end - end - - let!(:test_interactor_2) do - build_interactor('TestInteractor2') do - defer_after_callbacks_when_organized - - before_perform do - context.before_perform_2 = context.after_perform_1 + 1 - end - - after_perform do - context.after_perform_2 = context.after_all_perform + 1 - end - - around_perform :around - def around - context.around_perform_2_start = context.before_perform_2 + 1 - yield - context.around_perform_2_end = context.perform_2 + 1 - end - - def perform - context.perform_2 = context.around_perform_2_start + 1 - end - end - end - - let(:interactor_class) do - build_organizer do - before_all_perform do - context.before_all_perform = 1 - end - - after_all_perform do - context.after_all_perform = context.around_all_perform_end + 1 - end - - around_all_perform :around_all - def around_all - context.around_all_perform_start = context.before_all_perform + 1 - yield - context.around_all_perform_end = context.around_perform_2_end + 1 - end - - organize TestInteractor1, TestInteractor2 - end - end - - include_examples 'a class with interactor methods' - include_examples 'a class with interactor callback methods' - include_examples 'a class with interactor context methods' - include_examples 'a class with organizer callback methods' - - describe '.context_class' do - subject { interactor_class.context_class } - - it { is_expected.to eq TestOrganizer::Context } - it { is_expected.to be < ActiveInteractor::Context::Base } - end - - describe '.perform' do - subject { interactor_class.perform } - - it { is_expected.to be_a interactor_class.context_class } - it { is_expected.to be_successful } - it { is_expected.to have_attributes( - before_all_perform: 1, - around_all_perform_start: 2, - - before_perform_1: 3, - around_perform_1_start: 4, - perform_1: 5, - around_perform_1_end: 6, - after_perform_1: 7, - - before_perform_2: 8, - around_perform_2_start: 9, - perform_2: 10, - around_perform_2_end: 11, - - around_all_perform_end: 12, - after_all_perform: 13, - after_perform_2: 14, - ) } - end -end