coactive 0.1.2 → 0.2.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: ec350eb0f4e954f1fa493547fcea1bbc8bef66b7968ae5cc56c8d4028136d84a
4
- data.tar.gz: fb1fd1da81b027631dea81b1a03ee4295837c06bd1ef8332fb355529cf4120cd
3
+ metadata.gz: be01c5f37abbfc9ccf4a885e3eefdaf6416bdf2f04c08e29194acd4253a6a222
4
+ data.tar.gz: f65e89cca406a0d1a34ee056f3bbf3e8fb2dc4e12d0710007fd31c5c8d249b03
5
5
  SHA512:
6
- metadata.gz: 8b5238f3adc94100d21684d3fbec31d917e1ff69a08931a2b7fb11a9380f07b9257a0c17f958633f2cdd0477d3c3b2aa9de3cd97ca3aaa95d0aeac9ee10895f0
7
- data.tar.gz: d71895d84b2ee93ddc5778e1406b8c3b1aee756eb31f0d01d84c2d4e84f1134595812dc34ab21e4c22ee7868c23e4a6134076ce236b9b04554b51b0002cd4943
6
+ metadata.gz: a1290b6da95ab7806fa2eabd3bf1bd904bfb7536a7bb7d9b8ffa7d70e66e593ad91f211cb247d37f8a291d21fe98fb890c835b6a2a4a9d09057da8b5ff59f738
7
+ data.tar.gz: 6db5aad85ac64b5a398e892fdfe652448cecbf56a30f3c2352929ac7d138348d69afb109621ee7e105058c43d82649e53c32787d224eab8244bdb83e251b4381
@@ -4,21 +4,29 @@ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  test:
7
- runs-on: ubuntu-18.04
7
+ runs-on: ubuntu-20.04
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
12
- gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61']
11
+ ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0']
12
+ gemfile: ['rails50', 'rails51', 'rails52', 'rails60', 'rails61', 'rails70']
13
13
  exclude:
14
14
  - ruby: 2.3
15
15
  gemfile: rails60
16
16
  - ruby: 2.3
17
17
  gemfile: rails61
18
+ - ruby: 2.3
19
+ gemfile: rails70
18
20
  - ruby: 2.4
19
21
  gemfile: rails60
20
22
  - ruby: 2.4
21
23
  gemfile: rails61
24
+ - ruby: 2.4
25
+ gemfile: rails70
26
+ - ruby: 2.5
27
+ gemfile: rails70
28
+ - ruby: 2.6
29
+ gemfile: rails70
22
30
  - ruby: 3.0
23
31
  gemfile: rails50
24
32
  - ruby: 3.0
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  .bundle/
2
2
  Gemfile.lock
3
3
  coverage/
