rom-factory 0.11.0 → 0.13.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/.github/workflows/ci.yml +18 -18
- data/.github/workflows/docsite.yml +2 -3
- data/.github/workflows/sync_configs.yml +9 -15
- data/.postgres.env +4 -0
- data/{.action_hero.yml → .repobot.yml} +8 -5
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +64 -14
- data/Gemfile +19 -9
- data/Gemfile.devtools +1 -1
- data/README.md +2 -2
- data/changelog.yml +36 -1
- data/compose.yml +7 -0
- data/docsite/source/index.html.md +19 -7
- data/lib/rom/factory/attribute_registry.rb +15 -17
- data/lib/rom/factory/attributes/association.rb +102 -68
- data/lib/rom/factory/attributes/callable.rb +6 -0
- data/lib/rom/factory/builder/persistable.rb +19 -2
- data/lib/rom/factory/builder.rb +9 -1
- data/lib/rom/factory/dsl.rb +66 -34
- data/lib/rom/factory/factories.rb +27 -7
- data/lib/rom/factory/sequences.rb +3 -2
- data/lib/rom/factory/tuple_evaluator.rb +78 -25
- data/lib/rom/factory/version.rb +1 -1
- data/rom-factory.gemspec +12 -12
- metadata +18 -19
- data/CODEOWNERS +0 -1
@@ -3,10 +3,11 @@
|
|
3
3
|
module ROM::Factory
|
4
4
|
module Attributes
|
5
5
|
# @api private
|
6
|
+
# rubocop:disable Style/OptionalArguments
|
6
7
|
module Association
|
7
8
|
class << self
|
8
|
-
def new(assoc,
|
9
|
-
const_get(assoc.definition.type).new(assoc,
|
9
|
+
def new(assoc, ...)
|
10
|
+
const_get(assoc.definition.type).new(assoc, ...)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
@@ -23,9 +24,7 @@ module ROM::Factory
|
|
23
24
|
end
|
24
25
|
|
25
26
|
# @api private
|
26
|
-
def through?
|
27
|
-
false
|
28
|
-
end
|
27
|
+
def through? = false
|
29
28
|
|
30
29
|
# @api private
|
31
30
|
def builder
|
@@ -33,41 +32,64 @@ module ROM::Factory
|
|
33
32
|
end
|
34
33
|
|
35
34
|
# @api private
|
36
|
-
def name
|
37
|
-
assoc.key
|
38
|
-
end
|
35
|
+
def name = assoc.key
|
39
36
|
|
40
37
|
# @api private
|
41
|
-
def dependency?(*)
|
42
|
-
false
|
43
|
-
end
|
38
|
+
def dependency?(*) = false
|
44
39
|
|
45
40
|
# @api private
|
46
|
-
def value?
|
47
|
-
|
48
|
-
|
41
|
+
def value? = false
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def factories = builder.factories
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def foreign_key = assoc.foreign_key
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def count = options.fetch(:count, 1)
|
49
51
|
end
|
50
52
|
|
51
53
|
# @api private
|
52
54
|
class ManyToOne < Core
|
53
55
|
# @api private
|
56
|
+
# rubocop:disable Metrics/AbcSize
|
54
57
|
def call(attrs, persist: true)
|
55
|
-
if attrs.key?(name) &&
|
58
|
+
return if attrs.key?(name) && attrs[name].nil?
|
59
|
+
|
60
|
+
assoc_data = attrs.fetch(name, EMPTY_HASH)
|
61
|
+
|
62
|
+
if assoc_data.is_a?(::Hash) && assoc_data[assoc.target.primary_key] && !attrs[foreign_key]
|
56
63
|
assoc.associate(attrs, attrs[name])
|
57
|
-
elsif
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
elsif assoc_data.is_a?(::ROM::Struct)
|
65
|
+
assoc.associate(attrs, assoc_data)
|
66
|
+
else
|
67
|
+
parent =
|
68
|
+
if persist && !attrs[foreign_key]
|
69
|
+
builder.persistable.create(*parent_traits, **assoc_data)
|
70
|
+
else
|
71
|
+
builder.struct(
|
72
|
+
*parent_traits,
|
73
|
+
**assoc_data, assoc.target.primary_key => attrs[foreign_key]
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
tuple = {name => parent}
|
78
|
+
|
79
|
+
assoc.associate(tuple, parent)
|
65
80
|
end
|
66
81
|
end
|
82
|
+
# rubocop:enable Metrics/AbcSize
|
67
83
|
|
68
|
-
|
69
|
-
|
70
|
-
|
84
|
+
private
|
85
|
+
|
86
|
+
def parent_traits
|
87
|
+
@parent_traits ||=
|
88
|
+
if assoc.target.associations.key?(assoc.source.name)
|
89
|
+
traits + [assoc.target.associations[assoc.source.name].key => false]
|
90
|
+
else
|
91
|
+
traits
|
92
|
+
end
|
71
93
|
end
|
72
94
|
end
|
73
95
|
|
@@ -77,7 +99,7 @@ module ROM::Factory
|
|
77
99
|
def call(attrs = EMPTY_HASH, parent, persist: true)
|
78
100
|
return if attrs.key?(name)
|
79
101
|
|
80
|
-
structs = Array.new(count).map do
|
102
|
+
structs = ::Array.new(count).map do
|
81
103
|
# hash which contains the foreign key info, i.e: { user_id: 1 }
|
82
104
|
association_hash = assoc.associate(attrs, parent)
|
83
105
|
|
@@ -92,14 +114,7 @@ module ROM::Factory
|
|
92
114
|
end
|
93
115
|
|
94
116
|
# @api private
|
95
|
-
def dependency?(rel)
|
96
|
-
assoc.source == rel
|
97
|
-
end
|
98
|
-
|
99
|
-
# @api private
|
100
|
-
def count
|
101
|
-
options.fetch(:count)
|
102
|
-
end
|
117
|
+
def dependency?(rel) = assoc.source == rel
|
103
118
|
end
|
104
119
|
|
105
120
|
# @api private
|
@@ -113,59 +128,78 @@ module ROM::Factory
|
|
113
128
|
|
114
129
|
association_hash = assoc.associate(attrs, parent)
|
115
130
|
|
116
|
-
struct =
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
131
|
+
struct =
|
132
|
+
if persist
|
133
|
+
builder.persistable.create(*traits, **association_hash)
|
134
|
+
else
|
135
|
+
belongs_to_name = ::ROM::Inflector.singularize(assoc.source_alias)
|
136
|
+
belongs_to_associations = {belongs_to_name.to_sym => parent}
|
137
|
+
final_attrs = attrs.merge(association_hash).merge(belongs_to_associations)
|
138
|
+
builder.struct(*traits, **final_attrs)
|
139
|
+
end
|
124
140
|
|
125
141
|
{name => struct}
|
126
142
|
end
|
127
|
-
|
128
|
-
# @api private
|
129
|
-
def count
|
130
|
-
options.fetch(:count, 1)
|
131
|
-
end
|
132
143
|
end
|
133
144
|
|
134
|
-
class
|
145
|
+
class ManyToMany < Core
|
135
146
|
def call(attrs = EMPTY_HASH, parent, persist: true)
|
136
147
|
return if attrs.key?(name)
|
137
148
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
149
|
+
structs = count.times.map do
|
150
|
+
if persist && attrs[tpk]
|
151
|
+
attrs
|
152
|
+
elsif persist
|
153
|
+
builder.persistable.create(*traits, **attrs)
|
154
|
+
else
|
155
|
+
builder.struct(*traits, **attrs)
|
156
|
+
end
|
157
|
+
end
|
145
158
|
|
146
|
-
|
159
|
+
# Delegate to through factory if it exists
|
160
|
+
if persist
|
161
|
+
if through_factory?
|
162
|
+
structs.each do |child|
|
163
|
+
through_attrs = {
|
164
|
+
::ROM::Inflector.singularize(assoc.source.name.key).to_sym => parent,
|
165
|
+
assoc.through.assoc_name => child
|
166
|
+
}
|
167
|
+
|
168
|
+
factories[through_factory_name, **through_attrs]
|
169
|
+
end
|
170
|
+
else
|
171
|
+
assoc.persist([parent], structs)
|
172
|
+
end
|
147
173
|
|
148
|
-
|
174
|
+
{name => result(structs)}
|
175
|
+
else
|
176
|
+
result(structs)
|
177
|
+
end
|
149
178
|
end
|
150
179
|
|
151
|
-
def
|
152
|
-
|
180
|
+
def result(structs) = {name => structs}
|
181
|
+
|
182
|
+
def dependency?(rel) = assoc.source == rel
|
183
|
+
|
184
|
+
def through? = true
|
185
|
+
|
186
|
+
def through_factory?
|
187
|
+
factories.registry.key?(through_factory_name)
|
153
188
|
end
|
154
189
|
|
155
|
-
def
|
156
|
-
|
190
|
+
def through_factory_name
|
191
|
+
::ROM::Inflector.singularize(assoc.definition.through.source).to_sym
|
157
192
|
end
|
158
193
|
|
159
194
|
private
|
160
195
|
|
161
|
-
def
|
162
|
-
|
163
|
-
end
|
196
|
+
def tpk = assoc.target.primary_key
|
197
|
+
end
|
164
198
|
|
165
|
-
|
166
|
-
|
167
|
-
end
|
199
|
+
class OneToOneThrough < ManyToMany
|
200
|
+
def result(structs) = {name => structs[0]}
|
168
201
|
end
|
169
202
|
end
|
170
203
|
end
|
204
|
+
# rubocop:enable Style/OptionalArguments
|
171
205
|
end
|
@@ -28,6 +28,12 @@ module ROM::Factory
|
|
28
28
|
def dependency_names
|
29
29
|
block.parameters.map(&:last)
|
30
30
|
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def inspect
|
34
|
+
"#<#{self.class.name} #{name} at #{block.source_location.join(":")}>"
|
35
|
+
end
|
36
|
+
alias_method :to_s, :inspect
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
@@ -22,8 +22,9 @@ module ROM
|
|
22
22
|
|
23
23
|
# @api private
|
24
24
|
def create(*traits, **attrs)
|
25
|
-
tuple = tuple(*traits, **attrs)
|
26
25
|
validate_keys(traits, attrs)
|
26
|
+
|
27
|
+
tuple = tuple(*traits, **attrs)
|
27
28
|
persisted = persist(tuple)
|
28
29
|
|
29
30
|
if tuple_evaluator.has_associations?(traits)
|
@@ -41,13 +42,29 @@ module ROM
|
|
41
42
|
|
42
43
|
# @api private
|
43
44
|
def persist(attrs)
|
44
|
-
relation
|
45
|
+
result = relation
|
46
|
+
.with(auto_struct: !tuple_evaluator.has_associations?)
|
47
|
+
.command(:create)
|
48
|
+
.call(attrs)
|
49
|
+
|
50
|
+
# Handle PK values generated by the factory
|
51
|
+
if pk? && (pks = attrs.values_at(*primary_key_names)).compact.size == primary_key_names.size
|
52
|
+
relation.by_pk(*pks).one!
|
53
|
+
elsif result
|
54
|
+
result
|
55
|
+
else
|
56
|
+
relation.where(attrs).one!
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
60
|
# @api private
|
48
61
|
def primary_key_names
|
49
62
|
relation.schema.primary_key.map(&:name)
|
50
63
|
end
|
64
|
+
|
65
|
+
def pk?
|
66
|
+
primary_key_names.any?
|
67
|
+
end
|
51
68
|
end
|
52
69
|
end
|
53
70
|
end
|
data/lib/rom/factory/builder.rb
CHANGED
@@ -28,6 +28,10 @@ module ROM::Factory
|
|
28
28
|
# @return [Module] Custom struct namespace
|
29
29
|
option :struct_namespace, reader: false
|
30
30
|
|
31
|
+
# @!attribute [r] factories
|
32
|
+
# @return [Module] Factories with other builders
|
33
|
+
option :factories, reader: true, optional: true
|
34
|
+
|
31
35
|
# @api private
|
32
36
|
def tuple(*traits, **attrs)
|
33
37
|
tuple_evaluator.defaults(traits, attrs)
|
@@ -57,7 +61,11 @@ module ROM::Factory
|
|
57
61
|
|
58
62
|
# @api private
|
59
63
|
def tuple_evaluator
|
60
|
-
@__tuple_evaluator__ ||= TupleEvaluator.new(
|
64
|
+
@__tuple_evaluator__ ||= TupleEvaluator.new(
|
65
|
+
attributes,
|
66
|
+
tuple_evaluator_relation,
|
67
|
+
traits
|
68
|
+
)
|
61
69
|
end
|
62
70
|
|
63
71
|
# @api private
|
data/lib/rom/factory/dsl.rb
CHANGED
@@ -12,26 +12,39 @@ module ROM
|
|
12
12
|
|
13
13
|
class << self
|
14
14
|
# @api private
|
15
|
-
def fake(*args, **options)
|
16
|
-
|
15
|
+
def fake(*args, unique: false, **options)
|
16
|
+
factory, produce = fetch_or_store(:faker, unique, *args) do
|
17
17
|
*ns, method_name = args
|
18
18
|
|
19
19
|
const = ns.reduce(::Faker) do |obj, name|
|
20
|
-
obj.const_get(::
|
20
|
+
obj.const_get(::ROM::Inflector.camelize(name))
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
if unique
|
24
|
+
[const.unique, method_name]
|
25
|
+
else
|
26
|
+
[const, method_name]
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
|
-
|
30
|
+
factory.public_send(produce, **options)
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
34
|
# Factory builder DSL
|
31
35
|
#
|
32
36
|
# @api public
|
33
|
-
class DSL < BasicObject
|
34
|
-
|
37
|
+
class DSL < ::BasicObject
|
38
|
+
# @api private
|
39
|
+
module Kernel
|
40
|
+
%i[binding class instance_of? is_a? rand respond_to_missing? singleton_class].each do |meth|
|
41
|
+
define_method(meth, ::Kernel.instance_method(meth))
|
42
|
+
end
|
43
|
+
|
44
|
+
private :respond_to_missing?, :rand, :binding
|
45
|
+
end
|
46
|
+
|
47
|
+
include Kernel
|
35
48
|
|
36
49
|
attr_reader :_name, :_relation, :_attributes, :_factories, :_struct_namespace, :_valid_names
|
37
50
|
attr_reader :_traits
|
@@ -50,7 +63,13 @@ module ROM
|
|
50
63
|
|
51
64
|
# @api private
|
52
65
|
def call
|
53
|
-
::ROM::Factory::Builder.new(
|
66
|
+
::ROM::Factory::Builder.new(
|
67
|
+
_attributes,
|
68
|
+
_traits,
|
69
|
+
relation: _relation,
|
70
|
+
struct_namespace: _struct_namespace,
|
71
|
+
factories: _factories
|
72
|
+
)
|
54
73
|
end
|
55
74
|
|
56
75
|
# Delegate to a builder and persist a struct
|
@@ -58,17 +77,15 @@ module ROM
|
|
58
77
|
# @param [Symbol] The name of the registered builder
|
59
78
|
#
|
60
79
|
# @api public
|
61
|
-
def create(name, *args)
|
62
|
-
_factories[name, *args]
|
63
|
-
end
|
80
|
+
def create(name, *args) = _factories[name, *args]
|
64
81
|
|
65
82
|
# Create a sequence attribute
|
66
83
|
#
|
67
84
|
# @param [Symbol] name The attribute name
|
68
85
|
#
|
69
86
|
# @api private
|
70
|
-
def sequence(meth, &
|
71
|
-
define_sequence(meth,
|
87
|
+
def sequence(meth, &)
|
88
|
+
define_sequence(meth, &) if _valid_names.include?(meth)
|
72
89
|
end
|
73
90
|
|
74
91
|
# Set timestamp attributes
|
@@ -98,9 +115,13 @@ module ROM
|
|
98
115
|
# @example
|
99
116
|
# f.email { fake(:number, :between, from: 10, to: 100) }
|
100
117
|
#
|
118
|
+
# @example
|
119
|
+
# f.email { fake(:internet, :email, unique: true) }
|
120
|
+
#
|
101
121
|
# @param [Symbol] genre The faker API identifier ie. :internet, :product etc.
|
102
122
|
# @param [Symbol] type The value type to generate
|
103
|
-
# @param [Hash] options Additional arguments
|
123
|
+
# @param [Hash] options Additional arguments, including unique: true will generate unique values
|
124
|
+
#
|
104
125
|
#
|
105
126
|
# @overload fake(genre, subgenre, type, **options)
|
106
127
|
# @example
|
@@ -114,11 +135,9 @@ module ROM
|
|
114
135
|
# @see https://github.com/faker-ruby/faker/tree/master/doc
|
115
136
|
#
|
116
137
|
# @api public
|
117
|
-
def fake(
|
118
|
-
::ROM::Factory.fake(type, *args, **options)
|
119
|
-
end
|
138
|
+
def fake(...) = ::ROM::Factory.fake(...)
|
120
139
|
|
121
|
-
def trait(name, parents = [], &
|
140
|
+
def trait(name, parents = [], &)
|
122
141
|
_traits[name] = DSL.new(
|
123
142
|
"#{_name}_#{name}",
|
124
143
|
attributes: _traits.values_at(*parents).flat_map(&:elements).inject(
|
@@ -127,7 +146,7 @@ module ROM
|
|
127
146
|
relation: _relation,
|
128
147
|
factories: _factories,
|
129
148
|
struct_namespace: _struct_namespace,
|
130
|
-
&
|
149
|
+
&
|
131
150
|
)._attributes
|
132
151
|
end
|
133
152
|
|
@@ -139,12 +158,16 @@ module ROM
|
|
139
158
|
# @example has-many
|
140
159
|
# f.association(:posts, count: 2)
|
141
160
|
#
|
161
|
+
# @example adding traits
|
162
|
+
# f.association(:posts, traits: [:published])
|
163
|
+
#
|
142
164
|
# @param [Symbol] name The name of the configured association
|
143
165
|
# @param [Hash] options Additional options
|
144
166
|
# @option options [Integer] count Number of objects to generate
|
167
|
+
# @option options [Array<Symbol>] traits Traits to apply to the association
|
145
168
|
#
|
146
169
|
# @api public
|
147
|
-
def association(name, *traits, **options)
|
170
|
+
def association(name, *seq_traits, traits: EMPTY_ARRAY, **options)
|
148
171
|
assoc = _relation.associations[name]
|
149
172
|
|
150
173
|
if assoc.is_a?(::ROM::SQL::Associations::OneToOne) && options.fetch(:count, 1) > 1
|
@@ -153,15 +176,25 @@ module ROM
|
|
153
176
|
|
154
177
|
builder = -> { _factories.for_relation(assoc.target) }
|
155
178
|
|
156
|
-
_attributes << attributes::Association.new(
|
179
|
+
_attributes << attributes::Association.new(
|
180
|
+
assoc,
|
181
|
+
builder,
|
182
|
+
*seq_traits,
|
183
|
+
*traits,
|
184
|
+
**options
|
185
|
+
)
|
157
186
|
end
|
158
187
|
|
188
|
+
# @api private
|
189
|
+
def inspect = "#<#{self.class} name=#{_name}>"
|
190
|
+
alias_method :to_s, :inspect
|
191
|
+
|
159
192
|
private
|
160
193
|
|
161
194
|
# @api private
|
162
|
-
def method_missing(meth,
|
195
|
+
def method_missing(meth, ...)
|
163
196
|
if _valid_names.include?(meth)
|
164
|
-
define_attr(meth,
|
197
|
+
define_attr(meth, ...)
|
165
198
|
else
|
166
199
|
super
|
167
200
|
end
|
@@ -169,27 +202,26 @@ module ROM
|
|
169
202
|
|
170
203
|
# @api private
|
171
204
|
def respond_to_missing?(method_name, include_private = false)
|
172
|
-
_valid_names.include?(
|
205
|
+
_valid_names.include?(method_name) || super
|
173
206
|
end
|
174
207
|
|
175
208
|
# @api private
|
176
|
-
def define_sequence(name,
|
177
|
-
_attributes << attributes::Callable.new(name, self, attributes::Sequence.new(name, &
|
209
|
+
def define_sequence(name, &)
|
210
|
+
_attributes << attributes::Callable.new(name, self, attributes::Sequence.new(name, &))
|
178
211
|
end
|
179
212
|
|
180
213
|
# @api private
|
181
214
|
def define_attr(name, *args, &block)
|
182
|
-
_attributes <<
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
215
|
+
_attributes <<
|
216
|
+
if block
|
217
|
+
attributes::Callable.new(name, self, block)
|
218
|
+
else
|
219
|
+
attributes::Value.new(name, *args)
|
220
|
+
end
|
187
221
|
end
|
188
222
|
|
189
223
|
# @api private
|
190
|
-
def attributes
|
191
|
-
::ROM::Factory::Attributes
|
192
|
-
end
|
224
|
+
def attributes = ::ROM::Factory::Attributes
|
193
225
|
end
|
194
226
|
end
|
195
227
|
end
|
@@ -126,7 +126,7 @@ module ROM::Factory
|
|
126
126
|
# @return [ROM::Factory::Builder]
|
127
127
|
#
|
128
128
|
# @api public
|
129
|
-
def define(spec, opts = EMPTY_HASH, &
|
129
|
+
def define(spec, opts = EMPTY_HASH, &)
|
130
130
|
name, parent = spec.is_a?(Hash) ? spec.flatten(1) : spec
|
131
131
|
namespace = opts[:struct_namespace]
|
132
132
|
relation_name = opts.fetch(:relation) { infer_relation(name) }
|
@@ -137,7 +137,7 @@ module ROM::Factory
|
|
137
137
|
|
138
138
|
builder =
|
139
139
|
if parent
|
140
|
-
extend_builder(name, registry[parent], relation_name, namespace, &
|
140
|
+
extend_builder(name, registry[parent], relation_name, namespace, &)
|
141
141
|
else
|
142
142
|
relation = rom.relations[relation_name]
|
143
143
|
DSL.new(
|
@@ -145,7 +145,7 @@ module ROM::Factory
|
|
145
145
|
relation: relation,
|
146
146
|
factories: self,
|
147
147
|
struct_namespace: builder_struct_namespace(namespace),
|
148
|
-
&
|
148
|
+
&
|
149
149
|
).call
|
150
150
|
end
|
151
151
|
|
@@ -170,6 +170,7 @@ module ROM::Factory
|
|
170
170
|
def [](name, *traits, **attrs)
|
171
171
|
registry[name].struct_namespace(struct_namespace).persistable.create(*traits, **attrs)
|
172
172
|
end
|
173
|
+
alias_method :create, :[]
|
173
174
|
|
174
175
|
# Return in-memory struct builder
|
175
176
|
#
|
@@ -180,6 +181,25 @@ module ROM::Factory
|
|
180
181
|
@__structs__ ||= Structs.new(registry, struct_namespace)
|
181
182
|
end
|
182
183
|
|
184
|
+
# Return a new, non-persisted struct
|
185
|
+
#
|
186
|
+
# @example create a struct with default attributes
|
187
|
+
# MyFactory.build(:user)
|
188
|
+
#
|
189
|
+
# @example create a struct with some attributes overridden
|
190
|
+
# MyFactory.build(:uesr, name: "Jane")
|
191
|
+
#
|
192
|
+
# @param [Symbol] name The name of the registered factory
|
193
|
+
# @param [Array<Symbol>] traits List of traits to apply
|
194
|
+
# @param [Hash] attrs optional attributes to override the defaults
|
195
|
+
#
|
196
|
+
# @return [ROM::Struct]
|
197
|
+
#
|
198
|
+
# @api public
|
199
|
+
def build(name, *traits, **attrs)
|
200
|
+
structs[name, *traits, **attrs]
|
201
|
+
end
|
202
|
+
|
183
203
|
# Get factories with a custom struct namespace
|
184
204
|
#
|
185
205
|
# @example
|
@@ -213,16 +233,16 @@ module ROM::Factory
|
|
213
233
|
|
214
234
|
# @api private
|
215
235
|
def infer_factory_name(name)
|
216
|
-
::
|
236
|
+
::ROM::Inflector.singularize(name).to_sym
|
217
237
|
end
|
218
238
|
|
219
239
|
# @api private
|
220
240
|
def infer_relation(name)
|
221
|
-
::
|
241
|
+
::ROM::Inflector.pluralize(name).to_sym
|
222
242
|
end
|
223
243
|
|
224
244
|
# @api private
|
225
|
-
def extend_builder(name, parent, relation_name, ns, &
|
245
|
+
def extend_builder(name, parent, relation_name, ns, &)
|
226
246
|
namespace = parent.options[:struct_namespace]
|
227
247
|
namespace = builder_struct_namespace(ns) if ns
|
228
248
|
relation = rom.relations.fetch(relation_name) { parent.relation }
|
@@ -232,7 +252,7 @@ module ROM::Factory
|
|
232
252
|
relation: relation,
|
233
253
|
factories: self,
|
234
254
|
struct_namespace: namespace,
|
235
|
-
&
|
255
|
+
&
|
236
256
|
).call
|
237
257
|
end
|
238
258
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "concurrent/map"
|
3
4
|
require "singleton"
|
4
5
|
|
5
6
|
module ROM
|
@@ -24,12 +25,12 @@ module ROM
|
|
24
25
|
|
25
26
|
# @api private
|
26
27
|
def next(key)
|
27
|
-
registry
|
28
|
+
registry.compute(key) { |v| (v || 0).succ }
|
28
29
|
end
|
29
30
|
|
30
31
|
# @api private
|
31
32
|
def reset
|
32
|
-
@registry = Concurrent::Map.new
|
33
|
+
@registry = Concurrent::Map.new
|
33
34
|
self
|
34
35
|
end
|
35
36
|
end
|