nina 0.1.5 → 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: 6a252130b2ce26b317359fa4566edf12fba94042ddb9863fc8c71148a283c718
4
- data.tar.gz: bb24fa2d53ef3ba45b921cc7a2af366b9e54cd2703681f0cd14210362fe5b473
3
+ metadata.gz: 3d790033be42c95566d03b89e7e39e58f877b2d17e1aa665f9a83696bdeaeed3
4
+ data.tar.gz: 807a021192f39f110b36fdcebfc68516d1c32ee9d3a3cffbf80bce8dbacdec1b
5
5
  SHA512:
6
- metadata.gz: fb97e0c1d28b7dc902366368f770dc3ed7c6f677dd98260dac0caa0a97c16a5d89a09ece6bac74125f7f3a266f4a98baa7a14ff72d163e331b1aa2cb206b0a25
7
- data.tar.gz: fb26ae5dfac451918af68b16b3f45081559e2e546338fdf8eb0a4b48dcee0f79d4314b3d670b65ac066a472c96ae8c579686ae8f562546a71c2dc81d0bd43f99
6
+ metadata.gz: f8cb2f5c4e4eb44b3317892d9cfecdd2b6bdacfd190996222515351d68cbccf738e173773b3c36eef8ae65091ffb2944b0ac6c9a40dc6606eb0db4c18b272246
7
+ data.tar.gz: 320bbb01e2fda05a36633ab5cbfbe0ae63a305e2a5de4c651adadcb41fbfb730b38c11a2508349969507d94a98c41eb9f29b4322a6775ae4b353ad4423f2ff62
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nina (0.1.5)
4
+ nina (0.2.0)
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,27 @@ 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
+ yield @initialization if block
65
53
 
66
- @assembler.inject(@abstract_factory.build_order_list, delegate: delegate)
54
+ Nina.link(@initialization.to_h, delegate: delegate)
67
55
  end
68
56
 
69
- def nest(delegate: false, &block)
70
- yield @assembler.initialization if block
57
+ def wrap(delegate: false, &block)
58
+ yield @initialization if block
71
59
 
72
- @assembler.inject(@abstract_factory.build_order_list.reverse, delegate: delegate)
60
+ Nina.reverse_link(@initialization.to_h, delegate: delegate)
73
61
  end
74
62
 
75
63
  def subclass(&def_block)
@@ -77,13 +65,22 @@ module Nina
77
65
 
78
66
  @abstract_factory = Class.new(abstract_factory)
79
67
  @abstract_factory.class_eval(&def_block)
80
- @abstract_factory.build_order_list.freeze
81
- @assembler = Assembler.new(@abstract_factory, @assembler.callbacks)
68
+ @initialization = Builder::Initialization.new(self)
69
+ @callbacks = callbacks&.copy || Callbacks.new(@abstract_factory.build_order_list)
70
+ end
71
+
72
+ private
73
+
74
+ def callbacks_for(name)
75
+ return [] unless @callbacks
76
+
77
+ @callbacks.to_h.fetch(name, [])
82
78
  end
83
79
 
84
80
  def update(name, object)
81
+ callbacks_for(name).each { |c| c.call(object) }
85
82
  @observers.each do |observer|
86
- observer.public_send(:"on_#{name}_created", object) if observer.respond_to?(:"on_#{name}_created")
83
+ observer.public_send(:"on_#{name}_created", object, @name) if observer.respond_to?(:"on_#{name}_created")
87
84
  end
88
85
  end
89
86
  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.5'
4
+ VERSION = '0.2.0'
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'
@@ -45,31 +43,40 @@ module Nina
45
43
  def respond_to_missing?(method_name, _include_private = false)
46
44
  public_methods.detect { |m| m == :predecessor } || super
47
45
  end
48
-
49
- def predecessors
50
- Enumerator.new do |y|
51
- obj = self
52
- y << obj = obj.predecessor while obj.methods.detect { |m| m == :predecessor }
53
- end
54
- end
55
46
  end
56
47
 
57
48
  def self.included(receiver)
58
49
  receiver.extend ClassMethods
59
50
  end
60
51
 
61
- def self.def_accessor(accessor, on:, to:, delegate: false)
52
+ def self.def_reader(accessor, on:, to:, delegate: false)
62
53
  on.define_singleton_method(accessor) { to }
63
54
  on.define_singleton_method(:predecessor) { to }
55
+ def on.predecessors
56
+ Enumerator.new do |y|
57
+ obj = self
58
+ y << obj = obj.predecessor while obj.methods.detect { |m| m == :predecessor }
59
+ end
60
+ end
64
61
  return unless delegate
65
62
 
66
63
  on.extend(MethodMissingDelegation)
67
64
  end
68
65
 
69
- def self.linked_list(build_config, delegate: false)
70
- build_order = build_config.keys
71
- build_config.each.with_index(-1).inject(nil) do |prev, ((_, object), idx)|
72
- 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
73
80
  object
74
81
  end
75
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.5
4
+ version: 0.2.0
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-02 00:00:00.000000000 Z
11
+ date: 2024-01-15 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