interactor 2.1.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
- require "spec_helper"
2
-
3
1
  module Interactor
4
2
  describe Context do
5
3
  describe ".build" do
@@ -7,14 +5,14 @@ module Interactor
7
5
  context = Context.build(foo: "bar")
8
6
 
9
7
  expect(context).to be_a(Context)
10
- expect(context).to eq(foo: "bar")
8
+ expect(context.foo).to eq("bar")
11
9
  end
12
10
 
13
11
  it "builds an empty context if no hash is given" do
14
12
  context = Context.build
15
13
 
16
14
  expect(context).to be_a(Context)
17
- expect(context).to eq({})
15
+ expect(context.send(:table)).to eq({})
18
16
  end
19
17
 
20
18
  it "doesn't affect the original hash" do
@@ -23,7 +21,7 @@ module Interactor
23
21
 
24
22
  expect(context).to be_a(Context)
25
23
  expect {
26
- context[:foo] = "baz"
24
+ context.foo = "baz"
27
25
  }.not_to change {
28
26
  hash[:foo]
29
27
  }
@@ -35,32 +33,13 @@ module Interactor
35
33
 
36
34
  expect(context2).to be_a(Context)
37
35
  expect {
38
- context2[:foo] = "baz"
36
+ context2.foo = "baz"
39
37
  }.to change {
40
- context1[:foo]
38
+ context1.foo
41
39
  }.from("bar").to("baz")
42
40
  end
43
41
  end
44
42
 
45
- describe "#initialize" do
46
- it "defaults to empty" do
47
- expect {
48
- expect(Context.new).to eq({})
49
- }.not_to raise_error
50
- end
51
- end
52
-
53
- describe "#[]" do
54
- it "maintains indifferent access" do
55
- require "active_support/hash_with_indifferent_access"
56
-
57
- context = Context.build(HashWithIndifferentAccess.new(foo: "bar"))
58
-
59
- expect(context[:foo]).to eq("bar")
60
- expect(context["foo"]).to eq("bar")
61
- end
62
- end
63
-
64
43
  describe "#success?" do
65
44
  let(:context) { Context.build }
66
45
 
@@ -82,7 +61,7 @@ module Interactor
82
61
 
83
62
  it "sets success to false" do
84
63
  expect {
85
- context.fail!
64
+ context.fail! rescue nil
86
65
  }.to change {
87
66
  context.success?
88
67
  }.from(true).to(false)
@@ -90,17 +69,17 @@ module Interactor
90
69
 
91
70
  it "sets failure to true" do
92
71
  expect {
93
- context.fail!
72
+ context.fail! rescue nil
94
73
  }.to change {
95
74
  context.failure?
96
75
  }.from(false).to(true)
97
76
  end
98
77
 
99
78
  it "preserves failure" do
100
- context.fail!
79
+ context.fail! rescue nil
101
80
 
102
81
  expect {
103
- context.fail!
82
+ context.fail! rescue nil
104
83
  }.not_to change {
105
84
  context.failure?
106
85
  }
@@ -108,19 +87,81 @@ module Interactor
108
87
 
109
88
  it "preserves the context" do
110
89
  expect {
111
- context.fail!
90
+ context.fail! rescue nil
112
91
  }.not_to change {
113
- context[:foo]
92
+ context.foo
114
93
  }
115
94
  end
116
95
 
117
96
  it "updates the context" do
118
97
  expect {
119
- context.fail!(foo: "baz")
98
+ context.fail!(foo: "baz") rescue nil
120
99
  }.to change {
121
- context[:foo]
100
+ context.foo
122
101
  }.from("bar").to("baz")
123
102
  end
103
+
104
+ it "raises failure" do
105
+ expect {
106
+ context.fail!
107
+ }.to raise_error(Failure)
108
+ end
109
+
110
+ it "makes the context available from the failure" do
111
+ begin
112
+ context.fail!
113
+ rescue Failure => error
114
+ expect(error.context).to eq(context)
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "#called!" do
120
+ let(:context) { Context.build }
121
+ let(:instance1) { double(:instance1) }
122
+ let(:instance2) { double(:instance2) }
123
+
124
+ it "appends to the internal list of called instances" do
125
+ expect {
126
+ context.called!(instance1)
127
+ context.called!(instance2)
128
+ }.to change {
129
+ context._called
130
+ }.from([]).to([instance1, instance2])
131
+ end
132
+ end
133
+
134
+ describe "#rollback!" do
135
+ let(:context) { Context.build }
136
+ let(:instance1) { double(:instance1) }
137
+ let(:instance2) { double(:instance2) }
138
+
139
+ before do
140
+ allow(context).to receive(:_called) { [instance1, instance2] }
141
+ end
142
+
143
+ it "rolls back each instance in reverse order" do
144
+ expect(instance2).to receive(:rollback).once.with(no_args).ordered
145
+ expect(instance1).to receive(:rollback).once.with(no_args).ordered
146
+
147
+ context.rollback!
148
+ end
149
+
150
+ it "ignores subsequent attempts" do
151
+ expect(instance2).to receive(:rollback).once
152
+ expect(instance1).to receive(:rollback).once
153
+
154
+ context.rollback!
155
+ context.rollback!
156
+ end
157
+ end
158
+
159
+ describe "#_called" do
160
+ let(:context) { Context.build }
161
+
162
+ it "is empty by default" do
163
+ expect(context._called).to eq([])
164
+ end
124
165
  end
