Skip to content

Commit

Permalink
Separate organizer behavior and interactor lint
Browse files Browse the repository at this point in the history
  • Loading branch information
laserlemon committed Aug 15, 2013
1 parent f326498 commit 6cb765b
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 166 deletions.
24 changes: 0 additions & 24 deletions lib/interactor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ def perform(context = {})
new(context).tap(&:perform)
end

def interactors
@interactors ||= []
end

def organize(*interactors)
@interactors = interactors.flatten
end

def rollback(context = {})
new(context).tap(&:rollback)
end
Expand All @@ -38,26 +30,10 @@ def initialize(context = {})
def setup
end

def interactors
self.class.interactors
end

def perform
interactors.each do |interactor|
performed << interactor
interactor.perform(context)
rollback && break if context.failure?
end
end

def rollback
performed.reverse_each do |interactor|
interactor.rollback(context)
end
end

def performed
@performed ||= []
end

def success?
Expand Down
41 changes: 40 additions & 1 deletion lib/interactor/organizer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
module Interactor
module Organizer
def self.included(base)
base.send(:include, Interactor)
base.class_eval do
include Interactor

extend ClassMethods
include InstanceMethods

This comment has been minimized.

Copy link
@sferik

sferik Aug 19, 2013

Out of curiosity, what’s the reason for putting instance methods into a module within a module and then including those methods when the module is included?

This comment has been minimized.

Copy link
@laserlemon

laserlemon Aug 19, 2013

Author Collaborator

Great question! Got stuck in the old ActiveModel::Concern syntax. Will fix!

end
end

module ClassMethods
def interactors
@interactors ||= []
end

def organize(*interactors)
@interactors = interactors.flatten
end
end

module InstanceMethods
def interactors
self.class.interactors
end

def perform
interactors.each do |interactor|
performed << interactor
interactor.perform(context)
rollback && break if context.failure?
end
end

def rollback
performed.reverse_each do |interactor|
interactor.rollback(context)
end
end

def performed
@performed ||= []
end
end
end
end
4 changes: 3 additions & 1 deletion spec/interactor/organizer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

module Interactor
describe Organizer do
let(:interactor) { Class.new { include Organizer } }
include_examples :lint

let(:interactor) { Class.new.send(:include, Organizer) }

describe ".interactors" do
it "is empty by default" do
Expand Down
141 changes: 1 addition & 140 deletions spec/interactor_spec.rb
Original file line number Diff line number Diff line change
@@ -1,144 +1,5 @@
require "spec_helper"

describe Interactor do
let(:interactor) { Class.new { include Interactor } }

describe ".perform" do
let(:instance) { double(:instance) }

it "performs an instance with the given context" do
expect(interactor).to receive(:new).with(foo: "bar") { instance }
expect(instance).to receive(:perform).once.with(no_args)

expect(interactor.perform(foo: "bar")).to eq(instance)
end

it "provides a blank context if none is given" do
expect(interactor).to receive(:new).with({}) { instance }
expect(instance).to receive(:perform).once.with(no_args)

expect(interactor.perform).to eq(instance)
end
end

describe ".rollback" do
let(:instance) { double(:instance) }

it "rolls back an instance with the given context" do
expect(interactor).to receive(:new).with(foo: "bar") { instance }
expect(instance).to receive(:rollback).once.with(no_args)

expect(interactor.rollback(foo: "bar")).to eq(instance)
end

it "provides a blank context if none is given" do
expect(interactor).to receive(:new).with({}) { instance }
expect(instance).to receive(:rollback).once.with(no_args)

expect(interactor.rollback).to eq(instance)
end
end

describe ".new" do
let(:context) { double(:context) }

it "initializes a context" do
expect(Interactor::Context).to receive(:build).with(foo: "bar") { context }

instance = interactor.new(foo: "bar")

expect(instance).to be_a(Interactor)
expect(instance.context).to eq(context)
end

it "initializes a blank context if none is given" do
expect(Interactor::Context).to receive(:build).with({}) { context }

instance = interactor.new

expect(instance).to be_a(Interactor)
expect(instance.context).to eq(context)
end

it "calls setup" do
interactor.class_eval do
def setup
context[:foo] = bar
end
end

instance = interactor.new(bar: "baz")

expect(instance.context[:foo]).to eq("baz")
end
end

describe "#setup" do
let(:instance) { interactor.new }

it "exists" do
expect(instance).to respond_to(:setup)
expect { instance.setup }.not_to raise_error
expect { instance.method(:setup) }.not_to raise_error
end
end

describe "#success?" do
let(:instance) { interactor.new }
let(:context) { instance.context }

it "defers to the context" do
context.stub(success?: true)
expect(instance.success?).to eq(true)

context.stub(success?: false)
expect(instance.success?).to eq(false)
end
end

describe "#failure?" do
let(:instance) { interactor.new }
let(:context) { instance.context }

it "defers to the context" do
context.stub(failure?: true)
expect(instance.failure?).to eq(true)

context.stub(failure?: false)
expect(instance.failure?).to eq(false)
end
end

describe "#fail!" do
let(:instance) { interactor.new }
let(:context) { instance.context }

it "defers to the context" do
expect(context).to receive(:fail!).with(no_args)

instance.fail!
end

it "passes updates to the context" do
expect(context).to receive(:fail!).with(foo: "bar")

instance.fail!(foo: "bar")
end
end

describe "context deferral" do
let(:instance) { interactor.new(foo: "bar") }

it "defers to keys that exist in the context" do
expect(instance).to respond_to(:foo)
expect(instance.foo).to eq("bar")
expect { instance.method(:foo) }.not_to raise_error
end

it "bombs if the key does not exist in the context" do
expect(instance).not_to respond_to(:baz)
expect { instance.baz }.to raise_error(NoMethodError)
expect { instance.method(:baz) }.to raise_error(NameError)
end
end
include_examples :lint
end
Loading

0 comments on commit 6cb765b

Please sign in to comment.