Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,7 @@ interaction's lifecycle.

``` rb
class Increment < ActiveInteraction::Base
set_callback :run, :before, -> { puts 'before run' }
set_callback :filter, :before, -> { puts 'before filter' }

integer :x
Expand All @@ -1075,6 +1076,7 @@ class Increment < ActiveInteraction::Base
end

Increment.run!(x: 1)
# before run
# before filter
# after validate
# >>>
Expand All @@ -1083,7 +1085,7 @@ Increment.run!(x: 1)
# => 2
```

In order, the available callbacks are `filter`, `validate`, and `execute`.
In order, the available callbacks are `run`, `filter`, `validate`, and `execute`.
You can set `before`, `after`, or `around` on any of them.

### Composition
Expand Down
17 changes: 10 additions & 7 deletions lib/active_interaction/concerns/runnable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Runnable

included do
define_callbacks :execute
define_callbacks :run
end

# @return [Errors]
Expand Down Expand Up @@ -69,13 +70,15 @@ def compose(other, *args)
# @return (see #result=)
# @return [nil]
def run
return self.result = nil unless valid?

self.result = run_callbacks(:execute) do
execute
rescue Interrupt => e
errors.backtrace = e.errors.backtrace || e.backtrace
errors.merge!(e.errors)
run_callbacks(:run) do
return self.result = nil unless valid?

self.result = run_callbacks(:execute) do
execute
rescue Interrupt => e
errors.backtrace = e.errors.backtrace || e.backtrace
errors.merge!(e.errors)
end
end
end

Expand Down
29 changes: 28 additions & 1 deletion spec/active_interaction/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def filters(klass)
context 'callbacks' do
let(:described_class) { Class.new(TestInteraction) }

%w[filter validate execute].each do |name|
%w[run filter validate execute].each do |name|
%w[before after around].map(&:to_sym).each do |type|
it "runs the #{type} #{name} callback" do
called = false
Expand All @@ -480,6 +480,33 @@ def filters(klass)
end
end

it 'runs callbacks in the correct order' do
execution_log = []
described_class.set_callback(:filter, :before) { execution_log << :before_filter }
described_class.set_callback(:filter, :after) { execution_log << :after_filter }
described_class.set_callback(:validate, :before) { execution_log << :before_validate }
described_class.set_callback(:validate, :after) { execution_log << :after_validate }
described_class.set_callback(:execute, :before) { execution_log << :before_execute }
described_class.set_callback(:execute, :after) { execution_log << :after_execute }
described_class.set_callback(:run, :before) { execution_log << :before_run }
described_class.set_callback(:run, :after) { execution_log << :after_run }

outcome

expect(execution_log).to eq(
%i[
before_run
before_filter
after_filter
before_validate
after_validate
before_execute
after_execute
after_run
]
)
end

context 'with errors during filter' do
before do
described_class.set_callback(:filter, :before) do
Expand Down
1 change: 1 addition & 0 deletions spec/active_interaction/concerns/runnable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def execute

include_examples 'set_callback examples', :validate
include_examples 'set_callback examples', :execute
include_examples 'set_callback examples', :run

context 'execute with composed interaction' do
class WithFailingCompose # rubocop:disable Lint/ConstantDefinitionInBlock
Expand Down