125
166
  end
126
167
  end
@@ -1,17 +1,9 @@
1
- require "spec_helper"
2
-
3
1
  module Interactor
4
2
  describe Organizer do
5
3
  include_examples :lint
6
4
 
7
5
  let(:organizer) { Class.new.send(:include, Organizer) }
8
6
 
9
- describe ".interactors" do
10
- it "is empty by default" do
11
- expect(organizer.interactors).to eq([])
12
- end
13
- end
14
-
15
7
  describe ".organize" do
16
8
  let(:interactor2) { double(:interactor2) }
17
9
  let(:interactor3) { double(:interactor3) }
@@ -20,7 +12,7 @@ module Interactor
20
12
  expect {
21
13
  organizer.organize(interactor2, interactor3)
22
14
  }.to change {
23
- organizer.interactors
15
+ organizer.organized
24
16
  }.from([]).to([interactor2, interactor3])
25
17
  end
26
18
 
@@ -28,182 +20,37 @@ module Interactor
28
20
  expect {
29
21
  organizer.organize([interactor2, interactor3])
30
22
  }.to change {
31
- organizer.interactors
23
+ organizer.organized
32
24
  }.from([]).to([interactor2, interactor3])
33
25
  end
34
26
  end
35
27
 
36
- describe "#interactors" do
37
- let(:interactors) { double(:interactors) }
38
- let(:instance) { organizer.new }
39
-
40
- before do
41
- organizer.stub(:interactors) { interactors }
42
- end
43
-
44
- it "defers to the class" do
45
- expect(instance.interactors).to eq(interactors)
28
+ describe ".organized" do
29
+ it "is empty by default" do
30
+ expect(organizer.organized).to eq([])
46
31
  end
47
32
  end
48
33
 
49
- describe "#perform" do
34
+ describe "#call" do
50
35
  let(:instance) { organizer.new }
51
- let(:context) { instance.context }
36
+ let(:context) { double(:context) }
52
37
  let(:interactor2) { double(:interactor2) }
53
38
  let(:interactor3) { double(:interactor3) }
54
39
  let(:interactor4) { double(:interactor4) }
55
- let(:instance2) { double(:instance2) }
56
- let(:instance3) { double(:instance3) }
57
- let(:instance4) { double(:instance4) }
58
-
59
- before do
60
- organizer.stub(:interactors) { [interactor2, interactor3, interactor4] }
61
- end
62
-
63
- it "performs each interactor in order with the context" do
64
- expect(interactor2).to receive(:perform).once.with(context).ordered { instance2 }
65
- expect(interactor3).to receive(:perform).once.with(context).ordered { instance3 }
66
- expect(interactor4).to receive(:perform).once.with(context).ordered { instance4 }
67
-
68
- expect(instance).not_to receive(:rollback)
69
-
70
- instance.perform
71
- end
72
-
73
- it "builds up the performed interactors" do
74
- interactor2.stub(:perform) do
75
- expect(instance.performed).to eq([])
76
- instance2
77
- end
78
-
79
- interactor3.stub(:perform) do
80
- expect(instance.performed).to eq([instance2])
81
- instance3
82
- end
83
-
84
- interactor4.stub(:perform) do
85
- expect(instance.performed).to eq([instance2, instance3])
86
- instance4
87
- end
88
-
89
- expect {
90
- instance.perform
91
- }.to change {
92
- instance.performed
93
- }.from([]).to([instance2, instance3, instance4])
94
- end
95
-
96
- it "aborts and rolls back on failure" do
97
- expect(interactor2).to receive(:perform).once.with(context).ordered { instance2 }
98
- expect(interactor3).to receive(:perform).once.with(context).ordered { context.fail! }
99
- expect(interactor4).not_to receive(:perform)
100
-
101
- expect(instance).to receive(:rollback).once.ordered do
102
- expect(instance.performed).to eq([instance2])
103
- end
104
-
105
- instance.perform
106
- end
107
-
108
- it "aborts and rolls back on error" do
109
- error = StandardError.new("foo")
110
- expect(interactor2).to receive(:perform).once.with(context).ordered { instance2 }
111
- expect(interactor3).to receive(:perform).once.with(context).ordered { raise error }
112
- expect(interactor4).not_to receive(:perform)
113
-
114
- expect(instance).to receive(:rollback).once.ordered do
115
- expect(instance.performed).to eq([instance2])
116
- end
117
-
118
- expect { instance.perform }.to raise_error(error)
119
- end
120
- end
121
-
122
- describe "#rollback" do
123
- let(:instance) { organizer.new }
124
- let(:instance2) { double(:instance2) }
125
- let(:instance3) { double(:instance3) }
126
40
 
