rom-factory 0.11.0 → 0.12.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: ef868642e36f9a4e7b18c91925f378a2c0da27bd4c7ac6d048650c0c739ce3d9
4
- data.tar.gz: 8815cd6d8318b35298ff341e5308e7b76692be6d6af58b076a9c5ddd23688bd2
3
+ metadata.gz: 0cae4bd0ca2ec550f5fdfb9e17151f6e4be3b575f382935281721f75f905f46b
4
+ data.tar.gz: 1c6291ac1413d16b15fe946378d1eb59e08d8bafa61f2c8eb7743412bd53d31a
5
5
  SHA512:
6
- metadata.gz: fb80a101f1b6c380ee4660d49756c02a0af58ed60d8c53d7a97ba5f94b5a04b37255a11018c7f47fe0665b71ba4a47ee70c65b1c6418c0d642b7e662bc5beffa
7
- data.tar.gz: 9f74c1af825e6927a2f1926963c39ac41b7b74471e2795cd6e8b28d1aca839c8fe1a1c688d4116e371a759ababa0b5df3147fcf8ae6b15fc0aaa5418fc4af9db
6
+ metadata.gz: 14b6dcc4ea8a9077684b372baba986a0bd51b081ab75faadb954156c9b09dbcdf5f05bfe1a0cfc886e915b461c13afa8c0d42213337264188d438a0064ab8c34
7
+ data.tar.gz: 5705eb8df8fd99a9ff93c53b769a3c3c7f09749740642abf9a2d8f6907b67104147286d667d18de8ae372c8bafd7edad86d1baba8273cd2e67613ecc68a474c9
@@ -23,9 +23,10 @@ jobs:
23
23
  fail-fast: false
24
24
  matrix:
25
25
  ruby:
26
+ - '3.3'
27
+ - '3.2'
26
28
  - '3.1'
27
29
  - '3.0'
28
- - '2.7'
29
30
  env:
30
31
  COVERAGE: "${{matrix.coverage}}"
31
32
  COVERAGE_TOKEN: "${{secrets.CODACY_PROJECT_TOKEN}}"
@@ -78,7 +79,7 @@ jobs:
78
79
  - name: Set up Ruby
79
80
  uses: ruby/setup-ruby@v1
80
81
  with:
81
- ruby-version: 2.6
82
+ ruby-version: "3.1"
82
83
  - name: Install dependencies
83
84
  run: gem install ossy --no-document
84
85
  - name: Trigger release workflow
@@ -22,9 +22,9 @@ jobs:
22
22
  - run: |
23
23
  git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
24
24
  - name: Set up Ruby
25
- uses: actions/setup-ruby@v1
25
+ uses: ruby/setup-ruby@v1
26
26
  with:
27
- ruby-version: "2.6.x"
27
+ ruby-version: "3.1"
28
28
  - name: Set up git user
29
29
  run: |
30
30
  git config --local user.email "rom-bot@rom-rb.org"
@@ -60,4 +60,3 @@ jobs:
60
60
  GITHUB_LOGIN: rom-bot
61
61
  GITHUB_TOKEN: ${{secrets.GH_PAT}}
62
62
  run: ossy github workflow rom-rb/rom-rb.org ci
63
-
@@ -1,31 +1,25 @@
1
1
  # This file is synced from rom-rb/template-gem repo
2
2
 
3
- name: Changelog
3
+ name: sync
4
4
 
5
5
  on:
6
+ repository_dispatch:
6
7
  push:
7
- paths:
8
- - "changelog.yml"
9
8
  branches:
10
9
  - "main"
11
- pull_request:
12
- branches:
13
- - "main"
14
- types: [closed]
15
10
 
16
11
  jobs:
17
- sync:
12
+ main:
18
13
  runs-on: ubuntu-latest
19
- if: github.event.pull_request.merged == true
20
- name: Update
14
+ if: (github.event_name == 'repository_dispatch' && github.event.action == 'sync_configs') || github.event_name != 'repository_dispatch'
21
15
  env:
22
16
  GITHUB_LOGIN: rom-bot
