Skip to content

Commit

Permalink
#270 ensure interactor returns a ActiveInteractor::Interactor::Result
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronmallen committed Oct 28, 2020
1 parent 6a92def commit 712dadc
Show file tree
Hide file tree
Showing 32 changed files with 98 additions and 128 deletions.
18 changes: 10 additions & 8 deletions lib/active_interactor/context/attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ def attribute?(attr_name)
# @param context [Hash, Base, Class] attributes to assign to the {Base context}
# @return [Base] a new instance of {Base}
def initialize(context = {})
merge_errors!(context) if context.respond_to?(:errors)
copy_flags!(context)
copy_called!(context)
context = context_attributes_as_hash(context) || {}
context_object = context.is_a?(ActiveInteractor::Interactor::Result) ? context.context : context
merge_errors!(context_object) if context_object.respond_to?(:errors)
copy_flags!(context_object)
copy_called!(context_object)
context_object = context_attributes_as_hash(context_object) || {}
super

merge_attribute_values(context)
merge_attribute_values(context_object)
end

# Returns the value of an attribute
Expand Down Expand Up @@ -128,10 +129,11 @@ def attribute?(attr_name)
# @param context [Class] a {Base context} instance to be merged
# @return [self] the {Base context} instance
def merge!(context)
merge_errors!(context) if context.respond_to?(:errors)
copy_flags!(context)
context_object = context.is_a?(ActiveInteractor::Interactor::Result) ? context.context : context
merge_errors!(context_object) if context_object.respond_to?(:errors)
copy_flags!(context_object)

merged_context_attributes(context).each_pair do |key, value|
merged_context_attributes(context_object).each_pair do |key, value|
self[key] = value unless value.nil?
end
self
Expand Down
22 changes: 11 additions & 11 deletions lib/active_interactor/interactor/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ def contextualize_with(klass)
# @since 1.0.1
#
# @!method context_fail!(errors = nil)
# Call {ActiveInteractor::Context::Status#fail! #fail!} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
# Call {ActiveInteractor::Interactor::Result#fail! #fail!} on the {Base interactor} instance's
# {ActiveInteractor::Interactor::Result result} instance
#
# @since 1.0.0
#
Expand All @@ -244,8 +244,8 @@ def contextualize_with(klass)
# @since 1.0.1
#
# @!method context_rollback!
# Call {ActiveInteractor::Context::Status#rollback! #rollback!} on the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
# Call {ActiveInteractor::Interactor::Result#rollback! #rollback!} on the {Base interactor} instance's
# {ActiveInteractor::Interactor::Result result} instance
#
# @since 1.0.0
delegate :attribute_missing, :attribute_names, :fail!, :respond_to_without_attributes?, :rollback!,
Expand Down Expand Up @@ -354,19 +354,19 @@ def contextualize_with(klass)
# @param context [Hash, Class] attributes to assign to the {Base interactor} instance's
# {ActiveInteractor::Context::Base context} instance
# @return [Base] a new instance of {Base}
def initialize(context = {})
@context = self.class.context_class.new(context)
def initialize(context = {}, state = nil)
context_object = self.class.context_class.new(context)
@context = Result.new(context_object, state)
end

# Mark the {Base interactor} instance as called on the instance's {ActiveInteractor::Context::Base context}
# instance and return the {ActiveInteractor::Context::Base context} instance.
# Mark the {Base interactor} instance as called on the instance's {ActiveInteractor::Interactor::Result result}
# instance and return the {ActiveInteractor::Interactor::Result result} instance.
#
# @since 1.0.0
#
# @return [Class] the {ActiveInteractor::Context::Base context} instance
# @return [Class] the {ActiveInteractor::Interactor::Result result} instance
def finalize_context!
context.called!(self)
context.resolve
context.state.called!(self)
context
end

Expand Down
8 changes: 4 additions & 4 deletions lib/active_interactor/interactor/perform.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ module ClassMethods
# @param options [Hash, Perform::Options] options to use for the {.perform}. See {Perform::Options}
# @return [Class] an instance of the {Base interactor} class' {ActiveInteractor::Context::Base context}
# instance
def perform(context = {}, options = {})
new(context).with_options(options).execute_perform
def perform(context = {}, options = {}, state = nil)
new(context, state).with_options(options).execute_perform
end