127
41
  before do
128
- instance.stub(:performed) { [instance2, instance3] }
42
+ allow(instance).to receive(:context) { context }
43
+ allow(organizer).to receive(:organized) {
44
+ [interactor2, interactor3, interactor4]
45
+ }
129
46
  end
130
47
 
131
- it "rolls back each performed interactor in reverse" do
132
- expect(instance3).to receive(:rollback).once.ordered
133
- expect(instance2).to receive(:rollback).once.ordered
134
-
135
- instance.rollback
136
- end
137
- end
138
-
139
- describe "#performed" do
140
- let(:instance) { organizer.new }
141
-
142
- it "is empty by default" do
143
- expect(instance.performed).to eq([])
144
- end
145
- end
146
-
147
- # organizer
148
- # ├─ organizer2
149
- # │ ├─ interactor2a
150
- # │ ├─ interactor2b
151
- # │ └─ interactor2c
152
- # ├─ interactor3
153
- # ├─ organizer4
154
- # │ ├─ interactor4a
155
- # │ ├─ interactor4b
156
- # │ └─ interactor4c
157
- # └─ interactor5
158
- #
159
- context "organizers within organizers" do
160
- let(:instance) { organizer.new }
161
- let(:context) { instance.context }
162
- let(:organizer2) { Class.new.send(:include, Organizer) }
163
- let(:interactor2a) { double(:interactor2a) }
164
- let(:interactor2b) { double(:interactor2b) }
165
- let(:interactor2c) { double(:interactor2c) }
166
- let(:interactor3) { double(:interactor3) }
167
- let(:organizer4) { Class.new.send(:include, Organizer) }
168
- let(:interactor4a) { double(:interactor4a) }
169
- let(:interactor4b) { double(:interactor4b) }
170
- let(:interactor4c) { double(:interactor4c) }
171
- let(:interactor5) { double(:interactor5) }
172
-
173
- let(:instance2a) { double(:instance2a) }
174
- let(:instance2b) { double(:instance2b) }
175
- let(:instance2c) { double(:instance2c) }
176
- let(:instance3) { double(:instance3) }
177
- let(:instance4a) { double(:instance4a) }
178
- let(:instance4b) { double(:instance4b) }
179
-
180
- before do
181
- organizer.stub(:interactors) { [organizer2, interactor3, organizer4, interactor5] }
182
- organizer2.stub(:interactors) { [interactor2a, interactor2b, interactor2c] }
183
- organizer4.stub(:interactors) { [interactor4a, interactor4b, interactor4c] }
184
- end
185
-
186
- it "performs and rolls back properly" do
187
- expect(interactor2a).to receive(:perform).once.with(context).ordered { instance2a }
188
- expect(interactor2b).to receive(:perform).once.with(context).ordered { instance2b }
189
- expect(interactor2c).to receive(:perform).once.with(context).ordered { instance2c }
190
- expect(interactor3).to receive(:perform).once.with(context).ordered { instance3 }
191
- expect(interactor4a).to receive(:perform).once.with(context).ordered { instance4a }
192
- expect(interactor4b).to receive(:perform).once.with(context).ordered do
193
- context.fail!
194
- instance4b
195
- end
196
- expect(interactor4c).not_to receive(:perform)
197
- expect(interactor5).not_to receive(:perform)
198
-
199
- expect(instance4b).not_to receive(:rollback)
200
- expect(instance4a).to receive(:rollback).once.with(no_args).ordered
201
- expect(instance3).to receive(:rollback).once.with(no_args).ordered
202
- expect(instance2c).to receive(:rollback).once.with(no_args).ordered
203
- expect(instance2b).to receive(:rollback).once.with(no_args).ordered
204
- expect(instance2a).to receive(:rollback).once.with(no_args).ordered
48
+ it "calls each interactor in order with the context" do
49
+ expect(interactor2).to receive(:call!).once.with(context).ordered
50
+ expect(interactor3).to receive(:call!).once.with(context).ordered
51
+ expect(interactor4).to receive(:call!).once.with(context).ordered
205
52
 