23
17
  GITHUB_TOKEN: ${{ secrets.GH_PAT }}
24
18
  steps:
25
19
  - name: Checkout ${{github.repository}}
26
- uses: actions/checkout@v1
20
+ uses: actions/checkout@v3
27
21
  - name: Checkout devtools
28
- uses: actions/checkout@v2
22
+ uses: actions/checkout@v3
29
23
  with:
30
24
  repository: rom-rb/devtools
31
25
  path: tmp/devtools
@@ -34,9 +28,9 @@ jobs:
34
28
  git config --local user.email "rom-bot@rom-rb.org"
35
29
  git config --local user.name "rom-bot"
36
30
  - name: Set up Ruby
37
- uses: actions/setup-ruby@v1
31
+ uses: ruby/setup-ruby@v1
38
32
  with:
39
- ruby-version: "2.6"
33
+ ruby-version: "3.1"
40
34
  - name: Install dependencies
41
35
  run: gem install ossy --no-document
42
36
  - name: Update changelog.yml from commit
@@ -46,7 +40,7 @@ jobs:
46
40
  - name: Commit
47
41
  run: |
48
42
  git add -A
49
- git commit -m "Update CHANGELOG.md" || echo "nothing to commit"
43
+ git commit -m "[devtools] sync" || echo "nothing to commit"
50
44
  - name: Push changes
51
45
  run: |
52
46
  git pull --rebase origin main
@@ -1,12 +1,17 @@
1
+ ###########################################################
2
+ # DO NOT EDIT THIS FILE
3
+ #
1
4
  # This is a config synced from rom-rb/template-gem repo
5
+ ###########################################################
2
6
 
3
7
  sources:
4
8
  - repo: rom-rb/template-gem
5
9
  sync:
6
- - "!.github/workflows/ci.yml.erb"
7
- - ".action_hero.yml.erb"
10
+ - ".repobot.yml.erb"
8
11
  - ".devtools/templates/*.sync:${{dir}}/${{name}}"
9
12
  - ".github/**/*.*"
13
+ - ".rspec"
14
+ - ".rubocop.yml"
10
15
  - "spec/support/*"
11
16
  - "CODE_OF_CONDUCT.md"
12
17
  - "CONTRIBUTING.md"
@@ -14,8 +19,6 @@ sources:
14
19
  - "LICENSE.erb"
15
20
  - "README.erb"
16
21
  - "Gemfile.devtools"
17
- - ".rspec"
18
- - ".rubocop.yml"
19
- - repo: action-hero/workflows
22
+ - repo: repobot-app/workflows
20
23
  sync:
21
24
  - ".github/workflows/*.yml"
