diff --git a/lib/interactor/hooks.rb b/lib/interactor/hooks.rb index d82e7a5..93487bb 100644 --- a/lib/interactor/hooks.rb +++ b/lib/interactor/hooks.rb @@ -127,6 +127,11 @@ def after(*hooks, &block) hooks.each { |hook| after_hooks.unshift(hook) } end + def ensure_hook(*hooks, &block) + hooks << block if block + hooks.each { |hook| ensure_hooks.push(hook) } + end + # Internal: An Array of declared hooks to run around Interactor # invocation. The hooks appear in the order in which they will be run. # @@ -183,6 +188,10 @@ def before_hooks def after_hooks @after_hooks ||= [] end + + def ensure_hooks + @ensure_hooks ||= [] + end end private @@ -213,6 +222,8 @@ def with_hooks yield run_after_hooks end + ensure + run_ensure_hooks end # Internal: Run around hooks. @@ -238,6 +249,10 @@ def run_after_hooks run_hooks(self.class.after_hooks) end + def run_ensure_hooks + run_hooks(self.class.ensure_hooks) + end + # Internal: Run a colection of hooks. The "run_hooks" method is the common # interface by which collections of either before or after hooks are run. # diff --git a/spec/interactor/hooks_spec.rb b/spec/interactor/hooks_spec.rb index 7e40f63..39d7383 100644 --- a/spec/interactor/hooks_spec.rb +++ b/spec/interactor/hooks_spec.rb @@ -24,6 +24,40 @@ def process hooked end + context "with an ensure hook method" do + let(:hooked) { + build_hooked do + before :add_before + after :add_after_with_error + ensure_hook :add_ensure + + def self.context + @context ||= OpenStruct.new + end + + private + + def add_before + self.class.context.resource = 1 + end + + def add_after_with_error + raise 'something wrong has happened' + end + + def add_ensure + self.class.context.resource = 0 + end + end + } + + it "runs the ensure hook block" do + expect { hooked.process }.to raise_error('something wrong has happened') + + expect(hooked.context.resource).to eq 0 + end + end + context "with an around hook method" do let(:hooked) { build_hooked do