206
- instance.perform
53
+ instance.call
207
54
  end
208
55
  end
209
56
  end
@@ -1,5 +1,3 @@
1
- require "spec_helper"
2
-
3
1
  describe Interactor do
4
2
  include_examples :lint
5
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
- if ENV["TRAVIS"]
2
- require "coveralls"
3
- Coveralls.wear!
1
+ if ENV["CODECLIMATE_REPO_TOKEN"]
2
+ require "codeclimate-test-reporter"
3
+ CodeClimate::TestReporter.start
4
4
  end
5
5
 
6
6
  require "interactor"
data/spec/support/lint.rb CHANGED
@@ -1,30 +1,41 @@
1
1
  shared_examples :lint do
2
2
  let(:interactor) { Class.new.send(:include, described_class) }
3
3
 
4
- describe ".perform" do
5
- let(:instance) { double(:instance, failure?: false) }
4
+ describe ".call" do
5
+ let(:context) { double(:context) }
6
+ let(:instance) { double(:instance, context: context) }
6
7
 
7
- it "performs an instance with the given context" do
8
+ it "calls an instance with the given context" do
8
9
  expect(interactor).to receive(:new).once.with(foo: "bar") { instance }
9
- expect(instance).to receive(:perform).once.with(no_args)
10
+ expect(instance).to receive(:run).once.with(no_args)
10
11
 
11
- expect(interactor.perform(foo: "bar")).to eq(instance)
12
+ expect(interactor.call(foo: "bar")).to eq(context)
12
13
  end
13
14
 
14
15
  it "provides a blank context if none is given" do
15
16
  expect(interactor).to receive(:new).once.with({}) { instance }
16
- expect(instance).to receive(:perform).once.with(no_args)
17
+ expect(instance).to receive(:run).once.with(no_args)
17
18
 
18
- expect(interactor.perform).to eq(instance)
19
+ expect(interactor.call).to eq(context)
19
20
  end
21
+ end
20
22
 
21
- it "does not run perform if the context is a failure after setup" do
22
- expect(interactor).to receive(:new).once { instance }
23
- instance.stub(failure?: true)
23
+ describe ".call!" do
24
+ let(:context) { double(:context) }
25
+ let(:instance) { double(:instance, context: context) }
24
26
 
25
- expect(instance).not_to receive(:perform)
27
+ it "calls an instance with the given context" do
28
+ expect(interactor).to receive(:new).once.with(foo: "bar") { instance }
29
+ expect(instance).to receive(:run!).once.with(no_args)
26
30
 
27
- expect(interactor.perform).to eq(instance)
31
+ expect(interactor.call!(foo: "bar")).to eq(context)
32
+ end
33
+
34
+ it "provides a blank context if none is given" do
35
+ expect(interactor).to receive(:new).once.with({}) { instance }
36
+ expect(instance).to receive(:run!).once.with(no_args)
37
+
38
+ expect(interactor.call!).to eq(context)
28
39
  end
29
40
  end
30
41
 
@@ -48,122 +59,77 @@ shared_examples :lint do
48
59
  expect(instance).to be_a(interactor)
49
60
  expect(instance.context).to eq(context)
50
61
  end
51
-
52
- it "calls setup" do
53
- interactor.class_eval do
54
- def setup
55
- context[:foo] = bar
56
- end
57
- end
58
-
59
- instance = interactor.new(bar: "baz")
60
-
61
- expect(instance.context[:foo]).to eq("baz")
62
- end
63
62
  end
64
63
 
65
- describe "#setup" do
64
+ describe "#run" do
66
65
  let(:instance) { interactor.new }
67
66
 
68
- it "exists" do
69
- expect(instance).to respond_to(:setup)
70
- expect { instance.setup }.not_to raise_error
71
- expect { instance.method(:setup) }.not_to raise_error
72
- end
73
- end
74
-
75
- describe "#perform" do
76
- let(:instance) { interactor.new }
67
+ it "runs the interactor" do
68
+ expect(instance).to receive(:run!).once.with(no_args)
77
69
 
