rom-factory 0.11.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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