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.
@@ -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,15 +145,15 @@ 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
 
104
152
  # @api private
105
153
  def evaluate_values(attrs)
106
- attributes.values.tsort.each_with_object({}) do |attr, h|
107
- deps = attr.dependency_names.map { |k| h[k] }.compact
108
- result = attr.(attrs, *deps)
154
+ attributes.values.tsort.each_with_object(attrs.dup) do |attr, h|
155
+ deps = attr.dependency_names.filter_map { |k| h[k] }
156
+ result = attr.(h, *deps)
109
157
 
110
158
  if result
111
159
  h.update(result)
@@ -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 EMPTY_HASH 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
@@ -155,7 +208,7 @@ module ROM
155
208
 
156
209
  def select_mergeable_attrs(traits, attrs)
157
210
  unmergeable = assocs(traits).select(&:through?).map do |a|
158
- Dry::Core::Inflector.singularize(a.assoc.target.name.to_sym).to_sym
211
+ ::ROM::Inflector.singularize(a.assoc.target.name.to_sym).to_sym
159
212
  end
160
213
  attrs.dup.delete_if { |key, _| unmergeable.include?(key) }
161
214
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ROM
4
4
  module Factory
5
- VERSION = "0.11.0"
5
+ VERSION = "0.13.0"
6
6
  end
7
7
  end
data/rom-factory.gemspec CHANGED
@@ -1,9 +1,8 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
5
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
- require 'rom/factory/version'
5
+ require "rom/factory/version"
7
6
 
8
7
  Gem::Specification.new do |spec|
9
8
  spec.name = "rom-factory"
@@ -11,25 +10,26 @@ Gem::Specification.new do |spec|
11
10
  spec.authors = ["Janis Miezitis", "Piotr Solnica"]
12
11
  spec.email = ["janjiss@gmail.com", "piotr.solnica@gmail.com"]
13
12
 
14
- spec.summary = %q{ROM based builder library to make your specs awesome. DSL partially inspired by FactoryBot.}
15
- spec.description = %q{}
13
+ spec.summary = "ROM based builder library to make your specs awesome. DSL partially inspired by FactoryBot."
14
+ spec.description = ""
16
15
  spec.homepage = "https://github.com/rom-rb/rom-factory"
17
16
  spec.license = "MIT"
18
17
 
19
18
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
19
  # to allow pushing to a single host or delete this section to allow pushing to any host.
21
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ spec.metadata["rubygems_mfa_required"] = "true"
22
22
 
23
23
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
- spec.required_ruby_version = ">= 2.7.0"
28
+ spec.required_ruby_version = ">= 3.1.0"
29
29
 
30
- spec.add_runtime_dependency "dry-configurable", "~> 1.0"
31
- spec.add_runtime_dependency "dry-core", "~> 1.0"
32
- spec.add_runtime_dependency "dry-struct", "~> 1.6"
33
- spec.add_runtime_dependency "faker", ">= 2.0", "< 3.0"
34
- spec.add_runtime_dependency "rom-core", "~> 5.3"
30
+ spec.add_dependency "dry-configurable", "~> 1.3"
31
+ spec.add_dependency "dry-core", "~> 1.1"
32
+ spec.add_dependency "dry-struct", "~> 1.7"
33
+ spec.add_dependency "faker", ">= 2.0", "< 4"
34
+ spec.add_dependency "rom-core", "~> 5.4"
35
35
  end
metadata CHANGED
@@ -1,15 +1,14 @@
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.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janis Miezitis
8
8
  - Piotr Solnica
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2022-11-11 00:00:00.000000000 Z
11
+ date: 2025-01-21 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: dry-configurable
@@ -17,42 +16,42 @@ dependencies:
17
16
  requirements:
18
17
  - - "~>"
19
18
  - !ruby/object:Gem::Version
20
- version: '1.0'
19
+ version: '1.3'
21
20
  type: :runtime
22
21
  prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - "~>"
26
25
  - !ruby/object:Gem::Version
27
- version: '1.0'
26
+ version: '1.3'
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: dry-core
30
29
  requirement: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - "~>"
33
32
  - !ruby/object:Gem::Version
34
- version: '1.0'
33
+ version: '1.1'
35
34
  type: :runtime
36
35
  prerelease: false
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - "~>"
40
39
  - !ruby/object:Gem::Version