78
- it "exists" do
79
- expect(instance).to respond_to(:perform)
80
- expect { instance.perform }.not_to raise_error
81
- expect { instance.method(:perform) }.not_to raise_error
70
+ instance.run
82
71
  end
83
- end
84
72
 
85
- describe "#rollback" do
86
- let(:instance) { interactor.new }
73
+ it "rescues failure" do
74
+ expect(instance).to receive(:run!).and_raise(Interactor::Failure)
87
75
 
88
- it "exists" do
89
- expect(instance).to respond_to(:rollback)
90
- expect { instance.rollback }.not_to raise_error
91
- expect { instance.method(:rollback) }.not_to raise_error
76
+ expect {
77
+ instance.run
78
+ }.not_to raise_error
92
79
  end
93
- end
94
80
 
95
- describe "#success?" do
96
- let(:instance) { interactor.new }
97
- let(:context) { instance.context }
98
-
99
- it "defers to the context" do
100
- context.stub(success?: true)
101
- expect(instance.success?).to eq(true)
81
+ it "raises other errors" do
82
+ expect(instance).to receive(:run!).and_raise("foo")
102
83
 
103
- context.stub(success?: false)
104
- expect(instance.success?).to eq(false)
84
+ expect {
85
+ instance.run
86
+ }.to raise_error("foo")
105
87
  end
106
88
  end
107
89
 
108
- describe "#failure?" do
90
+ describe "#run!" do
109
91
  let(:instance) { interactor.new }
110
- let(:context) { instance.context }
111
92
 
112
- it "defers to the context" do
113
- context.stub(failure?: true)
114
- expect(instance.failure?).to eq(true)
93
+ it "calls the interactor" do
94
+ expect(instance).to receive(:call).once.with(no_args)
115
95
 
116
- context.stub(failure?: false)
117
- expect(instance.failure?).to eq(false)
96
+ instance.run!
118
97
  end
119
- end
120
-
121
- describe "#fail!" do
122
- let(:instance) { interactor.new }
123
- let(:context) { instance.context }
124
98
 
125
- it "defers to the context" do
126
- expect(context).to receive(:fail!).once.with(no_args)
99
+ it "raises failure" do
100
+ expect(instance).to receive(:run!).and_raise(Interactor::Failure)
127
101
 
128
- instance.fail!
102
+ expect {
103
+ instance.run!
104
+ }.to raise_error(Interactor::Failure)
129
105
  end
130
106
 
131
- it "passes updates to the context" do
132
- expect(context).to receive(:fail!).once.with(foo: "bar")
107
+ it "raises other errors" do
108
+ expect(instance).to receive(:run!).and_raise("foo")
133
109
 
134
- instance.fail!(foo: "bar")
110
+ expect {
111
+ instance.run
112
+ }.to raise_error("foo")
135
113
  end
136
114
  end
137
115
 
138
- describe "context deferral" do
139
- context "initialized" do
140
- let(:instance) { interactor.new(foo: "bar", "hello" => "world") }
141
-
142
- it "defers to keys that exist in the context" do
143
- expect(instance).to respond_to(:foo)
144
- expect(instance.foo).to eq("bar")
145
- expect { instance.method(:foo) }.not_to raise_error
146
- end
147
-
148
- it "defers to string keys that exist in the context" do
149
- expect(instance).to respond_to(:hello)
150
- expect(instance.hello).to eq("world")
151
- expect { instance.method(:hello) }.not_to raise_error
152
- end
153
-
154
- it "bombs if the key does not exist in the context" do
155
- expect(instance).not_to respond_to(:baz)
156
- expect { instance.baz }.to raise_error(NoMethodError)
157
- expect { instance.method(:baz) }.to raise_error(NameError)
158
- end
116
+ describe "#call" do
117
+ let(:instance) { interactor.new }
118
+
119
+ it "exists" do
120
+ expect(instance).to respond_to(:call)
121
+ expect { instance.call }.not_to raise_error
122
+ expect { instance.method(:call) }.not_to raise_error
159
123
  end
124
+ end
160
125
 
161
- context "allocated" do
162
- let(:instance) { interactor.allocate }
126
+ describe "#rollback" do
127
+ let(:instance) { interactor.new }
163
128
 
164
- it "doesn't respond to context keys before the context is set" do
165
- expect(instance).not_to respond_to(:foo)
166
- end
129
+ it "exists" do
130
+ expect(instance).to respond_to(:rollback)
131
+ expect { instance.rollback }.not_to raise_error
132
+ expect { instance.method(:rollback) }.not_to raise_error
167
133
  end
168
134
  end
169
135
  end