ii_interactor 1.0.0 → 2.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.
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