4
+ gemfiles/*.lock
4
5
  pkg/
5
6
  tmp/
6
7
  spec/dummy/db/*.sqlite3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.2.0
4
+
5
+ * Add context feature.
6
+ * Add priority to coaction.
7
+
3
8
  ## 0.1.2
4
9
 
5
10
  * Clear lookup cache when reloaded.
data/README.md CHANGED
@@ -21,6 +21,8 @@ Then execute:
21
21
 
22
22
  ## Usage
23
23
 
24
+ ### Coactors
25
+
24
26
  Include `Coactive::Base` to your base class:
25
27
 
26
28
  ```ruby
@@ -51,7 +53,7 @@ C.new.coactors
51
53
  # => [A, B]
52
54
  ```
53
55
 
54
- ### Named coactors
56
+ #### Named coactors
55
57
 
56
58
  You can also define coactive classes by using specific name:
57
59
 
@@ -88,30 +90,30 @@ class Base
88
90
  end
89
91
  ```
90
92
 
91
- ### Object-based coactors
93
+ #### Object-based coactors
92
94
 
93
95
  You can also define coactive classes by using object:
94
96
 
95
97
  ```ruby
96
- class ModelA
98
+ class ItemA
97
99
  end
98
100
 
99
- class ModelB
101
+ class ItemB
100
102
  end
101
103
 
102
- class Base::ModelA < Base
104
+ class Base::ItemA < Base
103
105
  end
104
106
 
105
- class Base::ModelB < Base
107
+ class Base::ItemB < Base
106
108
  end
107
109
 
108
110
  class Base::C < Base
109
- coact ModelA
110
- coact ModelB
111
+ coact ItemA
112
+ coact ItemB
111
113
  end
112
114
 
113
115
  Base::C.new.coactors
114
- #=> [Base::ModelA, Base::ModelB]
116
+ #=> [Base::ItemA, Base::ItemB]
115
117
  ```
116
118
 
117
119
  Coactors are looked up from the namespace corresponding with caller classes.
@@ -130,7 +132,7 @@ class Base
130
132
  end
131
133
  ```
132
134
 
133
- ### Dynamic coactors
135
+ #### Dynamic coactors
134
136
 
135
137
  You can also dynamically lookup coactors by using block or instance method:
136
138
 
@@ -184,7 +186,7 @@ D.new('B').coactors
184
186
  #=> [B]
185
187
  ```
186
188
 
187
- ### Nested coactors
189
+ #### Nested coactors
188
190
 
189
191
  You can define nested coactors. For example:
190
192
 
@@ -212,6 +214,110 @@ C.new.coactors.map { |klass| [klass] + klass.new.coactors }.flatten
212
214
  #=> [A, NestedA, B, NestedB]
213
215
  ```
214
216
 
217
+ ### Context
218
+
219
+ You can define variables used in a coactor as a context by including `Coactive::Initializer`.
220
+ The variables are stored in `context` as follows:
221
+
222
+ ```ruby
223
+ class Base
224
+ include Coactive::Base
225
+ include Coactive::Initializer
226
+ end
227
+
228
+ class A < Base
229
+ context :input
230
+ end
231
+
232
+ coactor = A.new(input: 'something')
233
+ coactor.context.input
234
+ #=> something
235
+ ```
236
+
237
+ #### Required context
238
+
239
+ You can also define required context as follows:
240
+
241
+ ```ruby
242
+ class A < Base
243
+ context :input, required: true
244
+ end
245
+
246
+ A.new
247
+ #=> Coactive::MissingContextError (missing required context: input)
248
+ ```
249
+
250
+ #### Default value
251
+
252
+ You can also define default value as follows:
253
+
254
+ ```ruby
255
+ class A < Base
256
+ context :input, default: 'something'
257
+ end
258
+
259
+ coactor = A.new
260
+ coactor.context.input
261
+ #=> something
262
+ ```
263
+
264
+ ### Contextualizer
265
+
266
+ You can copy context variables to a coactor by calling `contextualize`:
267
+
268
+ ```ruby
269
+ class Base
270
+ include Coactive::Base
271
+ include Coactive::Initializer
272
+ include Coactive::Contextualizer
273
+ end
274
+
275
+ class A < Base
276
+ context :input
277
+ end
278
+
279
+ coactor = A.new(input: 'something')
280
+ coactor.contextualize
281
+ coactor.input
282
+ #=> something
283
+ coactor.instance_variable_get(:@input)
284
+ #=> something
285
+ ```
286
+
287
+ #### Output
288
+
289
+ You can also set context when finished `contextualize` block:
290
+
291
+ ```ruby
292
+ class A < Base
293
+ context :result, output: true
294
+
295
+ def call
296
+ @result = 'something'
297
+ end
298
+ end
299
+
300
+ coactor = A.new
301
+ coactor.contextualize { coactor.call }
302
+ coactor.context.result
303
+ #=> something
304
+ ```
305
+
306
+ #### Output return value
307
+
308
+ You can also set context from return value of `contextualize` block:
309
+
310
+ ```ruby
311
+ class A < Base
312
+ context :result, output: :return
313
+ end
314
+
315
+ coactor = A.new
316
+ coactor.contextualize { 'return value' }
317
+ coactor.context.result
318
+ #=> return value
319
+ ```
320
+
215
321
  ### Configuration
216
322
 
217
323
  You can set configurations in your base class as follows:
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 7.0.0"
4
+
5
+ gemspec path: "../"
data/lib/coactive/base.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'configure'
4
- require_relative 'coact'
5
- require_relative 'coaction'
6
- require_relative 'coactors'
7
- require_relative 'loader'
4
+ require_relative 'coactor'
5
+ require_relative 'interface'
6
+ require_relative 'errors'
8
7
 
9
8
  module Coactive
10
9
  module Base
@@ -12,9 +11,7 @@ module Coactive
12
11
 
13
12
  included do
14
13
  include Configure
15
- include Coact
16
- include Coaction
17
- include Coactors
14
+ include Coactor
18
15
  end
19
16
  end
20
17
  end
@@ -1,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'loader'
3
4
  require_relative 'lookup'
5
+ require_relative 'coactors/coactants'
6
+ require_relative 'coactors/coactions'
4
7
 
5
8
  module Coactive
6
- module Coactors
9
+ module Coactor
7
10
  extend ActiveSupport::Concern
11
+ include Coactors::Coactants
12
+ include Coactors::Coactions
8
13
 
9
14
  def coactors
10
15
  self.class._coactants.map do |coactant|
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Coactors
5
+ module Coactants
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_coactants
10
+ self._coactants = []
11
+ end
12
+
13
+ class_methods do
14
+ def coact(*coactants, **options, &block)
15
+ if block
16
+ self._coactants = _coactants + [block]
17
+ elsif options[:before]
18
+ index = self._coactants.index { |coactant| coactant == options[:before] }
19
+ self._coactants = self._coactants.insert(index, *coactants)
20
+ else
21
+ self._coactants = _coactants + coactants
22
+ end
23
+ end
24
+
25
+ def coactants
26
+ self._coactants
27
+ end
28
+
29
+ def clear_coactants
30
+ self._coactants = []
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Coactors
5
+ class Coaction < Struct.new(:name, :options)
6
+ def priority
7
+ options[:priority]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'coaction'
4
+
5
+ module Coactive
6
+ module Coactors
7
+ module Coactions
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_coactions
12
+ self._coactions = []
13
+ end
14
+
15
+ class_methods do
16
+ def coaction(*names, **options)
17
+ self._coactions = _coactions + names.map { |name| Coaction.new(name, options) }
18
+ end
19
+
20
+ def coactions
21
+ self._coactions
22
+ end
23
+
24
+ def clear_coactions
25
+ self._coactions = []
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ class Context
5
+ attr_reader :_data
6
+
7
+ def initialize(data = {}, &block)
8
+ data = data.to_h if data.respond_to?(:to_h)
9
+ @_data = data
10
+ end
11
+
12
+ def [](key)
13
+ @_data[key]
14
+ end
15
+
16
+ def []=(key, value)
17
+ @_data[key] = value
18
+ end
19
+
20
+ def key?(key)
21
+ @_data.key?(key)
22
+ end
23
+
24
+ def to_h
25
+ @_data
26
+ end
27
+
28
+ def to_s
29
+ attrs = @_data.map { |k, v| "#{k}=#{v.inspect}" }.join(', ')
30
+ "#<#{self.class} #{attrs}>"
31
+ end
32
+ alias :inspect :to_s
33
+
34
+ def define_accessors!(keys)
35
+ Array(keys).each do |key|
36
+ define_singleton_method key do
37
+ @_data[key]
38
+ end
39
+ define_singleton_method "#{key}=" do |value|
40
+ @_data[key] = value
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Contextualizer
5
+ extend ActiveSupport::Concern
6
+
7
+ def contextualize
8
+ self.class._contexts.each do |var|
9
+ instance_variable_set("@#{var.name}", @context[var.name])
10
+ define_singleton_method var.name do
11
+ instance_variable_get("@#{var.name}")
12
+ end
13
+ end
14
+
15
+ return_value = yield if block_given?
16
+
17
+ self.class._contexts.each do |var|
18
+ if var.output_return?
19
+ @context[var.name] = return_value
20
+ elsif var.output? && instance_variable_defined?("@#{var.name}")
21
+ @context[var.name] = instance_variable_get("@#{var.name}")
22
+ end
23
+ end
24
+
25
+ return_value
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ class MissingContextError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context'
4
+ require_relative 'initializers/contexts'
5
+
6
+ module Coactive
7
+ module Initializer
8
+ extend ActiveSupport::Concern
9
+ include Initializers::Contexts
10
+
11
+ included do
12
+ class_attribute :context_class
13
+ self.context_class = Context
14
+ attr_reader :context
15
+ end
16
+
17
+ def initialize(args = {}, &block)
18
+ @context = self.class.context_class.new(args, &block)
19
+ @context.define_accessors!(self.class._contexts.map(&:name))
20
+
21
+ self.class._contexts.each do |var|
22
+ if var.required? && !@context.key?(var.name)
23
+ raise MissingContextError.new("missing required context: #{var.name}")
24
+ end
25
+ if var.default && !@context.key?(var.name)
26
+ @context[var.name] = Initializer.resolve(self, var.default)
27
+ end
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def resolve(instance, value)
33
+ if value.respond_to?(:call)
34
+ instance.instance_exec(&value)
35
+ elsif value.is_a?(Symbol) && instance.respond_to?(value, true)
36
+ instance.send(value)
37
+ else
38
+ value.deep_dup
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'variable'
4
+
5
+ module Coactive
6
+ module Initializers
7
+ module Contexts
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_contexts
12
+ self._contexts = []
13
+ end
14
+
15
+ class_methods do
16
+ def context(*names, **options)
17
+ self._contexts = self._contexts + names.map { |name| Variable.new(name, options) }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coactive
4
+ module Initializers
5
+ class Variable < Struct.new(:name, :options)
6
+ def default
7
+ options[:default] if options
8
+ end
9
+
10
+ def required?
11
+ options[:required] if options
12
+ end
13
+
14
+ def output?
15
+ options[:output] if options
16
+ end
17
+
18
+ def output_return?
19
+ options[:output] == :return if options
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'initializer'
4
+ require_relative 'contextualizer'
5
+
6
+ module Coactive
7
+ module Interface
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ include Initializer
12
+ include Contextualizer
13
+ end
14
+ end
15
+ end
@@ -15,9 +15,10 @@ module Coactive
15
15
  end
16
16
 
17
17
  def lookup
18
- @klass.coactive_config.base_class.descendants.select do |coactor|
19
- coactor._coactions.any? { |coaction| coaction == @coactant }
20
- end
18
+ @klass.coactive_config.base_class.descendants.each_with_object({}).with_index do |(coactor, hash), i|
19
+ coaction = coactor.coactions.detect { |coaction| coaction.name == @coactant }
20
+ hash[[coaction.priority || 1 << 63, i]] = coactor if coaction
21
+ end.sort.map { |x| x[1] }
21
22
  end
22
23
 
23
24
  class << self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coactive
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.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-12-13 00:00:00.000000000 Z
11
+ date: 2022-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -117,13 +117,22 @@ files:
117
117
  - gemfiles/rails52.gemfile
118
118
  - gemfiles/rails60.gemfile
119
119
  - gemfiles/rails61.gemfile
120
+ - gemfiles/rails70.gemfile
120
121
  - lib/coactive.rb
121
122
  - lib/coactive/base.rb
122
- - lib/coactive/coact.rb
123
- - lib/coactive/coaction.rb
124
- - lib/coactive/coactors.rb
123
+ - lib/coactive/coactor.rb
124
+ - lib/coactive/coactors/coactants.rb
125
+ - lib/coactive/coactors/coaction.rb
126
+ - lib/coactive/coactors/coactions.rb
125
127
  - lib/coactive/config.rb
126
128
  - lib/coactive/configure.rb
129
+ - lib/coactive/context.rb
130
+ - lib/coactive/contextualizer.rb
131
+ - lib/coactive/errors.rb
132
+ - lib/coactive/initializer.rb
133
+ - lib/coactive/initializers/contexts.rb
134
+ - lib/coactive/initializers/variable.rb
135
+ - lib/coactive/interface.rb
127
136
  - lib/coactive/loader.rb
128
137
  - lib/coactive/lookup.rb
129
138
  - lib/coactive/lookups/base.rb
@@ -149,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
158
  - !ruby/object:Gem::Version
150
159
  version: '0'
151
160
  requirements: []
152
- rubygems_version: 3.0.3
161
+ rubygems_version: 3.1.2
153
162
  signing_key:
154
163
  specification_version: 4
155
164
  summary: Make classes coactive
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coactive
4
- module Coact
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_coactants
9
- self._coactants = []
10
- end
11
-
12
- class_methods do
13
- def coact(*coactants, **options, &block)
14
- if block
15
- self._coactants = _coactants + [block]
16
- elsif options[:before]
17
- index = self._coactants.index { |coactant| coactant == options[:before] }
18
- self._coactants = self._coactants.insert(index, *coactants)
19
- else
20
- self._coactants = _coactants + coactants
21
- end
22
- end
23
-
24
- def coactants
25
- self._coactants
26
- end
27
-
28
- def clear_coactants
29
- self._coactants = []
30
- end
31
- end
32
- end
33
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coactive
4
- module Coaction
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
- class_attribute :_coactions
9
- self._coactions = []
10
- end
11
-
12
- class_methods do
13
- def coaction(*coactions)
14
- self._coactions = _coactions + coactions
15
- end
16
-
17
- def coactions
18
- self._coactions
19
- end
20
-
21
- def clear_coactions
22
- self._coactions = []
23
- end
24
- end
25
- end
26
- end