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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 499160b10c559f3a32fbcb92befd4f3986ca7e6fda2af05d0b5d8cfc21a5036a
4
- data.tar.gz: e21824691f8e19b9c9cfa87e3396a3d23afafe658a7824db5eb9988345c75c01
3
+ metadata.gz: 2552f792c064a8dc7fc6916b07f0741d696916f7b844bb0fb12db6f6db083680
4
+ data.tar.gz: 489c9ad648b20a0e2b48704dc87350011682b1f905354681a85cfb24e6217cbc
5
5
  SHA512:
6
- metadata.gz: 6af4a721285ec85a8923cc184b84333bc45ffc5f4f2febab9bceacf43c3447c5a6bf2e069176da21f9a7b1e52315dd89dfef96b8b48dfe46cd87a56d94deaa4a
7
- data.tar.gz: 72067d8447bf2cf390867882089aa9680f41c6e0a2e8ba2faf202d58433fce9da1564f4ea16bd6b36fefc584b885af06968d94f18eaa643a260323bca5f8a602
6
+ metadata.gz: cf06ff35a6512ed258d44e589b8015f09c28ffaa22eacca7cc162ef5cb623566f4cf72d38a13d651bb599f9407555847a0a001cf79d00235169f5664259e3060
7
+ data.tar.gz: 34b5f8ea7925272bcf9f4edd7b8ef4fe1221dbc27cc6e261ce283806dc87c58d290325976cfe6d059b5f334a6f27c5d5ae16bf9e6ebd3cc80e9b904ffd1bf30e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nina (0.1.6)
4
+ nina (0.2.1)
5
5
  toritori (= 0.2.1)
6
6
 
7
7
  GEM
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
- We may apply delegation techique from OOD to hide deeper layers of resulted object
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 < Initialization
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
- attr_reader :allow_list
8
-
9
- def initialize(allow_list, atts = {})
10
- @allow_list = allow_list
11
- @atts = 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] = [args, kwargs, block]
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, include_private = false)
21
- @allow_list.include?(method) || super
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
- @abstract_factory.build_order_list.freeze
42
- @assembler = Assembler.new(@abstract_factory, callbacks)
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: @assembler.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 @assembler.callbacks if block
46
+ yield @callbacks if block
59
47
 
60
48
  copy
61
49
  end
62
50
 
63
- def wrap(delegate: false, &block)
64
- yield @assembler.initialization if block
51
+ def nest(delegate: false, &block)
52
+ initialization = Builder::Initialization.new(self)
53
+ yield initialization if block
65
54
 
66
- @assembler.inject(@abstract_factory.build_order_list, delegate: delegate)
55
+ Nina.link(initialization.to_h, delegate: delegate)
67
56
  end
68
57
 
69
- def nest(delegate: false, &block)
70
- yield @assembler.initialization if block
58
+ def wrap(delegate: false, &block)
59
+ initialization = Builder::Initialization.new(self)
60
+ yield initialization if block
71
61
 
72
- @assembler.inject(@abstract_factory.build_order_list.reverse, delegate: delegate)
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.freeze
81
- @assembler = Assembler.new(@abstract_factory, @assembler.callbacks)
82
- @assembler.add_observer(self)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nina
4
- VERSION = '0.1.6'
4
+ VERSION = '0.2.1'
5
5
  end
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.def_accessor(accessor, on:, to:, delegate: false)
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.linked_list(build_config, delegate: false)
69
- build_order = build_config.keys
70
- build_config.each.with_index(-1).inject(nil) do |prev, ((_, object), idx)|
71
- Nina.def_accessor(build_order[idx], on: object, to: prev, delegate: delegate) if prev
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.6
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-03 00:00:00.000000000 Z
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
@@ -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