ii_interactor 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca26ce09ce20cc37d4e35337c05a5f49abf260a648752efeb1ac0e887f85dd8c
4
- data.tar.gz: 0b6f575ef339301f4501b5abd0104dc8a4ce083fba0fe9e0fae4f8cc09b0d023
3
+ metadata.gz: a2fdfb2e93eb53ee2b70845fb78c285aae26c237681537ae59893675873974f1
4
+ data.tar.gz: b85095ca4396ccc3523fbe6f212ed335f057a77dc60e87b0969c14666a5e0d0f
5
5
  SHA512:
6
- metadata.gz: cc4b8a3cfa9b100299f91c17e04efa110c8c482dd9a2d1365003f962bf27fc3682822b5970453bbf9e6d19b6a4267639052aa7acdf23a9c38a24245cdabeb323
7
- data.tar.gz: a882894b2f36b99e1aed993ae7bf888c8fc9261ec8f1c9f1a8f15ce39a67e374476dff2baa6c4a643e3d946a0fa7d8956aa91dcfcf26a1af4395cde0da466fd1
6
+ metadata.gz: 2b1321aee1be7d0c362315ef1020db0113d77445366617de0e0f9ff010c49d1118f7a26f5182102065efa60eb832616ded910f1e3bd222fc897d5fcaa0947236
7
+ data.tar.gz: a0d731eb5334b3846530e60858146c9b1f748d01b313917ee7e50153ab5f22f9fdb8a2cc53c24d08459ff7f074ee203579752c4d778bfad618e269c3361e318f
@@ -39,4 +39,4 @@ jobs:
39
39
  bundler-cache: true
40
40
  - name: Run test
41
41
  run: |
42
- bundle exec rspec
42
+ DEBUG=1 bundle exec rspec
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # CHANGELOG
2
+
3
+ ## 2.0.0
4
+
5
+ * Replace interaction feature with coactive.
6
+
7
+ ## 1.2.0
8
+
9
+ * Add variable setting feature.
10
+
11
+ ## 1.1.1
12
+
13
+ * Check `config.eager_load` instead of `Rails.env` before file loading.
14
+
15
+ ## 1.1.0
16
+
17
+ * Add log subscriber.
18
+
19
+ ## 1.0.0
20
+
21
+ * First release.
data/README.md CHANGED
@@ -36,222 +36,128 @@ Interactor.call(message: 'something')
36
36
  #=> #<IIInteractor::Context message="something", result="called by something">
37
37
  ```
38
38
 
39
- The first argument of `call` is set to `@context`.
40
- The return value of `call` is the same as `@context`.
39
+ The first argument of `Interactor.call` is set to `@context`.
40
+ The return value of `Interactor.call` is the same as `@context`.
41
41
 
42
- ### Callbacks
43
-
44
- Following callbacks are available:
45
-
46
- * `before_call`
47
- * `around_call`
48
- * `after_call`
42
+ ### Context variables
49
43
 
44
+ You can define context variables used in interactor explicitly.
50
45
  For example:
51
46
 
52
47
  ```ruby
53
48
  class Interactor < IIInteractor::Base
54
- before_call do
55
- @message = @context.message
56
- end
49
+ context_in :input
50
+ context_out :result
57
51
 
58
52
  def call
59
- puts @message
53
+ puts @input
54
+ @result = 'result value'
60
55
  end
61
56
  end
62
57
 
63
- Interactor.call(message: 'something')
64
- #=> something
58
+ puts Interactor.call(input: 'input').result
59
+ #=> input
60
+ # result value
65
61
  ```
66
62
 
67
- ### Interactions
63
+ `context_in` copies context into instance variables of interactor,
64
+ while `context_out` copies instance variables of interactor into context.
68
65
 
69
- You can call other interactors in the same context using `interact`:
66
+ You can also define required context as follows:
70
67
 
71
68
  ```ruby
72
- class AInteractor < IIInteractor::Base
73
- def call
74
- puts self.class.name
75
- end
76
- end
77
-
78
- class BInteractor < IIInteractor::Base
79
- def call
80
- puts self.class.name
81
- end
82
- end
83
-
84
- class MainInteractor < IIInteractor::Base
85
- interact AInteractor
86
- interact BInteractor
69
+ class Interactor < IIInteractor::Base
70
+ context_in :input, required: true
87
71
  end
88
72
 