data/CHANGELOG.md CHANGED
@@ -1,35 +1,55 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
- ## 0.11.0 2022-11-11
3
+ ## 0.12.0 2024-01-19
4
+
5
+
6
+ ### Added
7
+
8
+ - Support for many-to-many and one-to-one-through associations (via
9
+ - Support for UUID as PKs in associations (via
4
10
 
5
11
  ### Fixed
6
12
 
7
- - Support for plural Faker generators (@wuarmin)
13
+ - Relations without PKs should work too (via
14
+ - Relations with PK values generated on the Ruby side should work in SQlite too (via
15
+
16
+
17
+ [Compare v0.11.0...v0.12.0](https://github.com/rom-rb/rom-factory/compare/v0.11.0...v0.12.0)
18
+
19
+ ## 0.11.0 2022-11-11
20
+
8
21
 
9
22
  ### Added
10
23
 
11
24
  - Support for one-to-one associations (@ianks)
12
25
  - [internal] cache for Faker constants (@flash-gordon)
13
26
 
27
+ ### Fixed
28
+
29
+ - Support for plural Faker generators (@wuarmin)
30
+
14
31
  ### Changed
15
32
 
16
33
  - [BREAKING] attributes are always passed as keywords (@alassek)
17
- This may affect your code in places where attributes are passed as hashes.
18
- Places like
34
+ This may affect your code in places where attributes are passed as hashes.
35
+ Places like
36
+
37
+ ```ruby
38
+ user_attributes = { name: 'Jane' }
39
+ Factory[:user, user_attributes]
40
+
41
+ ```
42
+
43
+ must be updated to
44
+
45
+ ```ruby
46
+ user_attributes = { name: 'Jane' }
47
+ Factory[:user, **user_attributes]
48
+ ```
19
49
 
20
- ```ruby
21
- user_attributes = { name: 'Jane' }
22
- Factory[:user, user_attributes]
23
- ```
24
- must be updated to
25
- ```ruby
26
- user_attributes = { name: 'Jane' }
27
- Factory[:user, **user_attributes]
28
- ```
29
50
  - Upgraded to the latest versions of dry-rb dependencies, compatible with rom 5.3 (@flash-gordon)
30
51
  - Support for Faker 1.x was dropped (@alassek)
31
52
 
32
-
33
53
  [Compare v0.10.2...v0.11.0](https://github.com/rom-rb/rom-factory/compare/v0.10.2...v0.11.0)
34
54
 
35
55
  ## 0.10.2 2020-04-05
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ gemspec
6
6
 
7
7
  eval_gemfile "Gemfile.devtools"
8
8
 
9
- gem "faker", "~> 2.8"
9
+ gem "faker", "~> 3.0"
10
10
 
11
11
  gem "rspec", "~> 3.0"
12
12
 
@@ -18,12 +18,12 @@ git "https://github.com/rom-rb/rom.git", branch: "release-5.3" do
18
18
  end
19
19
 
20
20
  group :test do
21
- gem "pry", "~> 0.12.0", "<= 0.13"
21
+ gem "pry"
22
22
  gem "pry-byebug", "~> 3.8", platforms: :ruby
23
23
  gem "rom-sql", github: "rom-rb/rom-sql", branch: "release-3.6"
24
24
 
25
25
  gem "jdbc-postgres", platforms: :jruby
26
- gem "pg", "~> 0.21", platforms: :ruby
26
+ gem "pg", "~> 1.5", platforms: :ruby
27
27
  end
28
28
 
29
29
  group :tools do
data/changelog.yml CHANGED
@@ -1,4 +1,12 @@
1
1
  ---
2
+ - version: 0.12.0
3
+ date: 2024-01-19
4
+ added:
5
+ - Support for many-to-many and one-to-one-through associations (via #86) (@solnic)
6
+ - Support for UUID as PKs in associations (via #87) (@solnic)
7
+ fixed:
8
+ - Relations without PKs should work too (via #87) (@solnic)
9
+ - Relations with PK values generated on the Ruby side should work in SQlite too (via #87) (@solnic)
2
10
  - version: 0.11.0
3
11
  date: 2022-11-11
4
12
  added:
@@ -9,11 +17,15 @@
9
17
  [BREAKING] attributes are always passed as keywords (@alassek)
10
18
  This may affect your code in places where attributes are passed as hashes.
11
19
  Places like
20
+
12
21
  ```ruby
13
22
  user_attributes = { name: 'Jane' }
14
23
  Factory[:user, user_attributes]
24
+
15
25
  ```
26
+
16
27
  must be updated to
28
+
17
29
  ```ruby
18
30
  user_attributes = { name: 'Jane' }
19
31
  Factory[:user, **user_attributes]
@@ -44,7 +44,7 @@ end
44
44
 
45
45
  #### Specify namespace for your structs
46
46
 
47
- Struct `User` will be find in MyApp::Entities namespace
47
+ Struct `User` will be found in `MyApp::Entities` namespace
48
48
 
49
49
  ```ruby
50
50
  Factory.define(:user, struct_namespace: MyApp::Entities) do |f|
@@ -86,7 +86,7 @@ end
86
86
 
87
87
  Factory.define(:user) do |f|
88
88
  f.name 'John'
89
- f.association(:user)
89
+ f.association(:group)
90
90
  end
91
91
  ```
92
92
 
@@ -115,7 +115,7 @@ Factory.define(admin: :user) do |f|
115
115
  f.admin true
116
116
  end
117
117
 
118
- # Factory.structs(:admin)
118
+ # Factory.structs[:admin]
119
119
  ```
120
120
 
121
121
  #### Traits
@@ -130,7 +130,7 @@ Factory.define(:user) do |f|
130
130
  end
131
131
  end
132
132
 
133
- # Factory.structs(:user, :with_age)
133
+ # Factory.structs[:user, :with_age]
134
134
  ```
135
135
 
136
136
  #### Build-in [Faker](https://github.com/faker-ruby/faker) objects
@@ -50,6 +50,10 @@ module ROM
50
50
  self.class.new(elements.select { |e| e.is_a?(Attributes::Association::Core) })
51
51
  end
52
52
 
53
+ def reject(&block)
54
+ self.class.new(elements.reject(&block))
55
+ end
56
+
53
57
  private
54
58
 
55
59
  # @api private
@@ -3,6 +3,7 @@
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
9
  def new(assoc, builder, *traits, **options)
@@ -46,28 +47,62 @@ module ROM::Factory
46
47
  def value?
47
48
  false
48
49
  end
50
+
51
+ # @api private
52
+ def factories
53
+ builder.factories
54
+ end
55
+
56
+ # @api private
57
+ def foreign_key
58
+ assoc.foreign_key
59
+ end
60
+
61
+ # @api private
62
+ def count
63
+ options.fetch(:count, 1)
64
+ end
49
65
  end
50
66
 
51
67
  # @api private
52
68
  class ManyToOne < Core
53
69
  # @api private
70
+ # rubocop:disable Metrics/AbcSize
54
71
  def call(attrs, persist: true)
55
- if attrs.key?(name) && !attrs[foreign_key]
72
+ return if attrs.key?(name) && attrs[name].nil?
73
+
74
+ assoc_data = attrs.fetch(name, EMPTY_HASH)
75
+
76
+ if assoc_data.is_a?(Hash) && assoc_data[assoc.target.primary_key] && !attrs[foreign_key]
56
77
  assoc.associate(attrs, attrs[name])
57
- elsif !attrs[foreign_key]
58
- struct = if persist
59
- builder.persistable.create(*traits)
78
+ elsif assoc_data.is_a?(ROM::Struct)
79
+ assoc.associate(attrs, assoc_data)
80
+ else
81
+ parent = if persist && !attrs[foreign_key]
82
+ builder.persistable.create(*parent_traits, **assoc_data)
60
83
  else
61
- builder.struct(*traits)
84
+ builder.struct(
85
+ *parent_traits,
86
+ **assoc_data.merge(assoc.target.primary_key => attrs[foreign_key])
87
+ )
62
88
  end
63
- tuple = {name => struct}
64
- assoc.associate(tuple, struct)
89
+
90
+ tuple = {name => parent}
91
+
92
+ assoc.associate(tuple, parent)
65
93
  end
66
94
  end
95
+ # rubocop:enable Metrics/AbcSize
67
96
 
68
- # @api private
69
- def foreign_key
70
- assoc.foreign_key
97
+ private
98
+
99
+ def parent_traits
100
+ @parent_traits ||=
101
+ if assoc.target.associations.key?(assoc.source.name)
102
+ traits + [assoc.target.associations[assoc.source.name].key => false]
103
+ else
104
+ traits
105
+ end
71
106
  end
72
107
  end
73
108
 
@@ -95,11 +130,6 @@ module ROM::Factory
95
130
  def dependency?(rel)
96
131
  assoc.source == rel
97
132
  end
98
-
99
- # @api private
100
- def count
101
- options.fetch(:count)
102
- end
103
133
  end
104
134
 
105
135
  # @api private
@@ -124,28 +154,45 @@ module ROM::Factory
124
154
 
125
155
  {name => struct}
126
156
  end
127
-
128
- # @api private
129
- def count
130
- options.fetch(:count, 1)
131
- end
132
157
  end
133
158
 
134
- class OneToOneThrough < Core
159
+ class ManyToMany < Core
135
160
  def call(attrs = EMPTY_HASH, parent, persist: true)
136
161
  return if attrs.key?(name)
137
162
 
138
- struct = if persist && attrs[tpk]
139
- attrs
140
- elsif persist
141
- builder.persistable.create(*traits, **attrs)
142
- else
143
- builder.struct(*traits, **attrs)
144
- end
163
+ structs = count.times.map do
164
+ if persist && attrs[tpk]
165
+ attrs
166
+ elsif persist
167
+ builder.persistable.create(*traits, **attrs)
168
+ else
169
+ builder.struct(*traits, **attrs)
170
+ end
171
+ end
145
172
 
146
- assoc.persist([parent], struct) if persist
173
+ # Delegate to through factory if it exists
174
+ if persist
175
+ if through_factory?
176
+ structs.each do |child|
177
+ through_attrs = {
178
+ Dry::Core::Inflector.singularize(assoc.source.name.key).to_sym => parent,
179
+ assoc.through.assoc_name => child
180
+ }
181
+
182
+ factories[through_factory_name, **through_attrs]
183
+ end
184
+ else
185
+ assoc.persist([parent], structs)
186
+ end
147
187
 
148
- {name => struct}
188
+ {name => result(structs)}
189
+ else
190
+ result(structs)
191
+ end
192
+ end
193
+
194
+ def result(structs)
195
+ {name => structs}
149
196
  end
150
197
 
151
198
  def dependency?(rel)
@@ -156,16 +203,27 @@ module ROM::Factory
156
203
  true
157
204
  end
158
205
 
159
- private
206
+ def through_factory?
207
+ factories.registry.key?(through_factory_name)
208
+ end
160
209
 
161
- def count
162
- options.fetch(:count, 1)
210
+ def through_factory_name
211
+ ROM::Inflector.singularize(assoc.definition.through.source).to_sym
163
212
  end
164
213
 
214
+ private
215
+
165
216
  def tpk
166
217
  assoc.target.primary_key
167
218
  end
168
219
  end
220
+
221
+ class OneToOneThrough < ManyToMany
222
+ def result(structs)
223
+ {name => structs[0]}
224
+ end
225
+ end
169
226
  end
170
227
  end
228
+ # rubocop:enable Style/OptionalArguments
171
229
  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.with(auto_struct: !tuple_evaluator.has_associations?).command(:create).call(attrs)
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
@@ -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(attributes, tuple_evaluator_relation, traits)
64
+ @__tuple_evaluator__ ||= TupleEvaluator.new(
65
+ attributes,
66
+ tuple_evaluator_relation,
67
+ traits
68
+ )
61
69
  end
62
70
 
63
71
  # @api private
@@ -50,7 +50,13 @@ module ROM
50
50
 
51
51
  # @api private
52
52
  def call
53
- ::ROM::Factory::Builder.new(_attributes, _traits, relation: _relation, struct_namespace: _struct_namespace)
53
+ ::ROM::Factory::Builder.new(
54
+ _attributes,
55
+ _traits,
56
+ relation: _relation,
57
+ struct_namespace: _struct_namespace,
58
+ factories: _factories
59
+ )
54
60
  end
55
61
 
56
62
  # Delegate to a builder and persist a struct
@@ -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[key] += 1
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 { |h, k| h[k] = 0 }
33
+ @registry = Concurrent::Map.new
33
34
  self
34
35
  end
35
36
  end
@@ -6,6 +6,29 @@ module ROM
6
6
  module Factory
7
7
  # @api private
8
8
  class TupleEvaluator
9
+ class TupleEvaluatorError < StandardError
10
+ attr_reader :original_exception
11
+
12
+ def initialize(relation, original_exception, attrs, traits, assoc_attrs)
13
+ super(<<~STR)
14
+ Failed to build attributes for #{relation.name}
15
+
16
+ Attributes:
17
+ #{attrs.inspect}
18
+
19
+ Associations:
20
+ #{assoc_attrs}
21
+
22
+ Traits:
23
+ #{traits.inspect}
24
+
25
+ Original exception: #{original_exception.message}
26
+ STR
27
+
28
+ set_backtrace(original_exception.backtrace)
29
+ end
30
+ end
31
+
9
32
  # @api private
10
33
  attr_reader :attributes
11
34
 
@@ -15,9 +38,6 @@ module ROM
15
38
  # @api private
16
39
  attr_reader :traits
17
40
 
18
- # @api private
19
- attr_reader :model
20
-
21
41
  # @api private
22
42
  attr_reader :sequence
23
43
 
@@ -26,10 +46,13 @@ module ROM
26
46
  @attributes = attributes
27
47
  @relation = relation.with(auto_struct: true)
28
48
  @traits = traits
29
- @model = @relation.combine(*assoc_names).mapper.model
30
49
  @sequence = Sequences[relation]
31
50
  end
32
51
 
52
+ def model(traits, combine: assoc_names(traits))
53
+ @relation.combine(*combine).mapper.model
54
+ end
55
+
33
56
  # @api private
34
57
  def defaults(traits, attrs, **opts)
35
58
  mergeable_attrs = select_mergeable_attrs(traits, attrs)
@@ -45,21 +68,41 @@ module ROM
45
68
  attributes = merged_attrs.reject(&is_callable)
46
69
 
47
70
  materialized_callables = {}
48
- callables.each do |_name, callable|
71
+ callables.each_value do |callable|
49
72
  materialized_callables.merge!(callable.call(attributes, persist: false))
50
73
  end
51
74
 
52
75
  attributes.merge!(materialized_callables)
53
76
 
54
- associations = assoc_names
55
- .map { |key| [key, attributes[key]] if attributes.key?(key) }
56
- .compact
77
+ assoc_attrs = attributes.slice(*assoc_names(traits)).merge(
78
+ assoc_names(traits)
79
+ .select { |key|
80
+ build_assoc?(key, attributes)
81
+ }
82
+ .map { |key|
83
+ [key, build_assoc_attrs(key, attributes[relation.primary_key], attributes[key])]
84
+ }
57
85
  .to_h
86
+ )
87
+
88
+ model_attrs = relation.output_schema[attributes]
89
+ model_attrs.update(assoc_attrs)
90
+
91
+ model(traits).new(**model_attrs)
92
+ rescue StandardError => e
93
+ raise TupleEvaluatorError.new(relation, e, attrs, traits, assoc_attrs)
94
+ end
58
95
 
59
- attributes = relation.output_schema[attributes]
60
- attributes.update(associations)
96
+ def build_assoc?(name, attributes)
97
+ attributes.key?(name) && attributes[name] != [] && !attributes[name].nil?
98
+ end
61
99
 
62
- model.new(attributes)
100
+ def build_assoc_attrs(key, fk, value)
101
+ if value.is_a?(Array)
102
+ value.map { |el| build_assoc_attrs(key, fk, el) }
103
+ else
104
+ {attributes[key].foreign_key => fk}.merge(value.to_h)
105
+ end
63
106
  end
64
107
 
65
108
  # @api private
@@ -76,10 +119,15 @@ module ROM
76
119
  end
77
120
 
78
121
  def assocs(traits_names = [])
79
- traits
122
+ found_assocs = traits
80
123
  .values_at(*traits_names)
124
+ .compact
81
125
  .map(&:associations).flat_map(&:elements)
82
126
  .inject(AttributeRegistry.new(attributes.associations.elements), :<<)
127
+
128
+ exclude = traits_names.select { |t| t.is_a?(Hash) }.reduce(:merge) || EMPTY_HASH
129
+
130
+ found_assocs.reject { |a| exclude[a.name] == false }
83
131
  end
84
132
 
85
133
  # @api private
@@ -97,7 +145,7 @@ module ROM
97
145
  # @api private
98
146
  def evaluate(traits, attrs, opts)
99
147
  evaluate_values(attrs)
100
- .merge(evaluate_associations(attrs, opts))
148
+ .merge(evaluate_associations(traits, attrs, opts))
101
149
  .merge(evaluate_traits(traits, attrs, opts))
102
150
  end
103
151
 
@@ -113,24 +161,29 @@ module ROM
113
161
  end
114
162
  end
115
163
 
116
- def evaluate_traits(traits, attrs, opts)
117
- return {} if traits.empty?
164
+ def evaluate_traits(trait_list, attrs, opts)
165
+ return {} if trait_list.empty?
166
+
167
+ traits = trait_list.map { |v| v.is_a?(Hash) ? v : {v => true} }.reduce(:merge)
118
168
 
119
- traits_attrs = self.traits.values_at(*traits).flat_map(&:elements)
169
+ traits_attrs = self.traits.select { |key, _value| traits[key] }.values.flat_map(&:elements)
120
170
  registry = AttributeRegistry.new(traits_attrs)
171
+
121
172
  self.class.new(registry, relation).defaults([], attrs, **opts)
122
173
  end
123
174
 
124
175
  # @api private
125
- def evaluate_associations(attrs, opts)
126
- attributes.associations.each_with_object({}) do |assoc, h|
127
- if assoc.dependency?(relation)
128
- h[assoc.name] = ->(parent, call_opts) do
176
+ def evaluate_associations(traits, attrs, opts)
177
+ assocs(traits).associations.each_with_object({}) do |assoc, memo|
178
+ if attrs.key?(assoc.name) && attrs[assoc.name].nil?
179
+ memo
180
+ elsif assoc.dependency?(relation)
181
+ memo[assoc.name] = ->(parent, call_opts) do
129
182
  assoc.call(parent, **opts, **call_opts)
130
183
  end
131
184
  else
132
185
  result = assoc.(attrs, **opts)
133
- h.update(result) if result
186
+ memo.update(result) if result
134
187
  end
135
188
  end
136
189
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ROM
4
4
  module Factory
5
- VERSION = "0.11.0"
5
+ VERSION = "0.12.0"
6
6
  end
7
7
  end
data/rom-factory.gemspec CHANGED
@@ -30,6 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.add_runtime_dependency "dry-configurable", "~> 1.0"
31
31
  spec.add_runtime_dependency "dry-core", "~> 1.0"
32
32
  spec.add_runtime_dependency "dry-struct", "~> 1.6"
33
- spec.add_runtime_dependency "faker", ">= 2.0", "< 3.0"
33
+ spec.add_runtime_dependency "faker", ">= 2.0", "< 4"
34
34
  spec.add_runtime_dependency "rom-core", "~> 5.3"
35
35
  end
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.11.0
4
+ version: 0.12.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: 2022-11-11 00:00:00.000000000 Z
12
+ date: 2024-01-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dry-configurable
@@ -62,7 +62,7 @@ dependencies:
62
62
  version: '2.0'
63
63
  - - "<"
64
64
  - !ruby/object:Gem::Version
65
- version: '3.0'
65
+ version: '4'
66
66
  type: :runtime
67
67
  prerelease: false
68
68
  version_requirements: !ruby/object:Gem::Requirement
@@ -72,7 +72,7 @@ dependencies:
72
72
  version: '2.0'
73
73
  - - "<"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.0'
75
+ version: '4'
76
76
  - !ruby/object:Gem::Dependency
77
77
  name: rom-core
78
78
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +95,6 @@ executables: []
95
95
  extensions: []
96
96
  extra_rdoc_files: []
97
97
  files:
98
- - ".action_hero.yml"
99
98
  - ".devtools/templates/changelog.erb"
100
99
  - ".devtools/templates/release.erb"
101
100
  - ".github/FUNDING.yml"
@@ -107,6 +106,7 @@ files:
107
106
  - ".github/workflows/rubocop.yml"
108
107
  - ".github/workflows/sync_configs.yml"
109
108
  - ".gitignore"
109
+ - ".repobot.yml"
110
110
  - ".rspec"
111
111
  - ".rubocop.yml"
112
112
  - ".yardopts"
@@ -161,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
161
  - !ruby/object:Gem::Version
162
162
  version: '0'
163
163
  requirements: []
164
- rubygems_version: 3.3.7
164
+ rubygems_version: 3.5.5
165
165
  signing_key:
166
166
  specification_version: 4
167
167
  summary: ROM based builder library to make your specs awesome. DSL partially inspired