factrey 0.1.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30e4d58ea43d66a45f4d22cb563acaa3a5f7811d09cd6e7bb3a6f617c57f4223
4
- data.tar.gz: a0ba9e04f9d0a387d3cf643e27c7ae8d7ace5529c5dd7bca75f0ae7cbe3d1819
3
+ metadata.gz: b1b0e688509760fb4e5a6c8c9426015e5168ebcaf58dee51c1457580d62d219f
4
+ data.tar.gz: 8447962706952260936adc9d386edaeeb4d85750f33266e5702fa42c87e5bfc2
5
5
  SHA512:
6
- metadata.gz: 765c317be9294d3837c19e172ef05093b16b08d6903313f43ffaeeec9d39dc9e46e08fca2d07dddde9010595909f44bebe8cdcd735d8dd5f940760c6226fe829
7
- data.tar.gz: 2fcc20ada4f208d4341cc5165241349690bb08131eb18e11b7623ff9ed8374326fc79012f88ec4b2429870175f33ac029a644b5ba67806f17f84809118a2dd54
6
+ metadata.gz: 7b5b370ec393ebd9a9a99dc68c4c824b8e9e4af42a120a1c686e6db3c0558a9525164c64ac93dee49c323fbebfdc5cc5c9a158d15e4fb1e58576d9c15db22c8f
7
+ data.tar.gz: 447931fcff3033dfeb0b308c7012e23d85bfce06c26c1f5b429248518d9e41a339cd8e6f8e2dfbfea692c94a2e1e809ecec66ceba2bdc9e99d5ca28bc993b99f
@@ -18,50 +18,63 @@ module Factrey
18
18
  @blueprint = blueprint
19
19
  end
20
20
 
21
+ def instantiate_objects
22
+ @blueprint.nodes.each_value { ensure_object_instantiated(_1) }
23
+ @objects
24
+ end
25
+
26
+ def instantiate_result
27
+ instantiate_objects # To keep consistency in the order of instantiation
28
+ resolver.resolve(@blueprint.result)
29
+ end
30
+
31
+ private
32
+
21
33
  # @param node [Node]
22
34
  # @return [Object]
23
- def visit(node)
35
+ def ensure_object_instantiated(node)
24
36
  @objects.fetch(node.name) do
25
37
  unless @visited.add?(node.name)
26
38
  raise ArgumentError, "Circular references detected around #{node.type_annotated_name}"
27
39
  end
28
40
 
29
- @objects[node.name] = instantiate_object(node)
41
+ args = resolver.resolve(node.args)
42
+ kwargs = resolver.resolve(node.kwargs)
43
+ resolve_auto_references(node.type.auto_references, node.ancestors, kwargs)
44
+ @objects[node.name] = node.type.factory.call(node.type, @context, *args, **kwargs)
30
45
  end
31
46
  end
32
47
 
33
- private
34
-
35
- # @param node [Node]
36
- # @return [Object]
37
- def instantiate_object(node)
38
- resolver = Ref::Resolver.new(recursion_limit: 5) do |name|
39
- visit(
40
- @blueprint.nodes.fetch(name) do
41
- raise ArgumentError, "Missing definition #{name} around #{node.type_annotated_name}"
42
- end,
43
- )
48
+ # @return [Ref::Resolver]
49
+ def resolver
50
+ @resolver ||= Ref::Resolver.new(recursion_limit: 5) do |name|
51
+ node = @blueprint.nodes.fetch(name) { raise ArgumentError, "Missing definition: #{name}" }
52
+ ensure_object_instantiated(node)
44
53
  end
54
+ end
45
55
 
46
- args = resolver.resolve(node.args)
47
- kwargs = resolver.resolve(node.kwargs)
48
-
49
- # Resolve auto references to the ancestors
50
- auto_references = {}
51
- node.type.auto_references.each do |type_name, attribute|
52
- next if kwargs.member? attribute # explicitly specified
56
+ # @param auto_references [Hash{Symbol => Symbol}]
57
+ # @param referenceable_nodes [Array<Node>]
58
+ # @param dest [Hash{Symbol => Object}]
59
+ def resolve_auto_references(auto_references, referenceable_nodes, dest)
60
+ candidates = {}
61
+ auto_references.each do |type_name, attribute|
62
+ next if dest.member? attribute # this attribute is explicitly specified
53
63
 
54
- compatible_ancestor, index = node.ancestors.reverse_each.with_index.find do |ancestor, _|
55
- ancestor.type.compatible_types.include?(type_name)
64
+ compatible_node, index = referenceable_nodes.reverse_each.with_index.find do |node, _|
65
+ node.type.compatible_types.include?(type_name)
56
66
  end
57
- next unless compatible_ancestor
58
- next if auto_references.member?(attribute) && auto_references[attribute][1] <= index
67
+ next unless compatible_node
68
+
69
+ # the node closest to the end of the array has priority
70
+ next if candidates.member?(attribute) && candidates[attribute][1] <= index
59
71
 
60
- auto_references[attribute] = [compatible_ancestor, index]
72
+ candidates[attribute] = [compatible_node, index]
61
73
  end
62
- auto_references.each { |attribute, (ancestor, _)| kwargs[attribute] = visit(ancestor) }
63
74
 