89
- MainInteractor.call
90
- #=> AInteractor
91
- # BInteractor
73
+ Interactor.call
74
+ #=> IIInteractor::RequiredContextError (missing required context: input2)
92
75
  ```
93
76
 
94
- #### Named interaction
95
-
96
- You can also define named interactions.
97
- The interactors to be called are looked up from all interactors.
77
+ You can also define default value as follows:
98
78
 
99
79
  ```ruby
100
- class AInteractor < IIInteractor::Base
101
- react :some_name
102
-
103
- def call
104
- puts self.class.name
105
- end
106
- end
107
-
108
- class BInteractor < IIInteractor::Base
109
- react :some_name
80
+ class Interactor < IIInteractor::Base
81
+ context_in :input, default: 'input'
110
82
 
111
83
  def call
112
- puts self.class.name
84
+ puts @input
113
85
  end
114
86
  end
115
87
 
116
- class MainInteractor < IIInteractor::Base
117
- interact :some_name
118
- end
119
-
120
- MainInteractor.call
121
- #=> AInteractor
122
- # BInteractor
88
+ Interactor.call
89
+ #=> input
123
90
  ```
124
91
 
125
- Note followings:
126
-
127
- * All files in `app/interactors` are loaded in development mode to lookup interactors having same name.
128
- * The called interactors are unordered.
129
-
130
- #### Object based interaction
131
-
132
- You can also define object based interactions.
133
- The interactors to be called are looked up from the namespace corresponding with caller interactor.
92
+ You can also set context from return value of `call` method:
134
93
 
135
94
  ```ruby
136
- class A
137
- end
138
-
139
- class B
140
- end
95
+ class Interactor < IIInteractor::Base
96
+ context_out :result, from_return: true
141
97
 
142
- class Main::AInteractor < IIInteractor::Base
143
98
  def call
144
- puts self.class.name
99
+ 'returned value'
145
100
  end
146
101
  end
147
102
 
148
- class Main::BInteractor < IIInteractor::Base
149
- def call
150
- puts self.class.name
151
- end
152
- end
103
+ Interactor.call.result
104
+ #=> returned value
105
+ ```
153
106
 
154
- class MainInteractor < IIInteractor::Base
155
- interact A
156
- interact B
157
- end
107
+ ### Callbacks
158
108
 
159
- MainInteractor.call
160
- #=> Main::AInteractor
161
- # Main::BInteractor
162
- ```
109
+ Following callbacks are available:
163
110
 
164
- #### Custom interaction
111
+ * `before_call`
112
+ * `around_call`
113
+ * `after_call`
165
114
 
166
- You can also customize lookup of interactors as follows:
115
+ For example:
167
116
 
168
117
  ```ruby
169
- class AInteractor < IIInteractor::Base
170
- def call
171
- puts self.class.name
118
+ class Interactor < IIInteractor::Base
119
+ before_call do
120
+ @message = @context.message
172
121
  end
173
- end
174
122
 
175
- class BInteractor < IIInteractor::Base
176
123
  def call
177
- puts self.class.name
178
- end
179
- end
180
-
181
- class MainInteractor < IIInteractor::Base
182
- # set block
183
- interact do
184
- if @context.condition == 'A'
185
- AInteractor
186
- else
187
- BInteractor
188
- end
189
- end
190
-
191
- # set method name
192
- interact :conditional_interactors
193
-
194
- def conditional_interactors
195
- if @context.condition == 'A'
196
- AInteractor
197
- else
198
- BInteractor
199
- end
124
+ puts @message
200
125
  end
201
126
  end
202
127
 
203
- MainInteractor.call(condition: 'A')
204
- #=> AInteractor
205
-
206
- MainInteractor.call(condition: 'B')
207
- #=> BInteractor
128
+ Interactor.call(message: 'something')
129
+ #=> something
208
130
  ```
209
131
 
210
- #### Nested interaction
132
+ ### Coactions
211
133
 
212
- You can define nested interactions as follows:
134
+ You can call other interactors in the same context using `coact`:
213
135
 
