factrey 0.4.0 → 0.5.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 +4 -4
- data/lib/factrey/blueprint/instantiator.rb +9 -33
- data/lib/factrey/blueprint/node.rb +32 -12
- data/lib/factrey/blueprint/type.rb +11 -6
- data/lib/factrey/blueprint.rb +1 -1
- data/lib/factrey/dsl.rb +2 -4
- data/lib/factrey/proxy.rb +4 -2
- data/lib/factrey/ref/defer.rb +2 -2
- data/lib/factrey/ref/resolver.rb +2 -1
- data/lib/factrey/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3103ae36a9bc66cf15aa05d6f2731cbaa783bfb0139105a9636b865c762e427f
|
4
|
+
data.tar.gz: 51b7561b137fe05e919bf9e13cf517ad9208d5c9237573b0acb198a7dffcd169
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44b1d62bc324437ab8a587b6906044711d7b65659b103bed1d6f789a3a5dd9cd2ca7caeb6ff69fd74e48ae0872627945276c3c58c03b28fd450b6af96d7763a0
|
7
|
+
data.tar.gz: d49aaed8a5f83620638b44ebe3963b1c68c6ef7d4effd9bfe9254e4e39de50c4859c7e33eb8be5e3a82d6b6fdbb9f04ecfa22f3fda7daeab042ed3a6e0ed5210
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
module Factrey
|
6
4
|
class Blueprint
|
7
5
|
# An internal class used by {Blueprint#instantiate}.
|
@@ -14,12 +12,14 @@ module Factrey
|
|
14
12
|
def initialize(context, blueprint)
|
15
13
|
@context = context
|
16
14
|
@objects = {}
|
17
|
-
@visited = Set.new
|
18
15
|
@blueprint = blueprint
|
16
|
+
|
17
|
+
# Intermediate state
|
18
|
+
@creating_objects = Set.new
|
19
19
|
end
|
20
20
|
|
21
21
|
def instantiate_objects
|
22
|
-
@blueprint.nodes.each_value {
|
22
|
+
@blueprint.nodes.each_value { ensure_object_created(_1) }
|
23
23
|
@objects
|
24
24
|
end
|
25
25
|
|
@@ -27,16 +27,16 @@ module Factrey
|
|
27
27
|
|
28
28
|
# @param node [Node]
|
29
29
|
# @return [Object]
|
30
|
-
def
|
30
|
+
def ensure_object_created(node)
|
31
31
|
@objects.fetch(node.name) do
|
32
|
-
unless @
|
32
|
+
unless @creating_objects.add?(node.name)
|
33
33
|
raise ArgumentError, "Circular references detected around #{node.type_annotated_name}"
|
34
34
|
end
|
35
35
|
|
36
36
|
args = resolver.resolve(node.args)
|
37
37
|
kwargs = resolver.resolve(node.kwargs)
|
38
|
-
|
39
|
-
@objects[node.name] = node.type.
|
38
|
+
node.auto_referenced_ancestors.each { kwargs[_1] = ensure_object_created(_2) }
|
39
|
+
@objects[node.name] = node.type.create_object(@context, *args, **kwargs)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -44,31 +44,7 @@ module Factrey
|
|
44
44
|
def resolver
|
45
45
|
@resolver ||= Ref::Resolver.new(recursion_limit: 5) do |name|
|
46
46
|
node = @blueprint.nodes.fetch(name) { raise ArgumentError, "Missing definition: #{name}" }
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# @param auto_references [Hash{Symbol => Symbol}]
|
52
|
-
# @param referenceable_nodes [Array<Node>]
|
53
|
-
# @param dest [Hash{Symbol => Object}]
|
54
|
-
def resolve_auto_references(auto_references, referenceable_nodes, dest)
|
55
|
-
candidates = {}
|
56
|
-
auto_references.each do |type_name, attribute|
|
57
|
-
next if dest.member? attribute # this attribute is explicitly specified
|
58
|
-
|
59
|
-
compatible_node, index = referenceable_nodes.reverse_each.with_index.find do |node, _|
|
60
|
-
node.type.compatible_types.include?(type_name)
|
61
|
-
end
|
62
|
-
next unless compatible_node
|
63
|
-
|
64
|
-
# the node closest to the end of the array has priority
|
65
|
-
next if candidates.member?(attribute) && candidates[attribute][1] <= index
|
66
|
-
|
67
|
-
candidates[attribute] = [compatible_node, index]
|
68
|
-
end
|
69
|
-
|
70
|
-
candidates.each do |attribute, (node, _)|
|
71
|
-
dest[attribute] = ensure_object_instantiated(node)
|
47
|
+
ensure_object_created(node)
|
72
48
|
end
|
73
49
|
end
|
74
50
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
3
|
require "securerandom"
|
5
4
|
|
6
5
|
module Factrey
|
@@ -16,34 +15,32 @@ module Factrey
|
|
16
15
|
attr_reader :name
|
17
16
|
# @return [Type] type of the object
|
18
17
|
attr_reader :type
|
19
|
-
# @return [
|
20
|
-
attr_reader :
|
18
|
+
# @return [Node, nil] parent node
|
19
|
+
attr_reader :parent
|
21
20
|
# @return [Array<Object>] positional arguments to be passed to the factory
|
22
21
|
attr_reader :args
|
23
22
|
# @return [Hash{Object => Object}] keyword arguments to be passed to the factory
|
24
23
|
attr_reader :kwargs
|
25
24
|
|
26
|
-
def initialize(name, type,
|
25
|
+
def initialize(name, type, parent: nil, args: [], kwargs: {})
|
27
26
|
raise TypeError, "name must be a Symbol" if name && !name.is_a?(Symbol)
|
28
27
|
raise TypeError, "type must be a Blueprint::Type" unless type.is_a? Blueprint::Type
|
29
|
-
|
30
|
-
raise TypeError, "ancestors must be an Array of Nodes"
|
31
|
-
end
|
28
|
+
raise TypeError, "parent must be a Node" if parent && !parent.is_a?(Node)
|
32
29
|
raise TypeError, "args must be an Array" unless args.is_a? Array
|
33
30
|
raise TypeError, "kwargs must be a Hash" unless kwargs.is_a? Hash
|
34
31
|
|
35
32
|
@name = name || :"#{ANONYMOUS_NAME_PREFIX}#{SecureRandom.hex(6)}"
|
36
33
|
@type = type
|
37
|
-
@
|
34
|
+
@parent = parent
|
38
35
|
@args = args
|
39
36
|
@kwargs = kwargs
|
40
37
|
end
|
41
38
|
|
42
39
|
# @param name [Symbol, nil]
|
43
40
|
# @param value [Object]
|
44
|
-
# @param
|
45
|
-
def self.computed(name, value,
|
46
|
-
new(name, Blueprint::Type::COMPUTED,
|
41
|
+
# @param parent [Node, nil]
|
42
|
+
def self.computed(name, value, parent: nil)
|
43
|
+
new(name, Blueprint::Type::COMPUTED, parent:, args: [value])
|
47
44
|
end
|
48
45
|
|
49
46
|
# @return [Boolean]
|
@@ -57,7 +54,7 @@ module Factrey
|
|
57
54
|
|
58
55
|
# @return [Ref, nil] if this node works as an alias to another node, return the reference to the node
|
59
56
|
def alias_ref
|
60
|
-
case [
|
57
|
+
case [type, args]
|
61
58
|
in Blueprint::Type::COMPUTED, [Ref => ref]
|
62
59
|
ref
|
63
60
|
else
|
@@ -65,6 +62,29 @@ module Factrey
|
|
65
62
|
end
|
66
63
|
end
|
67
64
|
|
65
|
+
# @return [Array<Node>] a list of ancestor nodes
|
66
|
+
def ancestors = parent ? [parent].concat(parent.ancestors) : []
|
67
|
+
|
68
|
+
# @return [Hash{Symbol => Node}] a map from attributes to auto-referenced ancestor nodes
|
69
|
+
def auto_referenced_ancestors
|
70
|
+
ancestors = self.ancestors
|
71
|
+
candidates = {}
|
72
|
+
type.auto_references.each do |type_name, attribute|
|
73
|
+
next if kwargs.member? attribute # this attribute is explicitly specified
|
74
|
+
|
75
|
+
# closer ancestors have higher (lower integer) priority
|
76
|
+
compatible_node, priority = ancestors.each_with_index.find do |node, _|
|
77
|
+
node.type.compatible_types.include?(type_name)
|
78
|
+
end
|
79
|
+
next unless compatible_node
|
80
|
+
next if candidates.member?(attribute) && candidates[attribute][0] <= priority
|
81
|
+
|
82
|
+
candidates[attribute] = [priority, compatible_node]
|
83
|
+
end
|
84
|
+
|
85
|
+
candidates.transform_values { _2 }
|
86
|
+
end
|
87
|
+
|
68
88
|
# Used for debugging and error reporting.
|
69
89
|
# @return [String]
|
70
90
|
def type_annotated_name = "#{name}(#{type.name})"
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
module Factrey
|
6
4
|
class Blueprint
|
7
5
|
# A type representation on Factrey blueprints.
|
@@ -14,13 +12,12 @@ module Factrey
|
|
14
12
|
attr_reader :compatible_types
|
15
13
|
# @return [Hash{Symbol => Symbol}] a name-to-attribute mapping for auto-referencing
|
16
14
|
attr_reader :auto_references
|
17
|
-
# @return [Proc] procedure that actually creates an object. See {Blueprint::Instantiator} implementation
|
18
|
-
attr_reader :factory
|
19
15
|
|
20
16
|
# @param name [Symbol]
|
21
17
|
# @param compatible_types [Array<Symbol>, Symbol]
|
22
18
|
# @param auto_references [Hash{Symbol => Symbol}, Array<Symbol>, Symbol]
|
23
19
|
# @yield [type, context, *args, **kwargs]
|
20
|
+
# procedure that actually creates an object. See {Blueprint::Instantiator} implementation
|
24
21
|
def initialize(name, compatible_types: [], auto_references: {}, &factory)
|
25
22
|
compatible_types = [compatible_types] if compatible_types.is_a? Symbol
|
26
23
|
auto_references = [auto_references] if auto_references.is_a? Symbol
|
@@ -43,8 +40,16 @@ module Factrey
|
|
43
40
|
@factory = factory
|
44
41
|
end
|
45
42
|
|
46
|
-
#
|
47
|
-
|
43
|
+
# Create an object of this type.
|
44
|
+
# @param context [Object] the context object that is passed to the factory
|
45
|
+
# @param args [Array<Object>] positional arguments for the factory
|
46
|
+
# @param kwargs [Hash{Symbol => Object}] keyword arguments for the factory
|
47
|
+
def create_object(context, *, **)
|
48
|
+
@factory.call(self, context, *, **)
|
49
|
+
end
|
50
|
+
|
51
|
+
# A special type that represents values computed from other objects. See {Node.computed}.
|
52
|
+
COMPUTED = new(:_computed) { |_, _, arg| arg }
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
data/lib/factrey/blueprint.rb
CHANGED
@@ -25,7 +25,7 @@ module Factrey
|
|
25
25
|
node.name,
|
26
26
|
node.type,
|
27
27
|
# This is OK since Hash insertion order in Ruby is retained
|
28
|
-
|
28
|
+
parent: node.parent&.then { result.nodes[_1.name] },
|
29
29
|
args: node.args.dup,
|
30
30
|
kwargs: node.kwargs.dup,
|
31
31
|
)
|
data/lib/factrey/dsl.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
module Factrey
|
6
4
|
# {Blueprint} DSL implementation.
|
7
5
|
class DSL
|
@@ -37,7 +35,7 @@ module Factrey
|
|
37
35
|
# @yieldparam ref [Ref]
|
38
36
|
# @return [Ref]
|
39
37
|
def object_node(name, type, ...)
|
40
|
-
node = @blueprint.add_node(Blueprint::Node.new(name, type,
|
38
|
+
node = @blueprint.add_node(Blueprint::Node.new(name, type, parent: @ancestors.last))
|
41
39
|
on(node.name, ...)
|
42
40
|
end
|
43
41
|
|
@@ -47,7 +45,7 @@ module Factrey
|
|
47
45
|
# @param name [Symbol, nil]
|
48
46
|
# @param value [Object]
|
49
47
|
def computed_node(name, value)
|
50
|
-
node = @blueprint.add_node(Blueprint::Node.computed(name, value,
|
48
|
+
node = @blueprint.add_node(Blueprint::Node.computed(name, value, parent: @ancestors.last))
|
51
49
|
node.to_ref
|
52
50
|
end
|
53
51
|
|
data/lib/factrey/proxy.rb
CHANGED
@@ -15,16 +15,18 @@ module Factrey
|
|
15
15
|
class Proxy < BasicObject
|
16
16
|
# @param receiver [Object]
|
17
17
|
# @param method [Symbol]
|
18
|
-
|
18
|
+
# @param preargs [Array<Object>]
|
19
|
+
def initialize(receiver, method, *preargs)
|
19
20
|
@receiver = receiver
|
20
21
|
@method = method
|
22
|
+
@preargs = preargs
|
21
23
|
end
|
22
24
|
|
23
25
|
# @!visibility private
|
24
26
|
def respond_to_missing?(_method_name, _) = true
|
25
27
|
|
26
28
|
def method_missing(method_name, ...)
|
27
|
-
@receiver.__send__(@method, method_name, ...)
|
29
|
+
@receiver.__send__(@method, *@preargs, method_name, ...)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
data/lib/factrey/ref/defer.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
module Factrey
|
4
4
|
class Ref
|
5
|
-
# A thin wrapper around
|
5
|
+
# A thin wrapper around <code>Proc</coode> to represent the procedure using the results of the reference resolution.
|
6
6
|
# Each argument name is considered as a reference.
|
7
|
-
# These references are resolved and the results are passed to the
|
7
|
+
# These references are resolved and the results are passed to the <code>Proc</code>.
|
8
8
|
#
|
9
9
|
# {Ref}s and {Defer}s are usually created through {ShorthandMethods#ref}.
|
10
10
|
class Defer
|
data/lib/factrey/ref/resolver.rb
CHANGED
@@ -13,7 +13,8 @@ module Factrey
|
|
13
13
|
|
14
14
|
# Traverse data recursively and resolve all {Ref}s and {Defer}s.
|
15
15
|
#
|
16
|
-
# This method supports recursive traversal for
|
16
|
+
# This method supports recursive traversal for <code>Array</code> and <code>Hash</code>. For other structures,
|
17
|
+
# consider using {Defer}.
|
17
18
|
# @param object [Object]
|
18
19
|
# @param recursion_count [Integer]
|
19
20
|
def resolve(object, recursion_count: 0)
|
data/lib/factrey/version.rb
CHANGED
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.
|
4
|
+
version: 0.5.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-
|
11
|
+
date: 2024-11-27 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.
|
@@ -50,14 +50,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 3.
|
53
|
+
version: 3.2.0
|
54
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
56
|
- - ">="
|
57
57
|
- !ruby/object:Gem::Version
|
58
58
|
version: '0'
|
59
59
|
requirements: []
|
60
|
-
rubygems_version: 3.5.
|
60
|
+
rubygems_version: 3.5.22
|
61
61
|
signing_key:
|
62
62
|
specification_version: 4
|
63
63
|
summary: Provides a declarative DSL to represent the creation plan of objects
|