interactor 2.1.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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