# Initialize a new {Base interactor} instance and call its {Interactor::Perform#perform #perform} method without
Expand Down Expand Up @@ -72,8 +72,8 @@ def perform(context = {}, options = {})
# fails.
# @return [Class] an instance of the {Base interactor} class' {ActiveInteractor::Context::Base context}
# instance
def perform!(context = {}, options = {})
new(context).with_options(options).execute_perform!
def perform!(context = {}, options = {}, state = nil)
new(context, state).with_options(options).execute_perform!
end
end

Expand Down
5 changes: 3 additions & 2 deletions lib/active_interactor/interactor/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def fail!(errors = nil)
handle_errors(errors)
resolve_errors!
state.fail!
raise ActiveInteractor::Error::ContextFailure, context
raise ActiveInteractor::Error::ContextFailure, self
end

# Rollback {ActiveInteractor::Base interactors}. Any {ActiveInteractor::Base interactors} listed in {#state}
Expand Down Expand Up @@ -102,12 +102,13 @@ def handle_listed_errors(errors)
end

def method_missing(method_name, *args, &block)
context.public_send(method_name, *args, &block)
result = context.public_send(method_name, *args, &block)
ActiveInteractor::Deprecation::V2.deprecation_warning(
method_name,
'calling #context methods on an ActiveInteractor::Interactor::Result is deprecated use #context instead',
caller
)
result
rescue NoMethodError
super
end
Expand Down
1 change: 0 additions & 1 deletion lib/active_interactor/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def acts_as_context

include ActiveInteractor::Context::Attributes
include ActiveInteractor::Context::Errors
include ActiveInteractor::Context::Status
include ActiveInteractor::Models::InstanceMethods
delegate :each_pair, to: :attributes
end
Expand Down
4 changes: 2 additions & 2 deletions lib/active_interactor/organizer/interactor_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ def initialize(interactor_class, options = {})
# @raise [Error::ContextFailure] if `fail_on_error` is `true` and the {#interactor_class}
# {Context::Status#fail! fails} its {Context::Base context}.
# @return [Class] an instance of {Context::Base context}
def perform(target, context, fail_on_error = false, perform_options = {})
def perform(target, context, fail_on_error = false, perform_options = {}, state = nil)
return if check_conditionals(target, :if) == false
return if check_conditionals(target, :unless) == true

method = fail_on_error ? :perform! : :perform
options = self.perform_options.merge(perform_options)
interactor_class.send(method, context, options)
interactor_class.send(method, context, options, state)
end

def execute_inplace_callback(target, callback)
Expand Down
9 changes: 5 additions & 4 deletions lib/active_interactor/organizer/perform.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def perform
private

def execute_interactor(interface, fail_on_error = false, perform_options = {})
interface.perform(self, context, fail_on_error, perform_options)
interface.perform(self, context, fail_on_error, perform_options, @context.state)
end

