rom-factory 0.11.0 → 0.12.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: 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