41
- version: '1.0'
40
+ version: '1.1'
42
41
  - !ruby/object:Gem::Dependency
43
42
  name: dry-struct
44
43
  requirement: !ruby/object:Gem::Requirement
45
44
  requirements:
46
45
  - - "~>"
47
46
  - !ruby/object:Gem::Version
48
- version: '1.6'
47
+ version: '1.7'
49
48
  type: :runtime
50
49
  prerelease: false
51
50
  version_requirements: !ruby/object:Gem::Requirement
52
51
  requirements:
53
52
  - - "~>"
54
53
  - !ruby/object:Gem::Version
55
- version: '1.6'
54
+ version: '1.7'
56
55
  - !ruby/object:Gem::Dependency
57
56
  name: faker
58
57
  requirement: !ruby/object:Gem::Requirement
@@ -62,7 +61,7 @@ dependencies:
62
61
  version: '2.0'
63
62
  - - "<"
64
63
  - !ruby/object:Gem::Version
65
- version: '3.0'
64
+ version: '4'
66
65
  type: :runtime
67
66
  prerelease: false
68
67
  version_requirements: !ruby/object:Gem::Requirement
@@ -72,21 +71,21 @@ dependencies:
72
71
  version: '2.0'
73
72
  - - "<"
74
73
  - !ruby/object:Gem::Version
75
- version: '3.0'
74
+ version: '4'
76
75
  - !ruby/object:Gem::Dependency
77
76
  name: rom-core
78
77
  requirement: !ruby/object:Gem::Requirement
79
78
  requirements:
80
79
  - - "~>"
81
80
  - !ruby/object:Gem::Version
82
- version: '5.3'
81
+ version: '5.4'
83
82
  type: :runtime
84
83
  prerelease: false
85
84
  version_requirements: !ruby/object:Gem::Requirement
86
85
  requirements:
87
86
  - - "~>"
88
87
  - !ruby/object:Gem::Version
89
- version: '5.3'
88
+ version: '5.4'
90
89
  description: ''
91
90
  email:
92
91
  - janjiss@gmail.com
@@ -95,7 +94,6 @@ executables: []
95
94
  extensions: []
96
95
  extra_rdoc_files: []
97
96
  files:
98
- - ".action_hero.yml"
99
97
  - ".devtools/templates/changelog.erb"
100
98
  - ".devtools/templates/release.erb"
101
99
  - ".github/FUNDING.yml"
@@ -107,11 +105,12 @@ files:
107
105
  - ".github/workflows/rubocop.yml"
108
106
  - ".github/workflows/sync_configs.yml"
109
107
  - ".gitignore"
108
+ - ".postgres.env"
109
+ - ".repobot.yml"
110
110
  - ".rspec"
111
111
  - ".rubocop.yml"
112
112
  - ".yardopts"
113
113
  - CHANGELOG.md
114
- - CODEOWNERS
115
114
  - CODE_OF_CONDUCT.md
116
115
  - CONTRIBUTING.md
117
116
  - Gemfile
@@ -121,6 +120,7 @@ files:
121
120
  - Rakefile
122
121
  - benchmarks/basic.rb
123
122
  - changelog.yml
123
+ - compose.yml
124
124
  - docsite/source/index.html.md
125
125
  - lib/rom-factory.rb
126
126
  - lib/rom/factory.rb
@@ -146,7 +146,7 @@ licenses:
146
146
  - MIT
147
147
  metadata:
148
148
  allowed_push_host: https://rubygems.org
149
- post_install_message:
149
+ rubygems_mfa_required: 'true'
150
150
  rdoc_options: []
151
151
  require_paths:
152
152
  - lib
@@ -154,15 +154,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
154
154
  requirements:
155
155
  - - ">="
156
156
  - !ruby/object:Gem::Version
157
- version: 2.7.0
157
+ version: 3.1.0
158
158
  required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  requirements:
160
160
  - - ">="
161
161
  - !ruby/object:Gem::Version
162
162
  version: '0'
163
163
  requirements: []
164
- rubygems_version: 3.3.7
165
- signing_key:
164
+ rubygems_version: 3.6.2
166
165
  specification_version: 4
167
166
  summary: ROM based builder library to make your specs awesome. DSL partially inspired
168
167
  by FactoryBot.
data/CODEOWNERS DELETED
@@ -1 +0,0 @@
1
- * @solnic