def execute_interactor_with_callbacks(interface, fail_on_error = false, perform_options = {})
Expand All @@ -68,9 +68,9 @@ def execute_interactor_with_callbacks(interface, fail_on_error = false, perform_
end
end

def merge_contexts(contexts)
contexts.each { |context| @context.merge!(context) }
context_fail! if contexts.any?(&:failure?)
def merge_contexts(results)
results.each { |result| @context.merge!(result) }
context_fail! if results.any?(&:failure?)
end

def execute_and_merge_contexts(interface)
Expand All @@ -89,6 +89,7 @@ def perform_in_order
end
rescue ActiveInteractor::Error::ContextFailure => e
context.merge!(e.context)
context_fail!
end

def perform_in_parallel
Expand Down
6 changes: 3 additions & 3 deletions spec/active_interactor/interactor/worker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
describe '#execute_perform' do
subject { described_class.new(interactor).execute_perform }

it { is_expected.to be_an TestInteractor.context_class }
it { is_expected.to be_an ActiveInteractor::Interactor::Result }

context 'when context fails' do
before do
Expand All @@ -85,7 +85,7 @@
end

it { expect { subject }.not_to raise_error }
it { is_expected.to be_an TestInteractor.context_class }
it { is_expected.to be_an ActiveInteractor::Interactor::Result }
end

include_examples 'an interactor with options'
Expand All @@ -94,7 +94,7 @@
describe '#execute_perform!' do
subject { described_class.new(interactor).execute_perform! }

it { is_expected.to be_an TestInteractor.context_class }
it { is_expected.to be_an ActiveInteractor::Interactor::Result }

it 'is expected to run perform callbacks on interactor' do
expect_any_instance_of(TestInteractor).to receive(:run_callbacks)
Expand Down
14 changes: 7 additions & 7 deletions spec/active_interactor/organizer/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
end
end

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand All @@ -127,7 +127,7 @@
context 'with options :skip_each_perform_callbacks eq to true' do
subject { interactor_class.perform({}, skip_each_perform_callbacks: true) }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand All @@ -151,7 +151,7 @@ def perform

it { expect { subject }.not_to raise_error }
it { is_expected.to be_failure }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on the first interactor' do
expect_any_instance_of(interactor1).to receive(:perform)
subject
Expand All @@ -177,7 +177,7 @@ def perform

it { expect { subject }.not_to raise_error }
it { is_expected.to be_failure }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand All @@ -199,7 +199,7 @@ def perform
end
end

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand All @@ -217,7 +217,7 @@ def perform

it { expect { subject }.not_to raise_error }
it { is_expected.to be_failure }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand All @@ -241,7 +241,7 @@ def perform

it { expect { subject }.not_to raise_error }
it { is_expected.to be_failure }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #perform on both interactors' do
expect_any_instance_of(interactor1).to receive(:perform)
expect_any_instance_of(interactor2).to receive(:perform)
Expand Down
6 changes: 3 additions & 3 deletions spec/integration/a_basic_interactor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def perform
describe '.perform' do
subject { interactor_class.perform }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'test') }
end
Expand All @@ -34,7 +34,7 @@ def perform
subject { interactor_class.perform! }

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'test') }
end
Expand All @@ -58,7 +58,7 @@ def perform
describe '.perform' do
subject(:result) { interactor_class.perform }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'test', some_other_field: 'test 2') }

Expand Down
10 changes: 5 additions & 5 deletions spec/integration/a_basic_organizer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def perform
describe '.perform' do
subject { interactor_class.perform }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field_1: 'test 1', test_field_2: 'test 2') }
end
Expand All @@ -58,7 +58,7 @@ def perform
subject { interactor_class.perform }

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_failure }
it 'is expected to receive #rollback on the first interactor' do
expect_any_instance_of(test_interactor_1).to receive(:rollback)
Expand All @@ -81,7 +81,7 @@ def perform
end

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_failure }
it { expect(subject.errors.count).to eq 1 }
it 'is expected to have errors "something went wrong" on :context' do
Expand All @@ -105,7 +105,7 @@ def perform
subject { interactor_class.perform }

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_failure }
it 'is expected to receive #rollback on all interactors' do
expect_any_instance_of(test_interactor_2).to receive(:rollback)
Expand All @@ -124,7 +124,7 @@ def perform
end

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_failure }
it { expect(subject.errors.count).to eq 1 }
it 'is expected to have errors "something went wrong" on :context' do
Expand Down
2 changes: 1 addition & 1 deletion spec/integration/a_failing_interactor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def perform
subject { interactor_class.perform }

it { expect { subject }.not_to raise_error }
it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_failure }
it 'is expected to receive #rollback' do
expect_any_instance_of(interactor_class).to receive(:rollback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def downcase_test_field
context 'with valid context attributes' do
let(:context_attributes) { { test_field: 'TEST' } }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'test') }
end
Expand All @@ -52,15 +52,15 @@ def downcase_test_field
context 'with :test_field "TEST" and :should_downcase true' do
let(:context_attributes) { { test_field: 'TEST', should_downcase: true } }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'test') }
end

context 'with :test_field "TEST" and :should_downcase false' do
let(:context_attributes) { { test_field: 'TEST', should_downcase: false } }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it { is_expected.to have_attributes(test_field: 'TEST') }
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_after_perform; end
describe '.perform' do
subject { interactor_class.perform }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it { is_expected.to be_successful }
it 'is expected to receive #test_after_perform' do
expect_any_instance_of(interactor_class).to receive(:test_after_perform)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_after_rollback; end
describe '.perform' do
subject { interactor_class.perform }

it { is_expected.to be_a interactor_class.context_class }
it { is_expected.to be_a ActiveInteractor::Interactor::Result }
it 'is expected to receive #test_after_rollback' do
expect_any_instance_of(interactor_class).to receive(:test_after_rollback)
subject
Expand Down
Loading

0 comments on commit 712dadc

Please sign in to comment.