rom-factory 0.6.0 → 0.7.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/CHANGELOG.md +13 -0
- data/Gemfile +1 -0
- data/README.md +0 -2
- data/lib/rom/factory/attributes/association.rb +34 -14
- data/lib/rom/factory/attributes/callable.rb +1 -2
- data/lib/rom/factory/builder/persistable.rb +5 -5
- data/lib/rom/factory/builder.rb +9 -5
- data/lib/rom/factory/dsl.rb +23 -11
- data/lib/rom/factory/factories.rb +4 -4
- data/lib/rom/factory/tuple_evaluator.rb +55 -18
- data/lib/rom/factory/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f2cad99dc93108f4bb7e9ac571ed198eaedee1b541b886fc018bb4501360817
|
4
|
+
data.tar.gz: 7fde94283a3d31672b6fa3ea8c082320524dc106025d4f50ac64ab3005bda6c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d14003a058a9eda9b2a407be0d4571de6213ded73532a01a0dbee8bdb3de47836a517425a742cf117b8674b67b64bf00d7e2ac9f47887f110a05d8143d7e0be
|
7
|
+
data.tar.gz: 389317084e6d7331c8e1fafcd304f34c458e393a63aee70debf0e04bd78d026b005a5f5ff5678eb3be29e499fdb2f1052b27a3602c31232d8ca14588cd506bfa
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.7.0 2018-11-17
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for traits (v-kolesnikov)
|
6
|
+
* Support building structs with associations (@ianks)
|
7
|
+
|
8
|
+
### Fixed
|
9
|
+
|
10
|
+
* Overwritten attributes with dependencies (JanaVPetrova)
|
11
|
+
|
12
|
+
[Compare v0.6.0...v0.7.0](https://github.com/rom-rb/rom-factory/compare/v0.6.0...v0.7.0)
|
13
|
+
|
1
14
|
## 0.6.0 2018-01-31
|
2
15
|
|
3
16
|
### Added
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/rom-factory
|
2
2
|
[travis]: https://travis-ci.org/rom-rb/rom-factory
|
3
|
-
[gemnasium]: https://gemnasium.com/rom-rb/rom-factory
|
4
3
|
[codeclimate]: https://codeclimate.com/github/rom-rb/rom-factory
|
5
4
|
[inchpages]: http://inch-ci.org/github/rom-rb/rom-factory
|
6
5
|
|
@@ -8,7 +7,6 @@
|
|
8
7
|
|
9
8
|
[][gem]
|
10
9
|
[][travis]
|
11
|
-
[][gemnasium]
|
12
10
|
[][codeclimate]
|
13
11
|
[][codeclimate]
|
14
12
|
[][inchpages]
|
@@ -2,24 +2,25 @@ module ROM::Factory
|
|
2
2
|
module Attributes
|
3
3
|
# @api private
|
4
4
|
module Association
|
5
|
-
def self.new(assoc, builder, options
|
6
|
-
const_get(assoc.definition.type).new(assoc, builder, options)
|
5
|
+
def self.new(assoc, builder, *traits, **options)
|
6
|
+
const_get(assoc.definition.type).new(assoc, builder, *traits, **options)
|
7
7
|
end
|
8
8
|
|
9
9
|
# @api private
|
10
10
|
class Core
|
11
|
-
attr_reader :assoc, :options
|
11
|
+
attr_reader :assoc, :options, :traits
|
12
12
|
|
13
13
|
# @api private
|
14
|
-
def initialize(assoc, builder, options
|
14
|
+
def initialize(assoc, builder, *traits, **options)
|
15
15
|
@assoc = assoc
|
16
16
|
@builder_proc = builder
|
17
|
+
@traits = traits
|
17
18
|
@options = options
|
18
19
|
end
|
19
20
|
|
20
21
|
# @api private
|
21
22
|
def builder
|
22
|
-
@__builder__ ||= @builder_proc.
|
23
|
+
@__builder__ ||= @builder_proc.call
|
23
24
|
end
|
24
25
|
|
25
26
|
# @api private
|
@@ -41,13 +42,16 @@ module ROM::Factory
|
|
41
42
|
# @api private
|
42
43
|
class ManyToOne < Core
|
43
44
|
# @api private
|
44
|
-
def call(attrs
|
45
|
+
def call(attrs, opts)
|
45
46
|
if attrs.key?(name) && !attrs[foreign_key]
|
46
47
|
assoc.associate(attrs, attrs[name])
|
47
48
|
elsif !attrs[foreign_key]
|
48
|
-
struct =
|
49
|
+
struct = if opts.fetch(:persist, true)
|
50
|
+
builder.persistable.create(*traits)
|
51
|
+
else
|
52
|
+
builder.struct(*traits, attrs)
|
53
|
+
end
|
49
54
|
tuple = { name => struct }
|
50
|
-
|
51
55
|
assoc.associate(tuple, struct)
|
52
56
|
end
|
53
57
|
end
|
@@ -61,12 +65,19 @@ module ROM::Factory
|
|
61
65
|
# @api private
|
62
66
|
class OneToMany < Core
|
63
67
|
# @api private
|
64
|
-
def call(attrs = EMPTY_HASH, parent)
|
68
|
+
def call(attrs = EMPTY_HASH, parent, persist: true)
|
65
69
|
return if attrs.key?(name)
|
66
70
|
|
67
|
-
structs = count.
|
68
|
-
|
69
|
-
|
71
|
+
structs = Array.new(count).map do
|
72
|
+
# hash which contains the foreign key info, i.e: { user_id: 1 }
|
73
|
+
association_hash = assoc.associate(attrs, parent)
|
74
|
+
|
75
|
+
if persist
|
76
|
+
builder.persistable.create(*traits, association_hash)
|
77
|
+
else
|
78
|
+
builder.struct(*traits, attrs.merge(association_hash))
|
79
|
+
end
|
80
|
+
end
|
70
81
|
|
71
82
|
{ name => structs }
|
72
83
|
end
|
@@ -85,10 +96,19 @@ module ROM::Factory
|
|
85
96
|
# @api private
|
86
97
|
class OneToOne < OneToMany
|
87
98
|
# @api private
|
88
|
-
def call(attrs = EMPTY_HASH, parent)
|
99
|
+
def call(attrs = EMPTY_HASH, parent, opts)
|
89
100
|
return if attrs.key?(name)
|
90
101
|
|
91
|
-
|
102
|
+
association_hash = assoc.associate(attrs, parent)
|
103
|
+
|
104
|
+
struct = if opts.fetch(:persist, true)
|
105
|
+
builder.persistable.create(*traits, association_hash)
|
106
|
+
else
|
107
|
+
belongs_to_name = Dry::Core::Inflector.singularize(assoc.source_alias)
|
108
|
+
belongs_to_associations = { belongs_to_name.to_sym => parent }
|
109
|
+
final_attrs = attrs.merge(association_hash).merge(belongs_to_associations)
|
110
|
+
builder.struct(*traits, final_attrs)
|
111
|
+
end
|
92
112
|
|
93
113
|
{ name => struct }
|
94
114
|
end
|
@@ -19,16 +19,16 @@ module ROM
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# @api private
|
22
|
-
def create(attrs
|
23
|
-
tuple = tuple(attrs)
|
22
|
+
def create(*traits, **attrs)
|
23
|
+
tuple = tuple(*traits, attrs)
|
24
24
|
persisted = persist(tuple)
|
25
25
|
|
26
|
-
if tuple_evaluator.has_associations?
|
27
|
-
tuple_evaluator.persist_associations(tuple, persisted)
|
26
|
+
if tuple_evaluator.has_associations?(traits)
|
27
|
+
tuple_evaluator.persist_associations(tuple, persisted, traits)
|
28
28
|
|
29
29
|
pk = primary_key_names.map { |key| persisted[key] }
|
30
30
|
|
31
|
-
relation.by_pk(*pk).combine(*tuple_evaluator.assoc_names).first
|
31
|
+
relation.by_pk(*pk).combine(*tuple_evaluator.assoc_names(traits)).first
|
32
32
|
else
|
33
33
|
persisted
|
34
34
|
end
|
data/lib/rom/factory/builder.rb
CHANGED
@@ -16,18 +16,22 @@ module ROM::Factory
|
|
16
16
|
# @return [ROM::Factory::Attributes]
|
17
17
|
param :attributes
|
18
18
|
|
19
|
+
# @!attribute [r] traits
|
20
|
+
# @return [Hash]
|
21
|
+
param :traits, default: -> { EMPTY_HASH }
|
22
|
+
|
19
23
|
# @!attribute [r] relation
|
20
24
|
# @return [ROM::Relation]
|
21
25
|
option :relation, reader: false
|
22
26
|
|
23
27
|
# @api private
|
24
|
-
def tuple(attrs
|
25
|
-
tuple_evaluator.defaults(attrs)
|
28
|
+
def tuple(*traits, **attrs)
|
29
|
+
tuple_evaluator.defaults(traits, attrs)
|
26
30
|
end
|
27
31
|
|
28
32
|
# @api private
|
29
|
-
def struct(attrs
|
30
|
-
tuple_evaluator.struct(attrs)
|
33
|
+
def struct(*traits, **attrs)
|
34
|
+
tuple_evaluator.struct(*traits, attrs)
|
31
35
|
end
|
32
36
|
alias_method :create, :struct
|
33
37
|
|
@@ -43,7 +47,7 @@ module ROM::Factory
|
|
43
47
|
|
44
48
|
# @api private
|
45
49
|
def tuple_evaluator
|
46
|
-
@__tuple_evaluator__ ||= TupleEvaluator.new(attributes, options[:relation])
|
50
|
+
@__tuple_evaluator__ ||= TupleEvaluator.new(attributes, options[:relation], traits)
|
47
51
|
end
|
48
52
|
|
49
53
|
# @api private
|
data/lib/rom/factory/dsl.rb
CHANGED
@@ -26,6 +26,7 @@ module ROM
|
|
26
26
|
define_method(:rand, ::Kernel.instance_method(:rand))
|
27
27
|
|
28
28
|
attr_reader :_name, :_relation, :_attributes, :_factories, :_valid_names
|
29
|
+
attr_reader :_traits
|
29
30
|
|
30
31
|
# @api private
|
31
32
|
def initialize(name, attributes: AttributeRegistry.new, relation:, factories:)
|
@@ -33,13 +34,14 @@ module ROM
|
|
33
34
|
@_relation = relation
|
34
35
|
@_factories = factories
|
35
36
|
@_attributes = attributes.dup
|
37
|
+
@_traits = {}
|
36
38
|
@_valid_names = _relation.schema.attributes.map(&:name)
|
37
39
|
yield(self)
|
38
40
|
end
|
39
41
|
|
40
42
|
# @api private
|
41
43
|
def call
|
42
|
-
::ROM::Factory::Builder.new(_attributes, relation: _relation)
|
44
|
+
::ROM::Factory::Builder.new(_attributes, _traits, relation: _relation)
|
43
45
|
end
|
44
46
|
|
45
47
|
# Delegate to a builder and persist a struct
|
@@ -57,9 +59,7 @@ module ROM
|
|
57
59
|
#
|
58
60
|
# @api private
|
59
61
|
def sequence(meth, &block)
|
60
|
-
if _valid_names.include?(meth)
|
61
|
-
define_sequence(meth, block)
|
62
|
-
end
|
62
|
+
define_sequence(meth, block) if _valid_names.include?(meth)
|
63
63
|
end
|
64
64
|
|
65
65
|
# Set timestamp attributes
|
@@ -100,6 +100,18 @@ module ROM
|
|
100
100
|
::ROM::Factory.fake(*args)
|
101
101
|
end
|
102
102
|
|
103
|
+
def trait(name, parents = [], &block)
|
104
|
+
_traits[name] = DSL.new(
|
105
|
+
"#{_name}_#{name}",
|
106
|
+
attributes: _traits.values_at(*parents).flat_map(&:elements).inject(
|
107
|
+
AttributeRegistry.new, :<<
|
108
|
+
),
|
109
|
+
relation: _relation,
|
110
|
+
factories: _factories,
|
111
|
+
&block
|
112
|
+
)._attributes
|
113
|
+
end
|
114
|
+
|
103
115
|
# Create an association attribute
|
104
116
|
#
|
105
117
|
# @example belongs-to
|
@@ -113,11 +125,11 @@ module ROM
|
|
113
125
|
# @option options [Integer] count Number of objects to generate (has-many only)
|
114
126
|
#
|
115
127
|
# @api public
|
116
|
-
def association(name, options
|
128
|
+
def association(name, *traits, **options)
|
117
129
|
assoc = _relation.associations[name]
|
118
130
|
builder = -> { _factories.for_relation(assoc.target) }
|
119
131
|
|
120
|
-
_attributes << attributes::Association.new(assoc, builder, options)
|
132
|
+
_attributes << attributes::Association.new(assoc, builder, *traits, **options)
|
121
133
|
end
|
122
134
|
|
123
135
|
private
|
@@ -143,11 +155,11 @@ module ROM
|
|
143
155
|
|
144
156
|
# @api private
|
145
157
|
def define_attr(name, *args, &block)
|
146
|
-
if block
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
158
|
+
_attributes << if block
|
159
|
+
attributes::Callable.new(name, self, block)
|
160
|
+
else
|
161
|
+
attributes::Value.new(name, *args)
|
162
|
+
end
|
151
163
|
end
|
152
164
|
|
153
165
|
# @api private
|
@@ -39,8 +39,8 @@ module ROM::Factory
|
|
39
39
|
# @return [ROM::Struct]
|
40
40
|
#
|
41
41
|
# @api public
|
42
|
-
def [](name, attrs
|
43
|
-
registry[name].struct_namespace(struct_namespace).create(attrs)
|
42
|
+
def [](name, *traits, **attrs)
|
43
|
+
registry[name].struct_namespace(struct_namespace).create(*traits, attrs)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -160,8 +160,8 @@ module ROM::Factory
|
|
160
160
|
# @return [ROM::Struct]
|
161
161
|
#
|
162
162
|
# @api public
|
163
|
-
def [](name, attrs
|
164
|
-
registry[name].persistable(struct_namespace).create(attrs)
|
163
|
+
def [](name, *traits, **attrs)
|
164
|
+
registry[name].persistable(struct_namespace).create(*traits, attrs)
|
165
165
|
end
|
166
166
|
|
167
167
|
# Return in-memory struct builder
|
@@ -10,6 +10,9 @@ module ROM
|
|
10
10
|
# @api private
|
11
11
|
attr_reader :relation
|
12
12
|
|
13
|
+
# @api private
|
14
|
+
attr_reader :traits
|
15
|
+
|
13
16
|
# @api private
|
14
17
|
attr_reader :model
|
15
18
|
|
@@ -17,39 +20,61 @@ module ROM
|
|
17
20
|
attr_reader :sequence
|
18
21
|
|
19
22
|
# @api private
|
20
|
-
def initialize(attributes, relation)
|
23
|
+
def initialize(attributes, relation, traits = {})
|
21
24
|
@attributes = attributes
|
22
25
|
@relation = relation.with(auto_struct: true)
|
26
|
+
@traits = traits
|
23
27
|
@model = @relation.combine(*assoc_names).mapper.model
|
24
28
|
@sequence = Sequences[relation]
|
25
29
|
end
|
26
30
|
|
27
31
|
# @api private
|
28
|
-
def defaults(attrs)
|
29
|
-
evaluate(attrs).merge(attrs)
|
32
|
+
def defaults(traits, attrs, opts = EMPTY_HASH)
|
33
|
+
evaluate(traits, attrs, opts).merge(attrs)
|
30
34
|
end
|
31
35
|
|
32
36
|
# @api private
|
33
|
-
def struct(attrs)
|
34
|
-
|
37
|
+
def struct(*traits, attrs)
|
38
|
+
merged_attrs = struct_attrs.merge(defaults(traits, attrs, persist: false))
|
39
|
+
is_callable = proc { |_name, value| value.respond_to?(:call) }
|
40
|
+
|
41
|
+
callables = merged_attrs.select(&is_callable)
|
42
|
+
attributes = merged_attrs.reject(&is_callable)
|
43
|
+
|
44
|
+
materialized_callables = {}
|
45
|
+
callables.each do |_name, callable|
|
46
|
+
materialized_callables.merge!(callable.call(attributes, persist: false))
|
47
|
+
end
|
48
|
+
|
49
|
+
attributes.merge!(materialized_callables)
|
50
|
+
attributes = relation.output_schema.call(attributes)
|
51
|
+
|
52
|
+
model.new(attributes)
|
35
53
|
end
|
36
54
|
|
37
55
|
# @api private
|
38
|
-
def persist_associations(tuple, parent)
|
39
|
-
assoc_names.each do |name|
|
56
|
+
def persist_associations(tuple, parent, traits = [])
|
57
|
+
assoc_names(traits).each do |name|
|
40
58
|
assoc = tuple[name]
|
41
|
-
assoc.(parent) if assoc.is_a?(Proc)
|
59
|
+
assoc.call(parent, persist: true) if assoc.is_a?(Proc)
|
42
60
|
end
|
43
61
|
end
|
44
62
|
|
45
63
|
# @api private
|
46
|
-
def assoc_names
|
47
|
-
|
64
|
+
def assoc_names(traits = [])
|
65
|
+
assocs(traits).map(&:name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def assocs(traits_names = [])
|
69
|
+
traits
|
70
|
+
.values_at(*traits_names)
|
71
|
+
.map(&:associations).flat_map(&:elements)
|
72
|
+
.inject(AttributeRegistry.new(attributes.associations.elements), :<<)
|
48
73
|
end
|
49
74
|
|
50
75
|
# @api private
|
51
|
-
def has_associations?
|
52
|
-
assoc_names.
|
76
|
+
def has_associations?(traits = [])
|
77
|
+
!assoc_names(traits).empty?
|
53
78
|
end
|
54
79
|
|
55
80
|
# @api private
|
@@ -60,12 +85,14 @@ module ROM
|
|
60
85
|
private
|
61
86
|
|
62
87
|
# @api private
|
63
|
-
def evaluate(attrs)
|
64
|
-
evaluate_values(attrs)
|
88
|
+
def evaluate(traits, attrs, opts)
|
89
|
+
evaluate_values(attrs, opts)
|
90
|
+
.merge(evaluate_associations(attrs, opts))
|
91
|
+
.merge(evaluate_traits(traits, attrs, opts))
|
65
92
|
end
|
66
93
|
|
67
94
|
# @api private
|
68
|
-
def evaluate_values(attrs)
|
95
|
+
def evaluate_values(attrs, opts)
|
69
96
|
attributes.values.tsort.each_with_object({}) do |attr, h|
|
70
97
|
deps = attr.dependency_names.map { |k| h[k] }.compact
|
71
98
|
result = attr.(attrs, *deps)
|
@@ -76,13 +103,23 @@ module ROM
|
|
76
103
|
end
|
77
104
|
end
|
78
105
|
|
106
|
+
def evaluate_traits(traits, attrs, opts)
|
107
|
+
return {} if traits.empty?
|
108
|
+
|
109
|
+
traits_attrs = self.traits.values_at(*traits).flat_map(&:elements)
|
110
|
+
registry = AttributeRegistry.new(traits_attrs)
|
111
|
+
self.class.new(registry, relation).defaults([], attrs, opts)
|
112
|
+
end
|
113
|
+
|
79
114
|
# @api private
|
80
|
-
def evaluate_associations(attrs)
|
115
|
+
def evaluate_associations(attrs, opts)
|
81
116
|
attributes.associations.each_with_object({}) do |assoc, h|
|
82
117
|
if assoc.dependency?(relation)
|
83
|
-
h[assoc.name] = ->
|
118
|
+
h[assoc.name] = ->(parent, call_opts) do
|
119
|
+
assoc.call(parent, opts.merge(call_opts))
|
120
|
+
end
|
84
121
|
else
|
85
|
-
result = assoc.(attrs)
|
122
|
+
result = assoc.(attrs, opts)
|
86
123
|
h.update(result) if result
|
87
124
|
end
|
88
125
|
end
|
data/lib/rom/factory/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-factory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janis Miezitis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dry-configurable
|
@@ -132,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
132
|
version: '0'
|
133
133
|
requirements: []
|
134
134
|
rubyforge_project:
|
135
|
-
rubygems_version: 2.7.
|
135
|
+
rubygems_version: 2.7.6
|
136
136
|
signing_key:
|
137
137
|
specification_version: 4
|
138
138
|
summary: ROM based builder library to make your specs awesome. DSL partially inspired
|