64
- node.type.factory.call(node.type, @context, *args, **kwargs)
75
+ candidates.each do |attribute, (node, _)|
76
+ dest[attribute] = ensure_object_instantiated(node)
77
+ end
65
78
  end
66
79
  end
67
80
  end
@@ -35,6 +35,9 @@ module Factrey
35
35
  @kwargs = kwargs
36
36
  end
37
37
 
38
+ # @return [Ref]
39
+ def to_ref = Ref.new(name)
40
+
38
41
  # @return [Boolean]
39
42
  def root? = ancestors.empty?
40
43
 
@@ -10,6 +10,8 @@ module Factrey
10
10
  class Blueprint
11
11
  # @return [Hash{Symbol => Node}] a set of nodes
12
12
  attr_reader :nodes
13
+ # @return [Object] the result of the DSL code is defined here
14
+ attr_reader :result
13
15
 
14
16
  # Creates an empty blueprint.
15
17
  def initialize
@@ -34,10 +36,6 @@ module Factrey
34
36
  result
35
37
  end
36
38
 
37
- # Get the last root node.
38
- # @return [Node, nil]
39
- def representative_node = nodes.each_value.reverse_each.find(&:root?)
40
-
41
39
  # Add a node. This method is used by {DSL} and usually does not need to be called directly.
42
40
  # @return [Node]
43
41
  def add_node(...)
@@ -48,15 +46,23 @@ module Factrey
48
46
  node
49
47
  end
50
48
 
51
- # Create a set of objects based on this blueprint.
49
+ # Define the result. This method is used by {DSL} and usually does not need to be called directly.
50
+ # @param result [Object]
51
+ # @param overwrite [Boolean] whether to overwrite the existing result
52
+ def define_result(result, overwrite: false)
53
+ return if defined?(@result) && !overwrite
54
+
55
+ @result = result
56
+ end
57
+
58
+ # Create a set of objects and compute the result based on this blueprint.
52
59
  # @param context [Object] context object to be passed to the factories
53
- # @return [Hash{Symbol => Object}]
60
+ # @return [(Object, {Symbol => Object})] the result and the created objects
54
61
  def instantiate(context = nil)
55
62
  instantiator = Instantiator.new(context, self)
56
-
57
- nodes.each_value { instantiator.visit(_1) }
58
-
59
- instantiator.objects
63
+ objects = instantiator.instantiate_objects
64
+ result = instantiator.instantiate_result
65
+ [result, objects]
60
66
  end
61
67
  end
62
68
  end
data/lib/factrey/dsl.rb CHANGED
@@ -89,6 +89,8 @@ module Factrey
89
89
  #
90
90
  # This method is usually not called directly. Use the shorthand method defined by {.add_type} instead.
91
91
  # @param type [Blueprint::Type]
92
+ # @yieldparam ref [Ref]
93
+ # @return [Ref]
92
94
  def node(type, ...)
93
95
  name = @let_scope ? (@let_scope.name || type.name) : nil
94
96
  @let_scope = nil # consumed
@@ -98,6 +100,8 @@ module Factrey
98
100
  end
99
101
 
100
102
  # Enter the node to configure arguments and child nodes.
103
+ # @yieldparam ref [Ref]
104
+ # @return [Ref]
101
105
  # @example
102
106
  # Factrey.blueprint do
103
107
  # let.blog do
@@ -110,7 +114,7 @@ module Factrey
110
114
  # # Add title to `article2`
111
115
  # on.article2(title: "This is an article 2")
112
116
  # end
113
- def on(name = nil, ...)
117
+ def on(name = nil, *args, **kwargs)
114
118
  return On.new(self) if name.nil? && !block_given?
115
119
 
116
120
  node = @blueprint.nodes[name]
@@ -118,9 +122,10 @@ module Factrey
118
122
 
119
123
  stashed_ancestors = @ancestors
120
124
  @ancestors = node.ancestors + [node]
121
- args(...)
125
+ args(*args, **kwargs)
126
+ yield node.to_ref if block_given?
122
127
  @ancestors = stashed_ancestors
123
- node
128
+ node.to_ref
124
129
  end
125
130
 
126
131
  # Add arguments to the current node.
@@ -137,7 +142,6 @@ module Factrey
137
142
 
138
143
  @ancestors.last.args.concat(args)
139
144
  @ancestors.last.kwargs.update(kwargs)
140
- yield if block_given?
141
145
  end
142
146
 
143
147
  class << self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Factrey
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/factrey.rb CHANGED
@@ -37,7 +37,7 @@ module Factrey
37
37
  raise TypeError, "dsl must be a subclass of DSL" unless dsl <= DSL
38
38
 
39
39
  blueprint ||= Blueprint.new
40
- dsl.new(blueprint:, ext:).instance_exec(&) if block_given?
40
+ blueprint.define_result dsl.new(blueprint:, ext:).instance_exec(&) if block_given?
41
41
  blueprint
42
42
  end
43
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factrey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yubrot
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-22 00:00:00.000000000 Z
11
+ date: 2024-07-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Factrey provides a declarative DSL to represent the creation plan of objects, for FactoryBot::Blueprint.