ii_interactor 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ca26ce09ce20cc37d4e35337c05a5f49abf260a648752efeb1ac0e887f85dd8c
4
+ data.tar.gz: 0b6f575ef339301f4501b5abd0104dc8a4ce083fba0fe9e0fae4f8cc09b0d023
5
+ SHA512:
6
+ metadata.gz: cc4b8a3cfa9b100299f91c17e04efa110c8c482dd9a2d1365003f962bf27fc3682822b5970453bbf9e6d19b6a4267639052aa7acdf23a9c38a24245cdabeb323
7
+ data.tar.gz: a882894b2f36b99e1aed993ae7bf888c8fc9261ec8f1c9f1a8f15ce39a67e374476dff2baa6c4a643e3d946a0fa7d8956aa91dcfcf26a1af4395cde0da466fd1
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-18.04
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
12
+ gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61']
13
+ exclude:
14
+ - ruby: 2.3
15
+ gemfile: rails60
16
+ - ruby: 2.3
17
+ gemfile: rails61
18
+ - ruby: 2.4
19
+ gemfile: rails60
20
+ - ruby: 2.4
21
+ gemfile: rails61
22
+ - ruby: 3.0
23
+ gemfile: rails50
24
+ - ruby: 3.0
25
+ gemfile: rails51
26
+ - ruby: 3.0
27
+ gemfile: rails52
28
+
29
+ name: ruby ${{ matrix.ruby }}, ${{ matrix.gemfile }}
30
+
31
+ env:
32
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
33
+
34
+ steps:
35
+ - uses: actions/checkout@v2
36
+ - uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: ${{ matrix.ruby }}
39
+ bundler-cache: true
40
+ - name: Run test
41
+ run: |
42
+ bundle exec rspec
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ .bundle/
2
+ .project
3
+ Gemfile.lock
4
+ coverage/
5
+ pkg/
6
+ tmp/
7
+ spec/dummy/db/*.sqlite3
8
+ spec/dummy/db/schema_*.rb
9
+ spec/dummy/log/*.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Yoshikazu Kaneta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # IIInteractor
2
+
3
+ A base interactor to support management of bussiness logic.
4
+
5
+ This gem is inspired by [interactor](https://github.com/collectiveidea/interactor) specs.
6
+
7
+ ## Dependencies
8
+
9
+ * ruby 2.3+
10
+ * activesupport 5.0+
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'ii_interactor'
18
+ ```
19
+
20
+ Then execute:
21
+
22
+ $ bundle
23
+
24
+ ## Usage
25
+
26
+ Create interactor with `call` method and call it as follows:
27
+
28
+ ```ruby
29
+ class Interactor < IIInteractor::Base
30
+ def call
31
+ @context.result = "called by #{@context.message}"
32
+ end
33
+ end
34
+
35
+ Interactor.call(message: 'something')
36
+ #=> #<IIInteractor::Context message="something", result="called by something">
37
+ ```
38
+
39
+ The first argument of `call` is set to `@context`.
40
+ The return value of `call` is the same as `@context`.
41
+
42
+ ### Callbacks
43
+
44
+ Following callbacks are available:
45
+
46
+ * `before_call`
47
+ * `around_call`
48
+ * `after_call`
49
+
50
+ For example:
51
+
52
+ ```ruby
53
+ class Interactor < IIInteractor::Base
54
+ before_call do
55
+ @message = @context.message
56
+ end
57
+
58
+ def call
59
+ puts @message
60
+ end
61
+ end
62
+
63
+ Interactor.call(message: 'something')
64
+ #=> something
65
+ ```
66
+
67
+ ### Interactions
68
+
69
+ You can call other interactors in the same context using `interact`:
70
+
71
+ ```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
87
+ end
88
+
89
+ MainInteractor.call
90
+ #=> AInteractor
91
+ # BInteractor
92
+ ```
93
+
94
+ #### Named interaction
95
+
96
+ You can also define named interactions.
97
+ The interactors to be called are looked up from all interactors.
98
+
99
+ ```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
110
+
111
+ def call
112
+ puts self.class.name
113
+ end
114
+ end
115
+
116
+ class MainInteractor < IIInteractor::Base
117
+ interact :some_name
118
+ end
119
+
120
+ MainInteractor.call
121
+ #=> AInteractor
122
+ # BInteractor
123
+ ```
124
+
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.
134
+
135
+ ```ruby
136
+ class A
137
+ end
138
+
139
+ class B
140
+ end
141
+
142
+ class Main::AInteractor < IIInteractor::Base
143
+ def call
144
+ puts self.class.name
145
+ end
146
+ end
147
+
148
+ class Main::BInteractor < IIInteractor::Base
149
+ def call
150
+ puts self.class.name
151
+ end
152
+ end
153
+
154
+ class MainInteractor < IIInteractor::Base
155
+ interact A
156
+ interact B
157
+ end
158
+
159
+ MainInteractor.call
160
+ #=> Main::AInteractor
161
+ # Main::BInteractor
162
+ ```
163
+
164
+ #### Custom interaction
165
+
166
+ You can also customize lookup of interactors as follows:
167
+
168
+ ```ruby
169
+ class AInteractor < IIInteractor::Base
170
+ def call
171
+ puts self.class.name
172
+ end
173
+ end
174
+
175
+ class BInteractor < IIInteractor::Base
176
+ 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
200
+ end
201
+ end
202
+
203
+ MainInteractor.call(condition: 'A')
204
+ #=> AInteractor
205
+
206
+ MainInteractor.call(condition: 'B')
207
+ #=> BInteractor
208
+ ```
209
+
210
+ #### Nested interaction
211
+
212
+ You can define nested interactions as follows:
213
+
214
+ ```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
+ class AInteractor < IIInteractor::Base
228
+ interact NestedAInteractor
229
+
230
+ def call
231
+ puts self.class.name
232
+ end
233
+ end
234
+
235
+ class BInteractor < IIInteractor::Base
236
+ interact NestedBInteractor
237
+
238
+ def call
239
+ puts self.class.name
240
+ end
241
+ end
242
+
243
+ class MainInteractor < IIInteractor::Base
244
+ interact AInteractor
245
+ interact BInteractor
246
+ end
247
+
248
+ MainInteractor.call
249
+ #=> NestedAInteractor
250
+ # AInteractor
251
+ # NestedBInteractor
252
+ # BInteractor
253
+ ```
254
+
255
+ ### Stop interactions
256
+
257
+ You can stop interactions as follows:
258
+
259
+ ```ruby
260
+ class AInteractor < IIInteractor::Base
261
+ def call
262
+ puts self.class.name
263
+ stop!(message: "something happened!")
264
+ end
265
+ end
266
+
267
+ class BInteractor < IIInteractor::Base
268
+ def call
269
+ puts self.class.name
270
+ end
271
+ end
272
+
273
+ class MainInteractor < IIInteractor::Base
274
+ interact AInteractor
275
+ interact BInteractor
276
+ end
277
+
278
+ context = MainInteractor.call
279
+ #=> AInteractor
280
+
281
+ context.message
282
+ #=> something happened!
283
+
284
+ context.stopped?
285
+ #=> true
286
+
287
+ context.success?
288
+ #=> true
289
+ ```
290
+
291
+ ### Fail interactions
292
+
293
+ You can fail interactions and rollback called interactors as follows:
294
+
295
+ ```ruby
296
+ class AInteractor < IIInteractor::Base
297
+ def call
298
+ puts self.class.name
299
+ end
300
+
301
+ def rollback
302
+ puts "rollback #{self.class.name}"
303
+ end
304
+ end
305
+
306
+ class BInteractor < IIInteractor::Base
307
+ def call
308
+ fail!(message: "something happened!")
309
+ end
310
+
311
+ def rollback
312
+ puts "rollback #{self.class.name}"
313
+ end
314
+ end
315
+
316
+ class MainInteractor < IIInteractor::Base
317
+ interact AInteractor
318
+ interact BInteractor
319
+ end
320
+
321
+ context = MainInteractor.call
322
+ #=> AInteractor
323
+ # rollback AInteractor
324
+
325
+ context.message
326
+ #=> something happened!
327
+
328
+ context.failure?
329
+ #=> true
330
+ ```
331
+
332
+ ### Pass a block
333
+
334
+ You can pass a block to `call` method of a interactor.
335
+ The block is kept in the context and you can call it by `inform` as you like:
336
+
337
+ ```ruby
338
+ class AInteractor < IIInteractor::Base
339
+ def call
340
+ inform('called A')
341
+ end
342
+ end
343
+
344
+ class BInteractor < IIInteractor::Base
345
+ def call
346
+ inform('called B')
347
+ end
348
+ end
349
+
350
+ class MainInteractor < IIInteractor::Base
351
+ interact AInteractor
352
+ interact BInteractor
353
+ end
354
+
355
+ MainInteractor.call do |interactor, message|
356
+ puts "#{interactor.class}: #{message}"
357
+ end
358
+ #=> AInteractor: called A
359
+ # BInteractor: called B
360
+ ```
361
+
362
+ ## Contributing
363
+
364
+ Bug reports and pull requests are welcome at https://github.com/kanety/ii_interactor.
365
+
366
+ ## License
367
+
368
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ii_interactor"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 5.0.0"
4
+ gem "sqlite3", "~> 1.3.6"
5
+
6
+ gemspec path: "../"
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 5.1.0"
4
+ gem "sqlite3", "~> 1.3.6"
5
+
6
+ gemspec path: "../"
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 5.2.0"
4
+ gem "sqlite3", "~> 1.3.6"
5
+
6
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 6.0.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 6.1.0"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ii_interactor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ii_interactor"
8
+ spec.version = IIInteractor::VERSION
9
+ spec.authors = ["Yoshikazu Kaneta"]
10
+ spec.email = ["kaneta@sitebridge.co.jp"]
11
+ spec.summary = %q{A base interactor to support management of bussiness logic}
12
+ spec.description = %q{A base interactor to support management of bussiness logic}
13
+ spec.homepage = "https://github.com/kanety/ii_interactor"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "activesupport", ">= 5.0"
21
+
22
+ spec.add_development_dependency "rails", ">= 5.0"
23
+ spec.add_development_dependency "sqlite3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec-rails"
26
+ spec.add_development_dependency "simplecov"
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_support'
2
+
3
+ require 'ii_interactor/version'
4
+ require 'ii_interactor/errors'
5
+ require 'ii_interactor/config'
6
+ require 'ii_interactor/base'
7
+ require 'ii_interactor/loader'
8
+
9
+ module IIInteractor
10
+ class << self
11
+ def configure
12
+ yield Config
13
+ end
14
+
15
+ def config
16
+ Config
17
+ end
18
+
19
+ def load
20
+ Loader.call
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context'
4
+ require_relative 'callbacks'
5
+ require_relative 'interaction'
6
+ require_relative 'lookup'
7
+
8
+ module IIInteractor
9
+ class Base
10
+ 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
75
+ end
76
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ module Callbacks
5
+ extend ActiveSupport::Concern
6
+ include ActiveSupport::Callbacks
7
+
8
+ included do
9
+ define_callbacks :call
10
+ end
11
+
12
+ class_methods do
13
+ def before_call(*args, &block)
14
+ set_callback(:call, :before, *args, &block)
15
+ end
16
+
17
+ def after_call(*args, &block)
18
+ set_callback(:call, :after, *args, &block)
19
+ end
20
+
21
+ def around_call(*args, &block)
22
+ set_callback(:call, :around, *args, &block)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ class Config
5
+ class_attribute :data
6
+
7
+ self.data = {
8
+ lookup_cache: true
9
+ }
10
+
11
+ data.keys.each do |key|
12
+ define_singleton_method "#{key}" do
13
+ data[key]
14
+ end
15
+
16
+ define_singleton_method "#{key}=" do |val|
17
+ data[key] = val
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ class Context < OpenStruct
5
+ def initialize(hash, &block)
6
+ super
7
+ self[:_block] = block
8
+ self[:_failed] = false
9
+ self[:_stopped] = false
10
+ self[:_planned] = []
11
+ self[:_called] = []
12
+ end
13
+
14
+ def success?
15
+ !failure?
16
+ end
17
+
18
+ def failure?
19
+ self[:_failed] == true
20
+ end
21
+
22
+ def stopped?
23
+ self[:_stopped] == true
24
+ end
25
+
26
+ def fail!(data = {})
27
+ self[:_failed] = true
28
+ data.each { |k, v| self[k] = v }
29
+ end
30
+
31
+ def stop!(data = {})
32
+ self[:_stopped] = true
33
+ data.each { |k, v| self[k] = v }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ class UnprogressableError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,43 @@
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
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,66 @@
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
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IIInteractor
4
+ VERSION = '1.0.0'
5
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ii_interactor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yoshikazu Kaneta
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-07-13 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: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
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
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: A base interactor to support management of bussiness logic
98
+ email:
99
+ - kaneta@sitebridge.co.jp
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".github/workflows/ci.yml"
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - Gemfile
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - gemfiles/rails50.gemfile
114
+ - gemfiles/rails51.gemfile
115
+ - gemfiles/rails52.gemfile
116
+ - gemfiles/rails60.gemfile
117
+ - gemfiles/rails61.gemfile
118
+ - ii_interactor.gemspec
119
+ - lib/ii_interactor.rb
120
+ - lib/ii_interactor/base.rb
121
+ - lib/ii_interactor/callbacks.rb
122
+ - lib/ii_interactor/config.rb
123
+ - lib/ii_interactor/context.rb
124
+ - 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
131
+ - lib/ii_interactor/version.rb
132
+ homepage: https://github.com/kanety/ii_interactor
133
+ licenses: []
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.1.2
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: A base interactor to support management of bussiness logic
154
+ test_files: []