interactor_with_steroids 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,358 @@
1
+ module Interactor
2
+ describe Hooks do
3
+ describe "#with_hooks" do
4
+ def build_hooked(&block)
5
+ hooked = Class.new.send(:include, Interactor::Hooks)
6
+
7
+ hooked.class_eval do
8
+ attr_reader :steps
9
+
10
+ def self.process
11
+ new.tap(&:process).steps
12
+ end
13
+
14
+ def initialize
15
+ @steps = []
16
+ end
17
+
18
+ def process
19
+ with_hooks { steps << :process }
20
+ end
21
+ end
22
+
23
+ hooked.class_eval(&block) if block
24
+ hooked
25
+ end
26
+
27
+ context "with an around hook method" do
28
+ let(:hooked) {
29
+ build_hooked do
30
+ around :add_around_before_and_around_after
31
+
32
+ private
33
+
34
+ def add_around_before_and_around_after(hooked)
35
+ steps << :around_before
36
+ hooked.call
37
+ steps << :around_after
38
+ end
39
+ end
40
+ }
41
+
42
+ it "runs the around hook method" do
43
+ expect(hooked.process).to eq([
44
+ :around_before,
45
+ :process,
46
+ :around_after,
47
+ ])
48
+ end
49
+ end
50
+
51
+ context "with an around hook block" do
52
+ let(:hooked) {
53
+ build_hooked do
54
+ around do |hooked|
55
+ steps << :around_before
56
+ hooked.call
57
+ steps << :around_after
58
+ end
59
+ end
60
+ }
61
+
62
+ it "runs the around hook block" do
63
+ expect(hooked.process).to eq([
64
+ :around_before,
65
+ :process,
66
+ :around_after,
67
+ ])
68
+ end
69
+ end
70
+
71
+ context "with an around hook method and block in one call" do
72
+ let(:hooked) {
73
+ build_hooked do
74
+ around :add_around_before1_and_around_after1 do |hooked|
75
+ steps << :around_before2
76
+ hooked.call
77
+ steps << :around_after2
78
+ end
79
+
80
+ private
81
+
82
+ def add_around_before1_and_around_after1(hooked)
83
+ steps << :around_before1
84
+ hooked.call
85
+ steps << :around_after1
86
+ end
87
+ end
88
+ }
89
+
90
+ it "runs the around hook method and block in order" do
91
+ expect(hooked.process).to eq([
92
+ :around_before1,
93
+ :around_before2,
94
+ :process,
95
+ :around_after2,
96
+ :around_after1,
97
+ ])
98
+ end
99
+ end
100
+
101
+ context "with an around hook method and block in multiple calls" do
102
+ let(:hooked) {
103
+ build_hooked do
104
+ around do |hooked|
105
+ steps << :around_before1
106
+ hooked.call
107
+ steps << :around_after1
108
+ end
109
+
110
+ around :add_around_before2_and_around_after2
111
+
112
+ private
113
+
114
+ def add_around_before2_and_around_after2(hooked)
115
+ steps << :around_before2
116
+ hooked.call
117
+ steps << :around_after2
118
+ end
119
+ end
120
+ }
121
+
122
+ it "runs the around hook block and method in order" do
123
+ expect(hooked.process).to eq([
124
+ :around_before1,
125
+ :around_before2,
126
+ :process,
127
+ :around_after2,
128
+ :around_after1,
129
+ ])
130
+ end
131
+ end
132
+
133
+ context "with a before hook method" do
134
+ let(:hooked) {
135
+ build_hooked do
136
+ before :add_before
137
+
138
+ private
139
+
140
+ def add_before
141
+ steps << :before
142
+ end
143
+ end
144
+ }
145
+
146
+ it "runs the before hook method" do
147
+ expect(hooked.process).to eq([
148
+ :before,
149
+ :process,
150
+ ])
151
+ end
152
+ end
153
+
154
+ context "with a before hook block" do
155
+ let(:hooked) {
156
+ build_hooked do
157
+ before do
158
+ steps << :before
159
+ end
160
+ end
161
+ }
162
+
163
+ it "runs the before hook block" do
164
+ expect(hooked.process).to eq([
165
+ :before,
166
+ :process,
167
+ ])
168
+ end
169
+ end
170
+
171
+ context "with a before hook method and block in one call" do
172
+ let(:hooked) {
173
+ build_hooked do
174
+ before :add_before1 do
175
+ steps << :before2
176
+ end
177
+
178
+ private
179
+
180
+ def add_before1
181
+ steps << :before1
182
+ end
183
+ end
184
+ }
185
+
186
+ it "runs the before hook method and block in order" do
187
+ expect(hooked.process).to eq([
188
+ :before1,
189
+ :before2,
190
+ :process,
191
+ ])
192
+ end
193
+ end
194
+
195
+ context "with a before hook method and block in multiple calls" do
196
+ let(:hooked) {
197
+ build_hooked do
198
+ before do
199
+ steps << :before1
200
+ end
201
+
202
+ before :add_before2
203
+
204
+ private
205
+
206
+ def add_before2
207
+ steps << :before2
208
+ end
209
+ end
210
+ }
211
+
212
+ it "runs the before hook block and method in order" do
213
+ expect(hooked.process).to eq([
214
+ :before1,
215
+ :before2,
216
+ :process,
217
+ ])
218
+ end
219
+ end
220
+
221
+ context "with an after hook method" do
222
+ let(:hooked) {
223
+ build_hooked do
224
+ after :add_after
225
+
226
+ private
227
+
228
+ def add_after
229
+ steps << :after
230
+ end
231
+ end
232
+ }
233
+
234
+ it "runs the after hook method" do
235
+ expect(hooked.process).to eq([
236
+ :process,
237
+ :after,
238
+ ])
239
+ end
240
+ end
241
+
242
+ context "with an after hook block" do
243
+ let(:hooked) {
244
+ build_hooked do
245
+ after do
246
+ steps << :after
247
+ end
248
+ end
249
+ }
250
+
251
+ it "runs the after hook block" do
252
+ expect(hooked.process).to eq([
253
+ :process,
254
+ :after,
255
+ ])
256
+ end
257
+ end
258
+
259
+ context "with an after hook method and block in one call" do
260
+ let(:hooked) {
261
+ build_hooked do
262
+ after :add_after1 do
263
+ steps << :after2
264
+ end
265
+
266
+ private
267
+
268
+ def add_after1
269
+ steps << :after1
270
+ end
271
+ end
272
+ }
273
+
274
+ it "runs the after hook method and block in order" do
275
+ expect(hooked.process).to eq([
276
+ :process,
277
+ :after2,
278
+ :after1,
279
+ ])
280
+ end
281
+ end
282
+
283
+ context "with an after hook method and block in multiple calls" do
284
+ let(:hooked) {
285
+ build_hooked do
286
+ after do
287
+ steps << :after1
288
+ end
289
+
290
+ after :add_after2
291
+
292
+ private
293
+
294
+ def add_after2
295
+ steps << :after2
296
+ end
297
+ end
298
+ }
299
+
300
+ it "runs the after hook block and method in order" do
301
+ expect(hooked.process).to eq([
302
+ :process,
303
+ :after2,
304
+ :after1,
305
+ ])
306
+ end
307
+ end
308
+
309
+ context "with around, before and after hooks" do
310
+ let(:hooked) {
311
+ build_hooked do
312
+ around do |hooked|
313
+ steps << :around_before1
314
+ hooked.call
315
+ steps << :around_after1
316
+ end
317
+
318
+ around do |hooked|
319
+ steps << :around_before2
320
+ hooked.call
321
+ steps << :around_after2
322
+ end
323
+
324
+ before do
325
+ steps << :before1
326
+ end
327
+
328
+ before do
329
+ steps << :before2
330
+ end
331
+
332
+ after do
333
+ steps << :after1
334
+ end
335
+
336
+ after do
337
+ steps << :after2
338
+ end
339
+ end
340
+ }
341
+
342
+ it "runs hooks in the proper order" do
343
+ expect(hooked.process).to eq([
344
+ :around_before1,
345
+ :around_before2,
346
+ :before1,
347
+ :before2,
348
+ :process,
349
+ :after2,
350
+ :after1,
351
+ :around_after2,
352
+ :around_after1,
353
+ ])
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
@@ -0,0 +1,60 @@
1
+ module Interactor
2
+ describe Organizer do
3
+ let(:organizer) {
4
+ Class.new do
5
+ include Organizer
6
+ include Declaration
7
+ end
8
+ }
9
+
10
+ describe ".organize" do
11
+ let(:interactor2) { double(:interactor2) }
12
+ let(:interactor3) { double(:interactor3) }
13
+
14
+ it "sets interactors given class arguments" do
15
+ expect {
16
+ organizer.organize(interactor2, interactor3)
17
+ }.to change {
18
+ organizer.organized
19
+ }.from([]).to([interactor2, interactor3])
20
+ end
21
+
22
+ it "sets interactors given an array of classes" do
23
+ expect {
24
+ organizer.organize([interactor2, interactor3])
25
+ }.to change {
26
+ organizer.organized
27
+ }.from([]).to([interactor2, interactor3])
28
+ end
29
+ end
30
+
31
+ describe ".organized" do
32
+ it "is empty by default" do
33
+ expect(organizer.organized).to eq([])
34
+ end
35
+ end
36
+
37
+ describe "#call" do
38
+ let(:instance) { organizer.new }
39
+ let(:context) { double(:context) }
40
+ let(:interactor2) { Class.new.send(:include, Interactor) }
41
+ let(:interactor3) { Class.new.send(:include, Interactor) }
42
+ let(:interactor4) { Class.new.send(:include, Interactor) }
43
+
44
+ before do
45
+ allow(instance).to receive(:context) { context }
46
+ allow(organizer).to receive(:organized) {
47
+ [interactor2, interactor3, interactor4]
48
+ }
49
+ end
50
+
51
+ it "calls each interactor in order with the context" do
52
+ expect(interactor2).to receive(:call!).once.ordered
53
+ expect(interactor3).to receive(:call!).once.ordered
54
+ expect(interactor4).to receive(:call!).once.ordered
55
+
56
+ instance.call
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,127 @@
1
+ describe Interactor do
2
+ let(:interactor) { Class.new.send(:include, described_class) }
3
+
4
+ describe ".call" do
5
+ let(:context) { double(:context) }
6
+ let(:instance) { double(:instance, context: context) }
7
+
8
+ it "calls an instance with the given context" do
9
+ expect(interactor).to receive(:new).once.with(foo: "bar") { instance }
10
+ expect(instance).to receive(:run).once.with(no_args)
11
+
12
+ expect(interactor.call(foo: "bar")).to eq(context)
13
+ end
14
+
15
+ it "provides a blank context if none is given" do
16
+ expect(interactor).to receive(:new).once.with({}) { instance }
17
+ expect(instance).to receive(:run).once.with(no_args)
18
+
19
+ expect(interactor.call).to eq(context)
20
+ end
21
+ end
22
+
23
+ describe ".call!" do
24
+ let(:context) { double(:context) }
25
+ let(:instance) { double(:instance, context: context) }
26
+
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)
30
+
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)
39
+ end
40
+ end
41
+
42
+ describe ".new" do
43
+ let(:context) { double(:context) }
44
+
45
+ it "initializes a context" do
46
+ expect(Interactor::Context).to receive(:build).once.with(foo: "bar") { context }
47
+
48
+ instance = interactor.new(foo: "bar")
49
+
50
+ expect(instance).to be_a(interactor)
51
+ expect(instance.context).to eq(context)
52
+ end
53
+
54
+ it "initializes a blank context if none is given" do
55
+ expect(Interactor::Context).to receive(:build).once.with({}) { context }
56
+
57
+ instance = interactor.new
58
+
59
+ expect(instance).to be_a(interactor)
60
+ expect(instance.context).to eq(context)
61
+ end
62
+ end
63
+
64
+ describe "#run" do
65
+ let(:instance) { interactor.new }
66
+
67
+ it "runs the interactor" do
68
+ expect(instance).to receive(:run!).once.with(no_args)
69
+
70
+ instance.run
71
+ end
72
+
73
+ it "rescues failure" do
74
+ expect(instance).to receive(:run!).and_raise(Interactor::Failure)
75
+
76
+ expect {
77
+ instance.run
78
+ }.not_to raise_error
79
+ end
80
+
81
+ it "raises other errors" do
82
+ expect(instance).to receive(:run!).and_raise("foo")
83
+
84
+ expect {
85
+ instance.run
86
+ }.to raise_error("foo")
87
+ end
88
+ end
89
+
90
+ describe "#run!" do
91
+ let(:instance) { interactor.new(context) }
92
+ let(:context) { double(:context, to_h: {}) }
93
+
94
+ it "calls the interactor" do
95
+ expect(instance).to receive(:call).once.with(no_args)
96
+
97
+ instance.run!
98
+ end
99
+
100
+ it "raises failure" do
101
+ expect(instance).to receive(:call).and_raise(Interactor::Failure)
102
+ expect(instance.context).to receive(:fail!).and_raise(Interactor::Failure)
103
+
104
+ expect {
105
+ instance.run!
106
+ }.to raise_error(Interactor::Failure)
107
+ end
108
+
109
+ it "raises other errors" do
110
+ expect(instance).to receive(:run!).and_raise("foo")
111
+
112
+ expect {
113
+ instance.run
114
+ }.to raise_error("foo")
115
+ end
116
+ end
117
+
118
+ describe "#call" do
119
+ let(:instance) { interactor.new }
120
+
121
+ it "exists" do
122
+ expect(instance).to respond_to(:call)
123
+ expect { instance.call }.not_to raise_error
124
+ expect { instance.method(:call) }.not_to raise_error
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,6 @@
1
+ if ENV["CODECLIMATE_REPO_TOKEN"]
2
+ require "simplecov"
3
+ SimpleCov.start
4
+ end
5
+
6
+ require "interactor"
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: interactor_with_steroids
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Collective Idea/Sorare Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Interactor provides a common interface for performing complex user interactions.
56
+ email: hello@sorare.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".gitignore"
62
+ - ".rspec"
63
+ - ".standard.yml"
64
+ - ".travis.yml"
65
+ - CHANGELOG.md
66
+ - CONTRIBUTING.md
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - interactor.gemspec
72
+ - lib/interactor.rb
73
+ - lib/interactor/context.rb
74
+ - lib/interactor/declaration.rb
75
+ - lib/interactor/error.rb
76
+ - lib/interactor/hooks.rb
77
+ - lib/interactor/organizer.rb
78
+ - spec/interactor/context_spec.rb
79
+ - spec/interactor/declaration_spec.rb
80
+ - spec/interactor/hooks_spec.rb
81
+ - spec/interactor/organizer_spec.rb
82
+ - spec/interactor_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://github.com/sorare/interactor
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.1.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Simple interactor implementation
107
+ test_files:
108
+ - spec/interactor/context_spec.rb
109
+ - spec/interactor/declaration_spec.rb
110
+ - spec/interactor/hooks_spec.rb
111
+ - spec/interactor/organizer_spec.rb
112
+ - spec/interactor_spec.rb
113
+ - spec/spec_helper.rb