nina 0.1.6 → 0.2.1
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +39 -4
- data/lib/nina/builder/callbacks.rb +10 -1
- data/lib/nina/builder/initialization.rb +10 -9
- data/lib/nina/builder.rb +25 -28
- data/lib/nina/version.rb +1 -1
- data/lib/nina.rb +15 -7
- metadata +2 -3
- data/lib/nina/assembler.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2552f792c064a8dc7fc6916b07f0741d696916f7b844bb0fb12db6f6db083680
|
4
|
+
data.tar.gz: 489c9ad648b20a0e2b48704dc87350011682b1f905354681a85cfb24e6217cbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf06ff35a6512ed258d44e589b8015f09c28ffaa22eacca7cc162ef5cb623566f4cf72d38a13d651bb599f9407555847a0a001cf79d00235169f5664259e3060
|
7
|
+
data.tar.gz: 34b5f8ea7925272bcf9f4edd7b8ef4fe1221dbc27cc6e261ce283806dc87c58d290325976cfe6d059b5f334a6f27c5d5ae16bf9e6ebd3cc80e9b904ffd1bf30e
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -65,22 +65,41 @@ Lets explore what we have as a result
|
|
65
65
|
```ruby
|
66
66
|
# Wrapping strategy
|
67
67
|
builder = abstract_factory.main_builder
|
68
|
-
instance = builder.wrap
|
68
|
+
instance = builder.wrap do |build|
|
69
|
+
# This block controlls order of building process steps
|
70
|
+
# Allows you to provide initialization attributes
|
71
|
+
# And specify only things you need to be added
|
72
|
+
build.params # The most nested object;
|
73
|
+
q = build.query # query will get a reader to params
|
74
|
+
build.command if i_need_this? # Top level object; command will get a reader to query
|
75
|
+
build.query == q # memoization do not allow creation of objects multiple times
|
76
|
+
build.query(1, 2, 3) # instead it returns first object no matter what parameters you provided later
|
77
|
+
end
|
69
78
|
instance # => #<Command>
|
70
79
|
instance.query # => #<Query>
|
71
80
|
instance.query.params # => #<Params>
|
72
81
|
|
73
82
|
# Nesting strategy
|
74
83
|
builder = abstract_factory.secondary_builder
|
75
|
-
instance = builder.nest
|
84
|
+
instance = builder.nest do |build|
|
85
|
+
build.params # Top level object
|
86
|
+
build.query # query will get a reader to params
|
87
|
+
build.command # The most nested object; query will get a reader to command
|
88
|
+
end
|
76
89
|
instance # => #<A>
|
77
90
|
instance.query # => #<B>
|
78
91
|
instance.query.command # => #<C>
|
79
92
|
```
|
80
|
-
|
93
|
+
|
94
|
+
### Delegation
|
95
|
+
We may apply delegation techique from OOD to expose methods of deeper layers
|
81
96
|
```ruby
|
82
97
|
builder = abstract_factory.secondary_builder
|
83
|
-
instance = builder.nest(delegate: true)
|
98
|
+
instance = builder.nest(delegate: true) do |build|
|
99
|
+
build.params
|
100
|
+
build.query
|
101
|
+
build.command
|
102
|
+
end
|
84
103
|
instance.a # => nil
|
85
104
|
instance.b # => nil
|
86
105
|
instance.c # => nil
|
@@ -89,6 +108,7 @@ instance.query.c # => nil
|
|
89
108
|
If you need provide an initalization parameters for the objects
|
90
109
|
```ruby
|
91
110
|
instance = builder.wrap(delegate: true) do |b|
|
111
|
+
# b.params(1, 2) => ArgumentError
|
92
112
|
b.params(1)
|
93
113
|
b.query(2)
|
94
114
|
b.command(3)
|
@@ -97,6 +117,21 @@ instance.a # => 1
|
|
97
117
|
instance.b # => 2
|
98
118
|
instance.c # => 3
|
99
119
|
```
|
120
|
+
|
121
|
+
### Top level API
|
122
|
+
If you have some objects and just want to link them you can use following methods
|
123
|
+
```ruby
|
124
|
+
setup = { params: params, query: query, command: command }
|
125
|
+
Nina.link(setup, delegate: true) do |name, object| # => params.query.command
|
126
|
+
# optionally do something
|
127
|
+
end
|
128
|
+
|
129
|
+
Nina.reverse_link(setup, delegate: false) do |name, object| # => command.query.params
|
130
|
+
# optionally do something
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
### Callbacks
|
100
135
|
To do something between stages (after creation of object)
|
101
136
|
```ruby
|
102
137
|
builder_with_callbacks = builder.with_callbacks do |c|
|
@@ -3,7 +3,12 @@
|
|
3
3
|
module Nina
|
4
4
|
class Builder
|
5
5
|
# Utility to get user defined callbacks
|
6
|
-
class Callbacks
|
6
|
+
class Callbacks
|
7
|
+
def initialize(allow_list, atts = {})
|
8
|
+
@allow_list = allow_list
|
9
|
+
@atts = atts
|
10
|
+
end
|
11
|
+
|
7
12
|
def copy
|
8
13
|
Callbacks.new(@allow_list, to_h.dup)
|
9
14
|
end
|
@@ -19,6 +24,10 @@ module Nina
|
|
19
24
|
def respond_to_missing?(method, include_private = false)
|
20
25
|
super
|
21
26
|
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
@atts
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
24
33
|
end
|
@@ -4,25 +4,26 @@ module Nina
|
|
4
4
|
class Builder
|
5
5
|
# A way to call methods from initalization proc on base_class
|
6
6
|
class Initialization < BasicObject
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@allow_list =
|
11
|
-
@atts =
|
7
|
+
def initialize(builder)
|
8
|
+
@builder = builder
|
9
|
+
@abstract_factory = builder.abstract_factory
|
10
|
+
@allow_list = @abstract_factory.factories.keys
|
11
|
+
@atts = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
def method_missing(method, *args, **kwargs, &block)
|
15
15
|
return super unless @allow_list.include?(method)
|
16
16
|
|
17
|
-
@atts[method]
|
17
|
+
@atts[method] ||= @abstract_factory.create(method, *args, **kwargs, &block)
|
18
|
+
.tap { |o| @builder.send(:update, method, o) }
|
18
19
|
end
|
19
20
|
|
20
|
-
def respond_to_missing?(method,
|
21
|
-
@allow_list.include?(method)
|
21
|
+
def respond_to_missing?(method, _include_private = false)
|
22
|
+
@allow_list.include?(method)
|
22
23
|
end
|
23
24
|
|
24
25
|
def to_h
|
25
|
-
@atts
|
26
|
+
@atts.dup
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
data/lib/nina/builder.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'nina/builder/initialization'
|
4
|
+
require 'nina/builder/callbacks'
|
5
|
+
|
3
6
|
# This should be a kind of factory that creates complex objects
|
4
7
|
# from simple ones. It should use torirori to create objects.
|
5
8
|
# It also enriches objects with some methods that make them more
|
@@ -11,21 +14,7 @@ module Nina
|
|
11
14
|
|
12
15
|
# Definaes support methods and variables for concrete builder
|
13
16
|
module ClassMethods
|
14
|
-
def build_order_list
|
15
|
-
@build_order_list ||= []
|
16
|
-
end
|
17
|
-
|
18
|
-
def build_order_list=(other)
|
19
|
-
@build_order_list = other.dup.freeze
|
20
|
-
end
|
21
|
-
|
22
|
-
def inherited(subclass)
|
23
|
-
super
|
24
|
-
subclass.build_order_list = build_order_list.dup.freeze
|
25
|
-
end
|
26
|
-
|
27
17
|
def factory(name, *args, **kwargs, &block)
|
28
|
-
build_order_list << name
|
29
18
|
super
|
30
19
|
define_singleton_method(name) do |klass = nil, &definition|
|
31
20
|
factories[__method__].subclass(produces: klass, &definition)
|
@@ -38,9 +27,8 @@ module Nina
|
|
38
27
|
@def_block = def_block
|
39
28
|
@abstract_factory = abstract_factory.include(Toritori).extend(ClassMethods)
|
40
29
|
@abstract_factory.class_eval(&def_block) if def_block
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@assembler.add_observer(self)
|
30
|
+
@initialization = Builder::Initialization.new(self)
|
31
|
+
@callbacks = callbacks&.copy || Callbacks.new(@abstract_factory.factories.keys)
|
44
32
|
@observers = []
|
45
33
|
end
|
46
34
|
|
@@ -49,27 +37,29 @@ module Nina
|
|
49
37
|
end
|
50
38
|
|
51
39
|
def copy
|
52
|
-
new_builder = self.class.new(name, abstract_factory: abstract_factory, callbacks: @
|
40
|
+
new_builder = self.class.new(name, abstract_factory: abstract_factory, callbacks: @callbacks)
|
53
41
|
@observers.each { |observer| new_builder.add_observer(observer) }
|
54
42
|
new_builder
|
55
43
|
end
|
56
44
|
|
57
45
|
def with_callbacks(&block)
|
58
|
-
yield @
|
46
|
+
yield @callbacks if block
|
59
47
|
|
60
48
|
copy
|
61
49
|
end
|
62
50
|
|
63
|
-
def
|
64
|
-
|
51
|
+
def nest(delegate: false, &block)
|
52
|
+
initialization = Builder::Initialization.new(self)
|
53
|
+
yield initialization if block
|
65
54
|
|
66
|
-
|
55
|
+
Nina.link(initialization.to_h, delegate: delegate)
|
67
56
|
end
|
68
57
|
|
69
|
-
def
|
70
|
-
|
58
|
+
def wrap(delegate: false, &block)
|
59
|
+
initialization = Builder::Initialization.new(self)
|
60
|
+
yield initialization if block
|
71
61
|
|
72
|
-
|
62
|
+
Nina.reverse_link(initialization.to_h, delegate: delegate)
|
73
63
|
end
|
74
64
|
|
75
65
|
def subclass(&def_block)
|
@@ -77,12 +67,19 @@ module Nina
|
|
77
67
|
|
78
68
|
@abstract_factory = Class.new(abstract_factory)
|
79
69
|
@abstract_factory.class_eval(&def_block)
|
80
|
-
@abstract_factory.build_order_list
|
81
|
-
|
82
|
-
|
70
|
+
@callbacks = callbacks&.copy || Callbacks.new(@abstract_factory.build_order_list)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def callbacks_for(name)
|
76
|
+
return [] unless @callbacks
|
77
|
+
|
78
|
+
@callbacks.to_h.fetch(name, [])
|
83
79
|
end
|
84
80
|
|
85
81
|
def update(name, object)
|
82
|
+
callbacks_for(name).each { |c| c.call(object) }
|
86
83
|
@observers.each do |observer|
|
87
84
|
observer.public_send(:"on_#{name}_created", object, @name) if observer.respond_to?(:"on_#{name}_created")
|
88
85
|
end
|
data/lib/nina/version.rb
CHANGED
data/lib/nina.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'toritori'
|
4
|
-
require 'observer'
|
5
|
-
require 'nina/assembler'
|
6
4
|
require 'nina/builder'
|
7
5
|
|
8
6
|
require_relative 'nina/version'
|
@@ -51,7 +49,7 @@ module Nina
|
|
51
49
|
receiver.extend ClassMethods
|
52
50
|
end
|
53
51
|
|
54
|
-
def self.
|
52
|
+
def self.def_reader(accessor, on:, to:, delegate: false)
|
55
53
|
on.define_singleton_method(accessor) { to }
|
56
54
|
on.define_singleton_method(:predecessor) { to }
|
57
55
|
def on.predecessors
|
@@ -65,10 +63,20 @@ module Nina
|
|
65
63
|
on.extend(MethodMissingDelegation)
|
66
64
|
end
|
67
65
|
|
68
|
-
def self.
|
69
|
-
|
70
|
-
|
71
|
-
Nina.
|
66
|
+
def self.link(build, delegate: false, &block)
|
67
|
+
result = nil
|
68
|
+
build.each.inject(nil) do |prev, (name, object)|
|
69
|
+
Nina.def_reader(name, on: prev, to: object, delegate: delegate) if prev
|
70
|
+
yield(name, object) if block
|
71
|
+
object.tap { |o| result ||= o }
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.reverse_link(build, delegate: false, &block)
|
77
|
+
build.each.with_index(-1).inject(nil) do |prev, ((name, object), idx)|
|
78
|
+
Nina.def_reader(build.keys[idx], on: object, to: prev, delegate: delegate) if prev
|
79
|
+
yield(name, object) if block
|
72
80
|
object
|
73
81
|
end
|
74
82
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nina
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrii Baran
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01
|
11
|
+
date: 2024-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: toritori
|
@@ -41,7 +41,6 @@ files:
|
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
43
|
- lib/nina.rb
|
44
|
-
- lib/nina/assembler.rb
|
45
44
|
- lib/nina/builder.rb
|
46
45
|
- lib/nina/builder/callbacks.rb
|
47
46
|
- lib/nina/builder/initialization.rb
|
data/lib/nina/assembler.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'nina/builder/initialization'
|
4
|
-
require 'nina/builder/callbacks'
|
5
|
-
|
6
|
-
module Nina
|
7
|
-
# Generates module that adds support for objects creation
|
8
|
-
class Assembler
|
9
|
-
include Observable
|
10
|
-
|
11
|
-
attr_reader :initialization, :callbacks
|
12
|
-
|
13
|
-
def initialize(abstract_factory, callbacks = nil)
|
14
|
-
@abstract_factory = abstract_factory
|
15
|
-
@initialization = Builder::Initialization.new(@abstract_factory.build_order_list)
|
16
|
-
@callbacks = callbacks&.copy || Builder::Callbacks.new(@abstract_factory.build_order_list)
|
17
|
-
end
|
18
|
-
|
19
|
-
def inject(build_order, delegate: false)
|
20
|
-
build_order.each.with_index(-1).inject(nil) do |prev, (name, idx)|
|
21
|
-
object = create_object(name, initialization)
|
22
|
-
setup_relation(object, prev, name, build_order[idx], delegate)
|
23
|
-
changed
|
24
|
-
notify_observers(name, object)
|
25
|
-
object
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def setup_relation(object, prev, name, accessor, delegate)
|
32
|
-
Nina.def_accessor(accessor, on: object, to: prev, delegate: delegate) if prev
|
33
|
-
callbacks.to_h[name].each { |c| c.call(object) } if callbacks&.to_h&.key?(name)
|
34
|
-
end
|
35
|
-
|
36
|
-
def create_object(name, initialization = {})
|
37
|
-
return @abstract_factory.create(name) if initialization.to_h[name].nil?
|
38
|
-
|
39
|
-
args, kwargs, block = initialization.to_h[name]
|
40
|
-
@abstract_factory.create(name, *args, **kwargs, &block)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|