214
136
  ```ruby
215
- class NestedAInteractor < IIInteractor::Base
216
- def call
217
- puts self.class.name
218
- end
219
- end
220
-
221
- class NestedBInteractor < IIInteractor::Base
222
- def call
223
- puts self.class.name
224
- end
225
- end
226
-
227
137
  class AInteractor < IIInteractor::Base
228
- interact NestedAInteractor
229
-
230
138
  def call
231
139
  puts self.class.name
232
140
  end
233
141
  end
234
142
 
235
143
  class BInteractor < IIInteractor::Base
236
- interact NestedBInteractor
237
-
238
144
  def call
239
145
  puts self.class.name
240
146
  end
241
147
  end
242
148
 
243
149
  class MainInteractor < IIInteractor::Base
244
- interact AInteractor
245
- interact BInteractor
150
+ coact AInteractor
151
+ coact BInteractor
246
152
  end
247
153
 
248
154
  MainInteractor.call
249
- #=> NestedAInteractor
250
- # AInteractor
251
- # NestedBInteractor
155
+ #=> AInteractor
252
156
  # BInteractor
253
157
  ```
254
158
 
159
+ See [coactive](https://github.com/kanety/coactive) for more `coact` examples:
160
+
255
161
  ### Stop interactions
256
162
 
257
163
  You can stop interactions as follows:
@@ -271,8 +177,8 @@ class BInteractor < IIInteractor::Base
271
177
  end
272
178
 
273
179
  class MainInteractor < IIInteractor::Base
274
- interact AInteractor
275
- interact BInteractor
180
+ coact AInteractor
181
+ coact BInteractor
276
182
  end
277
183
 
278
184
  context = MainInteractor.call
@@ -314,8 +220,8 @@ class BInteractor < IIInteractor::Base
314
220
  end
315
221
 
316
222
  class MainInteractor < IIInteractor::Base
317
- interact AInteractor
318
- interact BInteractor
223
+ coact AInteractor
224
+ coact BInteractor
319
225
  end
320
226
 
321
227
  context = MainInteractor.call
@@ -348,8 +254,8 @@ class BInteractor < IIInteractor::Base
348
254
  end
349
255
 
350
256
  class MainInteractor < IIInteractor::Base
351
- interact AInteractor
352
- interact BInteractor
257
+ coact AInteractor
258
+ coact BInteractor
353
259
  end
354
260
 
355
261
  MainInteractor.call do |interactor, message|
@@ -359,6 +265,21 @@ end
359
265
  # BInteractor: called B
360
266
  ```
361
267
 
268
+ ### Logging
269
+
270
+ Interactor supports instrumentation hook supplied by `ActiveSupport::Notifications`.
271
+ You can enable log subscriber as follows:
272
+
273
+ ```ruby
274
+ IIInteractor::LogSubscriber.attach_to :ii_interactor
275
+ ```
276
+
277
+ This subscriber will write logs in debug mode as the following example:
278
+
279
+ ```
280
+ Called SimpleInteractor (Duration: 0.3ms, Allocations: 42)
281
+ ```
282
+
362
283
  ## Contributing
363
284
 
364
285
  Bug reports and pull requests are welcome at https://github.com/kanety/ii_interactor.
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency "activesupport", ">= 5.0"
21
+ spec.add_dependency "coactive", ">= 0.1"
21
22
 
22
23
  spec.add_development_dependency "rails", ">= 5.0"
23
24
  spec.add_development_dependency "sqlite3"
@@ -1,76 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'context'
4
+ require_relative 'core'
5
+ require_relative 'variables'
4
6
  require_relative 'callbacks'
5
- require_relative 'interaction'
6
- require_relative 'lookup'
7
+ require_relative 'instrumentation'
8
+ require_relative 'coactors'
7
9
 
8
10
  module IIInteractor
9
11
  class Base
12
+ include Core
10
13
  include Callbacks
11
- include Interaction
12
- include Lookup
13
-
14
- attr_reader :context
15
-
16
- def initialize(context = {}, &block)
17
- @context = if context.is_a?(IIInteractor::Context)
18
- context
19
- else
20
- IIInteractor::Context.new(context, &block)
21
- end
22
- end
23
-
24
- def call_all
25
- planned = lookup.map { |interactor| interactor.new(@context) } + [self]
26
- @context._planned += planned
27
- planned.each_with_index do |interactor, i|
28
- if i == planned.size - 1
29
- interactor.call_self
30
- else
31
- interactor.call_all
32
- end
33
- break if @context.stopped?
34
- end
35
- end
36
-
37
- def call_self
38
- run_callbacks :call do
39
- call
40
- end
41
- @context._called << self
42
- end
43
-
44
- def call
45
- end
46
-
47
- def rollback
48
- end
49
-
50
- def inform(*args)
51
- @context._block.call(*([self] + args)) if @context._block
52
- end
53
-
54
- def fail!(data = {})
55
- @context.fail!(data)
56
- raise UnprogressableError.new(@context)
57
- end
58
-
59
- def stop!(data = {})
60
- @context.stop!(data)
61
- end
62
-
63
- class << self
64
- def call(context = {}, &block)
65
- interactor = new(context, &block)
66
- interactor.call_all
67
- interactor.context
68
- rescue UnprogressableError
69
- interactor.context._called.reverse.each do |called|
70
- called.rollback
71
- end
72
- interactor.context
73
- end
74
- end
14
+ include Instrumentation
15
+ include Variables
16
+ include Coactors
75
17
  end
76
18
  end
@@ -9,6 +9,12 @@ module IIInteractor
9
9
  define_callbacks :call
10
10
  end
11
11
 
12
+ def call_self
13
+ run_callbacks :call do
14
+ super
15
+ end
16
+ end
17
+
12
18
  class_methods do
13
19
  def before_call(*args, &block)
14
20
  set_callback(:call, :before, *args, &block)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ module Coactors
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Coactive::Base
9
+
10
+ configure_coactive do |config|
11
+ config.load_paths = ['app/interactors']
12
+ config.class_suffix = 'Interactor'
13
+ config.use_cache = true
14
+ config.lookup_superclass_until = ['ActiveRecord::Base', 'ActiveModel::Base']
15
+ end
16
+
17
+ class << self
18
+ alias_method :interact, :coact
19
+ alias_method :react, :coaction
20
+ end
21
+ end
22
+ end
23
+ end
@@ -7,7 +7,6 @@ module IIInteractor
7
7
  self[:_block] = block
8
8
  self[:_failed] = false
9
9
  self[:_stopped] = false
10
- self[:_planned] = []
11
10
  self[:_called] = []
12
11
  end
13
12
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ module Core
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_reader :context
9
+ end
10
+
11
+ def initialize(context = {}, &block)
12
+ @context = if context.is_a?(IIInteractor::Context)
13
+ context
14
+ else
15
+ IIInteractor::Context.new(context, &block)
16
+ end
17
+ end
18
+
19
+ def call_all
20
+ planned = coactors.map { |interactor| interactor.new(@context) } + [self]
21
+ planned.each_with_index do |interactor, i|
22
+ if i == planned.size - 1
23
+ interactor.call_self
24
+ else
25
+ interactor.call_all
26
+ end
27
+ break if @context.stopped?
28
+ end
29
+ end
30
+
31
+ def call_self
32
+ call.tap do
33
+ @context._called << self
34
+ end
35
+ end
36
+
37
+ def call
38
+ end
39
+
40
+ def rollback
41
+ end
42
+
43
+ def inform(*args)
44
+ @context._block.call(*([self] + args)) if @context._block
45
+ end
46
+
47
+ def fail!(data = {})
48
+ @context.fail!(data)
49
+ raise UnprogressableError.new(@context)
50
+ end
51
+
52
+ def stop!(data = {})
53
+ @context.stop!(data)
54
+ end
55
+
56
+ class_methods do
57
+ def call(*args, &block)
58
+ interactor = new(*args, &block)
59
+ interactor.call_all
60
+ interactor.context
61
+ rescue UnprogressableError
62
+ interactor.context._called.reverse.each do |called|
63
+ called.rollback
64
+ end
65
+ interactor.context
66
+ end
67
+ end
68
+ end
69
+ end
@@ -3,4 +3,7 @@
3
3
  module IIInteractor
4
4
  class UnprogressableError < StandardError
5
5
  end
6
+
7
+ class RequiredContextError < StandardError
8
+ end
6
9
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ module Instrumentation
5
+ extend ActiveSupport::Concern
6
+
7
+ def call_self
8
+ ActiveSupport::Notifications.instrument 'call.ii_interactor', interactor: self do
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ class LogSubscriber < ActiveSupport::LogSubscriber
5
+ def call(event)
6
+ debug do
7
+ interactor = event.payload[:interactor]
8
+ "Called #{interactor.class} (#{additional_log(event)})"
9
+ end
10
+ end
11
+
12
+ def additional_log(event)
13
+ additions = ["Duration: %.1fms" % event.duration]
14
+ additions << "Allocations: %d" % event.allocations if event.respond_to?(:allocations)
15
+ additions.join(', ')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ class Variable < Struct.new(:name, :options)
5
+ def default
6
+ options[:default] if options
7
+ end
8
+
9
+ def required?
10
+ options[:required] if options
11
+ end
12
+
13
+ def from_return?
14
+ options[:from_return] if options
15
+ end
16
+ end
17
+
18
+ module Variables
19
+ extend ActiveSupport::Concern
20
+
21
+ def call_self
22
+ (self.class.context_ins + self.class.context_outs).each do |var|
23
+ if var.required? && !@context.respond_to?(var.name)
24
+ raise RequiredContextError.new("missing required context: #{var.name}")
25
+ end
26
+ if var.default && !@context.respond_to?(var.name)
27
+ @context[var.name] = Variables.resolve(self, var.default)
28
+ end
29
+ instance_variable_set("@#{var.name}", @context[var.name])
30
+ end
31
+
32
+ super.tap do |return_value|
33
+ self.class.context_outs.each do |var|
34
+ @context[var.name] =
35
+ if var.from_return?
36
+ return_value
37
+ else
38
+ instance_variable_get("@#{var.name}")
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class << self
45
+ def resolve(interactor, value)
46
+ if value.respond_to?(:call)
47
+ interactor.instance_exec(&value)
48
+ elsif value.is_a?(Symbol) && interactor.respond_to?(value, true)
49
+ interactor.send(value)
50
+ else
51
+ value.deep_dup
52
+ end
53
+ end
54
+ end
55
+
56
+ included do
57
+ class_attribute :context_ins, :context_outs
58
+ self.context_ins = []
59
+ self.context_outs = []
60
+ end
61
+
62
+ class_methods do
63
+ def context_in(*names, **options)
64
+ self.context_ins = self.context_ins + names.map { |name| Variable.new(name, options) }
65
+ end
66
+
67
+ def context_out(*names, **options)
68
+ self.context_outs = self.context_outs + names.map { |name| Variable.new(name, options) }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IIInteractor
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/ii_interactor.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'active_support'
2
+ require 'coactive'
2
3
 
3
4
  require 'ii_interactor/version'
4
5
  require 'ii_interactor/errors'
5
6
  require 'ii_interactor/config'
6
7
  require 'ii_interactor/base'
7
- require 'ii_interactor/loader'
8
+ require 'ii_interactor/log_subscriber'
8
9
 
9
10
  module IIInteractor
10
11
  class << self
@@ -15,9 +16,5 @@ module IIInteractor
15
16
  def config
16
17
  Config
17
18
  end
18
-
19
- def load
20
- Loader.call
21
- end
22
19
  end
23
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ii_interactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-13 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: coactive
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rails
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,7 @@ files:
104
118
  - ".github/workflows/ci.yml"
105
119
  - ".gitignore"
106
120
  - ".rspec"
121
+ - CHANGELOG.md
107
122
  - Gemfile
108
123
  - LICENSE
109
124
  - README.md
@@ -119,15 +134,14 @@ files:
119
134
  - lib/ii_interactor.rb
120
135
  - lib/ii_interactor/base.rb
121
136
  - lib/ii_interactor/callbacks.rb
137
+ - lib/ii_interactor/coactors.rb
122
138
  - lib/ii_interactor/config.rb
123
139
  - lib/ii_interactor/context.rb
140
+ - lib/ii_interactor/core.rb
124
141
  - lib/ii_interactor/errors.rb
125
- - lib/ii_interactor/interaction.rb
126
- - lib/ii_interactor/loader.rb
127
- - lib/ii_interactor/lookup.rb
128
- - lib/ii_interactor/lookups/base.rb
129
- - lib/ii_interactor/lookups/name.rb
130
- - lib/ii_interactor/lookups/object.rb
142
+ - lib/ii_interactor/instrumentation.rb
143
+ - lib/ii_interactor/log_subscriber.rb
144
+ - lib/ii_interactor/variables.rb
131
145
  - lib/ii_interactor/version.rb
132
146
  homepage: https://github.com/kanety/ii_interactor
133
147
  licenses: []
@@ -147,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
161
  - !ruby/object:Gem::Version
148
162
  version: '0'
149
163
  requirements: []
150
- rubygems_version: 3.1.2
164
+ rubygems_version: 3.0.3
151
165
  signing_key:
152
166
  specification_version: 4
153
167
  summary: A base interactor to support management of bussiness logic
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IIInteractor
4
- module Interaction
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_interactions
9
- self._interactions = []
10
- class_attribute :_reactions
11
- self._reactions = []
12
- end
13
-
14
- class_methods do
15
- def interact(*interactors, **options, &block)
16
- if block
17
- self._interactions = _interactions + [block]
18
- elsif options[:before]
19
- index = self._interactions.index { |interaction| interaction == options[:before] }
20
- self._interactions = self._interactions.insert(index, *interactors)
21
- else
22
- self._interactions = _interactions + interactors
23
- end
24
- end
25
-
26
- def interactions
27
- self._interactions
28
- end
29
-
30
- def clear_interactions
31
- self._interactions = []
32
- end
33
-
34
- def react(*reactions)
35
- self._reactions = _reactions + reactions
36
- end
37
-
38
- def reactions
39
- self._reactions
40
- end
41
- end
42
- end
43
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IIInteractor
4
- module Loader
5
- class << self
6
- def call
7
- return unless defined?(Rails)
8
- return if Rails.env.production?
9
-
10
- engines = [Rails] + Rails::Engine.subclasses.map(&:instance)
11
- engines.each do |engine|
12
- Dir["#{engine.root}/app/interactors/**/*.rb"].each do |file|
13
- require file
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lookups/base'
4
- require_relative 'lookups/name'
5
- require_relative 'lookups/object'
6
-
7
- module IIInteractor
8
- module Lookup
9
- extend ActiveSupport::Concern
10
-
11
- def lookup_all
12
- lookup.map { |interactor| [interactor] + interactor.new(@context).lookup_all }.flatten
13
- end
14
-
15
- def lookup
16
- self.class._interactions.map do |interaction|
17
- if interaction.is_a?(Symbol) && respond_to?(interaction, true)
18
- send(interaction)
19
- elsif interaction.is_a?(Proc)
20
- instance_exec(&interaction)
21
- else
22
- interaction
23
- end
24
- end.flatten.compact.map do |interaction|
25
- if interaction.is_a?(Class) && interaction < IIInteractor::Base
26
- interaction
27
- else
28
- self.class.lookup(interaction)
29
- end
30
- end.flatten.compact
31
- end
32
-
33
- class_methods do
34
- def lookup(*interactions)
35
- interactions = _interactions unless interactions
36
- interactions.map { |interaction| Lookup.call(self, interaction) }.flatten
37
- end
38
- end
39
-
40
- class << self
41
- class_attribute :lookups
42
- self.lookups = [Lookups::Name, Lookups::Object]
43
-
44
- class_attribute :_cache
45
- self._cache = {}
46
-
47
- def call(klass, interaction)
48
- cache(klass, interaction) do
49
- lookup = lookups.detect { |lookup| lookup.call?(interaction) }
50
- lookup.new(klass, interaction).call if lookup
51
- end
52
- end
53
-
54
- private
55
-
56
- def cache(klass, interaction)
57
- if Config.lookup_cache
58
- self._cache[klass] ||= {}
59
- self._cache[klass][interaction] ||= yield
60
- else
61
- yield
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IIInteractor
4
- module Lookups
5
- class Base
6
- def initialize(klass, interaction)
7
- @klass = klass
8
- @interaction = interaction
9
- end
10
-
11
- def call
12
- end
13
-
14
- class << self
15
- def call?(interaction)
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IIInteractor
4
- module Lookups
5
- class Name < Base
6
- def call
7
- IIInteractor.load
8
- IIInteractor::Base.descendants.select do |interactor|
9
- interactor._reactions.any? { |reaction| reaction == @interaction }
10
- end
11
- end
12
-
13
- class << self
14
- def call?(interaction)
15
- interaction.is_a?(Symbol)
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IIInteractor
4
- module Lookups
5
- class Object < Base
6
- def call
7
- return if terminate?
8
-
9
- if @interaction.name.present? && (interactor = resolve)
10
- interactor
11
- elsif @interaction.superclass
12
- self.class.new(@klass, @interaction.superclass).call
13
- end
14
- end
15
-
16
- private
17
-
18
- def terminate?
19
- @interaction.name.to_s.in?(['Object', 'ActiveRecord::Base', 'ActiveModel::Base'])
20
- end
21
-
22
- def resolve
23
- name = resolve_name
24
- interactor = name.safe_constantize
25
- return interactor if interactor && name == interactor.name
26
- end
27
-
28
- def resolve_name
29
- namespace = @klass.name.to_s.sub(/Interactor$/, '').to_s
30
- [namespace, "#{@interaction.name}Interactor"].join('::')
31
- end
32
-
33
- class << self
34
- def call?(interaction)
35
- interaction.is_a?(Module)
36
- end
37
- end
38
- end
39
- end
40
- end