rom-factory 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.action_hero.yml +21 -0
- data/.devtools/templates/changelog.erb +43 -0
- data/.devtools/templates/release.erb +36 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/ISSUE_TEMPLATE/bug-report.md +26 -0
- data/.github/ISSUE_TEMPLATE/config.yml +5 -0
- data/.github/SUPPORT.md +3 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/docsite.yml +38 -9
- data/.github/workflows/rubocop.yml +46 -0
- data/.github/workflows/sync_configs.yml +41 -18
- data/.rspec +1 -1
- data/.rubocop.yml +198 -40
- data/CHANGELOG.md +88 -33
- data/CODEOWNERS +1 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +22 -22
- data/Gemfile.devtools +20 -0
- data/LICENSE +1 -1
- data/README.md +13 -48
- data/Rakefile +1 -1
- data/benchmarks/basic.rb +10 -10
- data/changelog.yml +99 -0
- data/docsite/source/index.html.md +162 -1
- data/lib/rom/factory/attribute_registry.rb +2 -2
- data/lib/rom/factory/attributes/association.rb +52 -12
- data/lib/rom/factory/attributes/callable.rb +1 -1
- data/lib/rom/factory/attributes/value.rb +1 -1
- data/lib/rom/factory/attributes.rb +4 -6
- data/lib/rom/factory/builder/persistable.rb +3 -5
- data/lib/rom/factory/builder.rb +8 -18
- data/lib/rom/factory/constants.rb +1 -1
- data/lib/rom/factory/dsl.rb +36 -23
- data/lib/rom/factory/factories.rb +13 -15
- data/lib/rom/factory/registry.rb +2 -2
- data/lib/rom/factory/sequences.rb +1 -1
- data/lib/rom/factory/tuple_evaluator.rb +18 -22
- data/lib/rom/factory/version.rb +1 -1
- data/lib/rom/factory.rb +8 -5
- data/lib/rom-factory.rb +1 -1
- data/project.yml +3 -0
- data/rom-factory.gemspec +8 -11
- metadata +43 -43
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/----please-don-t-report-feature-requests-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---a-detailed-bug-report.md +0 -30
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/custom_ci.yml +0 -118
- data/Appraisals +0 -9
- data/LICENSE.txt +0 -21
- data/gemfiles/faker_1.gemfile +0 -34
- data/gemfiles/faker_1.gemfile.lock +0 -178
- data/gemfiles/faker_2.gemfile +0 -34
- data/gemfiles/faker_2.gemfile.lock +0 -178
data/README.md
CHANGED
@@ -1,64 +1,29 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/rom-factory
|
2
2
|
[actions]: https://github.com/rom-rb/rom-factory/actions
|
3
|
-
[
|
3
|
+
[codacy]: https://www.codacy.com/gh/rom-rb/rom-factory
|
4
|
+
[chat]: https://rom-rb.zulipchat.com
|
4
5
|
[inchpages]: http://inch-ci.org/github/rom-rb/rom-factory
|
5
6
|
|
6
|
-
# rom-factory
|
7
|
+
# rom-factory [![Join the chat at https://rom-rb.zulipchat.com](https://img.shields.io/badge/rom--rb-join%20chat-%23346b7a.svg)][chat]
|
7
8
|
|
8
9
|
[![Gem Version](https://badge.fury.io/rb/rom-factory.svg)][gem]
|
9
10
|
[![CI Status](https://github.com/rom-rb/rom-factory/workflows/ci/badge.svg)][actions]
|
10
|
-
[![
|
11
|
-
[![
|
11
|
+
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fd26fae687549218458879b1a607e18)][codacy]
|
12
|
+
[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/5fd26fae687549218458879b1a607e18)][codacy]
|
12
13
|
[![Inline docs](http://inch-ci.org/github/rom-rb/rom-factory.svg?branch=master)][inchpages]
|
13
14
|
|
14
|
-
|
15
|
+
## Links
|
15
16
|
|
16
|
-
|
17
|
+
* [User documentation](https://rom-rb.org/learn/factory)
|
18
|
+
* [API documentation](https://rubydoc.info/gems/rom-factory)
|
17
19
|
|
18
|
-
|
20
|
+
## Supported Ruby versions
|
19
21
|
|
20
|
-
|
22
|
+
This library officially supports the following Ruby versions:
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
```ruby
|
25
|
-
gem 'rom-factory'
|
26
|
-
```
|
27
|
-
|
28
|
-
And then execute:
|
29
|
-
|
30
|
-
$ bundle
|
31
|
-
|
32
|
-
Or install it yourself as:
|
33
|
-
|
34
|
-
$ gem install rom-factory
|
35
|
-
|
36
|
-
### Performance
|
37
|
-
|
38
|
-
Seems like this thing is a bit faster than other popular factory gems:
|
39
|
-
|
40
|
-
```
|
41
|
-
Warming up --------------------------------------
|
42
|
-
rom-factory 1.000 i/100ms
|
43
|
-
factory_girl 1.000 i/100ms
|
44
|
-
fabrication 1.000 i/100ms
|
45
|
-
Calculating -------------------------------------
|
46
|
-
rom-factory 1.550 (± 0.0%) i/s - 8.000 in 5.166227s
|
47
|
-
factory_girl 0.982 (± 0.0%) i/s - 5.000 in 5.098193s
|
48
|
-
fabrication 1.011 (± 0.0%) i/s - 6.000 in 5.942209s
|
49
|
-
|
50
|
-
Comparison:
|
51
|
-
rom-factory: 1.5 i/s
|
52
|
-
fabrication: 1.0 i/s - 1.53x slower
|
53
|
-
factory_girl: 1.0 i/s - 1.58x slower
|
54
|
-
```
|
55
|
-
|
56
|
-
> See [benchmarks/basic.rb](https://github.com/rom-rb/rom-factory/blob/master/benchmarks/basic.rb)
|
57
|
-
|
58
|
-
## Credits
|
59
|
-
|
60
|
-
This project was originally created by [Jānis Miezītis](https://github.com/janjiss) and eventually moved to `rom-rb` organization.
|
24
|
+
* MRI >= `2.5`
|
25
|
+
* jruby >= `9.2`
|
61
26
|
|
62
27
|
## License
|
63
28
|
|
64
|
-
See `LICENSE
|
29
|
+
See `LICENSE` file.
|
data/Rakefile
CHANGED
data/benchmarks/basic.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
3
|
+
require "rom-factory"
|
4
|
+
require "rom-core"
|
5
|
+
require "active_record"
|
6
|
+
require "factory_bot"
|
7
|
+
require "fabrication"
|
8
|
+
require "benchmark/ips"
|
9
9
|
|
10
|
-
DATABASE_URL =
|
10
|
+
DATABASE_URL = "postgres://localhost/rom_factory_bench"
|
11
11
|
|
12
12
|
rom = ROM.container(:sql, DATABASE_URL) do |conf|
|
13
13
|
conf.default.connection.create_table?(:users) do
|
@@ -50,15 +50,15 @@ Fabricator(:user) do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
Benchmark.ips do |x|
|
53
|
-
x.report(
|
53
|
+
x.report("rom-factory persisted struct") do
|
54
54
|
factory[:user]
|
55
55
|
end
|
56
56
|
|
57
|
-
x.report(
|
57
|
+
x.report("factory_bot") do
|
58
58
|
FactoryBot.create(:user)
|
59
59
|
end
|
60
60
|
|
61
|
-
x.report(
|
61
|
+
x.report("fabrication") do
|
62
62
|
Fabricate(:user)
|
63
63
|
end
|
64
64
|
|
data/changelog.yml
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
---
|
2
|
+
- version: 0.11.0
|
3
|
+
date: 2022-11-11
|
4
|
+
added:
|
5
|
+
- Support for one-to-one associations (@ianks)
|
6
|
+
- "[internal] cache for Faker constants (@flash-gordon)"
|
7
|
+
changed:
|
8
|
+
- |
|
9
|
+
[BREAKING] attributes are always passed as keywords (@alassek)
|
10
|
+
This may affect your code in places where attributes are passed as hashes.
|
11
|
+
Places like
|
12
|
+
```ruby
|
13
|
+
user_attributes = { name: 'Jane' }
|
14
|
+
Factory[:user, user_attributes]
|
15
|
+
```
|
16
|
+
must be updated to
|
17
|
+
```ruby
|
18
|
+
user_attributes = { name: 'Jane' }
|
19
|
+
Factory[:user, **user_attributes]
|
20
|
+
```
|
21
|
+
- "Upgraded to the latest versions of dry-rb dependencies, compatible with rom 5.3 (@flash-gordon)"
|
22
|
+
- Support for Faker 1.x was dropped (@alassek)
|
23
|
+
fixed:
|
24
|
+
- Support for plural Faker generators (@wuarmin)
|
25
|
+
- version: 0.10.2
|
26
|
+
date: "2020-04-05"
|
27
|
+
fixed:
|
28
|
+
- Fix more keyword warnings (@flash-gordon)
|
29
|
+
- version: 0.10.1
|
30
|
+
date: '2019-12-28'
|
31
|
+
added:
|
32
|
+
- 'Support for faker 2 (@ianks)'
|
33
|
+
fixed:
|
34
|
+
- Keyword warnings reported by Ruby 2.7 (@flash-gordon)
|
35
|
+
- version: 0.10.0
|
36
|
+
date: '2019-12-11'
|
37
|
+
added:
|
38
|
+
- |-
|
39
|
+
`struct_namespace` option is supported by factory builders (@graceful-potato)
|
40
|
+
|
41
|
+
``` ruby
|
42
|
+
factories.define(:user, struct_namespace: MyApp::Entities) do |f|
|
43
|
+
# ...
|
44
|
+
end
|
45
|
+
```
|
46
|
+
fixed:
|
47
|
+
- Support building structs when child assoc does not define parent (@psparrow)
|
48
|
+
- 'Fixed `TupleEvaluator#struct_attrs` for non-standard output schema (@AMHOL)'
|
49
|
+
- version: 0.9.1
|
50
|
+
date: '2019-10-23'
|
51
|
+
fixed:
|
52
|
+
- Attributes of a struct are no longer accidentally passed to their associations (@psparrow)
|
53
|
+
- version: 0.9.0
|
54
|
+
date: '2019-08-12'
|
55
|
+
added:
|
56
|
+
- 'When attributes hash includes unknown attributes, a `ROM::Factory::UnknownAttributeError` will be raised (@rawburt)'
|
57
|
+
- version: 0.8.0
|
58
|
+
date: '2019-04-24'
|
59
|
+
fixed:
|
60
|
+
- 'Loaded association structs are no longer rejected by output schemas (issue #34) (flash-gordon + solnic)'
|
61
|
+
- version: 0.7.0
|
62
|
+
date: '2018-11-17'
|
63
|
+
added:
|
64
|
+
- Support for traits (v-kolesnikov)
|
65
|
+
- Support building structs with associations (@ianks)
|
66
|
+
fixed:
|
67
|
+
- Overwritten attributes with dependencies (JanaVPetrova)
|
68
|
+
- version: 0.6.0
|
69
|
+
date: '2018-01-31'
|
70
|
+
added:
|
71
|
+
- Support for factories with custom struct namespaces (solnic)
|
72
|
+
changed:
|
73
|
+
- 'Accessing a factory which is not defined will result in `FactoryNotDefinedError` exception (GustavoCaso + solnic)'
|
74
|
+
fixed:
|
75
|
+
- 'Using dependent attributes with sequences works correctly, ie `f.sequence(:login) { |i, name| "name-#{i}"}` (solnic)'
|
76
|
+
- version: 0.5.0
|
77
|
+
date: '2017-10-24'
|
78
|
+
added:
|
79
|
+
- Updated to rom 4.0 (solnic)
|
80
|
+
- 'Support for `has_many` and `has_one` associations (solnic)'
|
81
|
+
- Support for attributes depending on values from other attributes (solnic)
|
82
|
+
- 'Support for `rand` inside the generator block (flash-gordon)'
|
83
|
+
changed:
|
84
|
+
- 'Depends on `rom-core` now (solnic)'
|
85
|
+
- version: 0.4.0
|
86
|
+
date: '2017-03-03'
|
87
|
+
summary: improves internals.
|
88
|
+
added:
|
89
|
+
- 'Support for defining multiple factories via `MyFactory = ROM::Factory.configure { |c| ... }` (solnic)'
|
90
|
+
- 'Support for builder inheritence via `define(admin: :user) { |f| ... }` (solnic)'
|
91
|
+
- 'Support for generating in-memory structs via `MyFactory.structs[:user]` that are not persisted (solnic)'
|
92
|
+
- 'Support for `belongs_to` associations via `f.association(:user)` (solnic)'
|
93
|
+
- 'New DSL for defining builders `MyFactory.define(:user) { |f| ... }` which infers default relation name (solnic)'
|
94
|
+
- 'New factory method `MyFactory#[]` ie `MyFactory[:user, name: "Jane"]` (solnic)'
|
95
|
+
- 'New `fake` helper which uses faker gem under the hood ie `f.email { fake(:internet, :email) }` (solnic)'
|
96
|
+
changed:
|
97
|
+
- "`Rom::Factory::Config.configure` was replaced with `ROM::Factory.configure` (solnic)"
|
98
|
+
- Global factory config and builders are gone (solnic)
|
99
|
+
- Structs are now based on dry-struct (solnic)
|
@@ -5,4 +5,165 @@ chapter: Factory
|
|
5
5
|
|
6
6
|
# rom-factory
|
7
7
|
|
8
|
-
|
8
|
+
`rom-factory` provides a simple API for creating and persisting `ROM::Struct`'s. If you already know FactoryBot you'll definitely understand the concept.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
First of all you need to define a ROM Container for Factory. For example if you are using `rspec`, you can add these lines to `spec_helper.rb`. Also you need to require here all files with Factories.
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
Factory = ROM::Factory.configure do |config|
|
16
|
+
config.rom = my_rom_container
|
17
|
+
end
|
18
|
+
|
19
|
+
Dir[File.dirname(__FILE__) + '/support/factories/*.rb'].each { |file| require file }
|
20
|
+
```
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Define factory
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# 'spec/support/factories/users.rb'
|
28
|
+
|
29
|
+
Factory.define(:user) do |f|
|
30
|
+
f.name 'John'
|
31
|
+
f.age 42
|
32
|
+
end
|
33
|
+
```
|
34
|
+
#### Specify relations
|
35
|
+
|
36
|
+
You can specify ROM relation if you want. It'll be pluralized factory name by default.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Factory.define(:user, relation: :people) do |f|
|
40
|
+
f.name 'John'
|
41
|
+
f.age 42
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
#### Specify namespace for your structs
|
46
|
+
|
47
|
+
Struct `User` will be find in MyApp::Entities namespace
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
Factory.define(:user, struct_namespace: MyApp::Entities) do |f|
|
51
|
+
# ...
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
#### Sequences
|
56
|
+
|
57
|
+
You can use sequences for uniq fields
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
Factory.define(:user) do |f|
|
61
|
+
f.name 'John'
|
62
|
+
f.sequence(:email) { |n| "john#{n}@example.com" }
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
#### Timestamps
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
Factory.define(:user) do |f|
|
70
|
+
f.name 'John'
|
71
|
+
f.timestamps
|
72
|
+
# same as
|
73
|
+
# f.created_at { Time.now }
|
74
|
+
# f.updated_at { Time.now }
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
#### Associations
|
79
|
+
|
80
|
+
* belongs_to
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
Factory.define(:group) do |f|
|
84
|
+
f.name 'Admins'
|
85
|
+
end
|
86
|
+
|
87
|
+
Factory.define(:user) do |f|
|
88
|
+
f.name 'John'
|
89
|
+
f.association(:user)
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
* has_many
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Factory.define(:group) do |f|
|
97
|
+
f.name 'Admins'
|
98
|
+
f.association(:user, count: 2)
|
99
|
+
end
|
100
|
+
|
101
|
+
Factory.define(:user) do |f|
|
102
|
+
f.name 'John'
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Extend already existing factory
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Factory.define(:user) do |f|
|
110
|
+
f.name 'John'
|
111
|
+
f.admin false
|
112
|
+
end
|
113
|
+
|
114
|
+
Factory.define(admin: :user) do |f|
|
115
|
+
f.admin true
|
116
|
+
end
|
117
|
+
|
118
|
+
# Factory.structs(:admin)
|
119
|
+
```
|
120
|
+
|
121
|
+
#### Traits
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
Factory.define(:user) do |f|
|
125
|
+
f.name 'John'
|
126
|
+
f.admin false
|
127
|
+
|
128
|
+
f.trait :with_age do |t|
|
129
|
+
t.age 42
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Factory.structs(:user, :with_age)
|
134
|
+
```
|
135
|
+
|
136
|
+
#### Build-in [Faker](https://github.com/faker-ruby/faker) objects
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
Factory.define(:user) do |f|
|
140
|
+
f.email { fake(:internet, :email) }
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
#### Dependent attributes
|
145
|
+
|
146
|
+
Attributes can be based on the values of other attributes:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
Factory.define(:user) do |f|
|
150
|
+
f.full_name { fake(:name) }
|
151
|
+
# Dependent attributes are inferred from the block parameter names:
|
152
|
+
f.login { |full_name| full_name.downcase.gsub(/\s+/, '_') }
|
153
|
+
# Works with sequences too:
|
154
|
+
f.sequence(:email) { |n, login| "#{login}-#{n}@example.com" }
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
### Build and persist objects
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# Create in-memory object
|
162
|
+
Factory.structs[:user]
|
163
|
+
|
164
|
+
# Persist struct in database
|
165
|
+
Factory[:user]
|
166
|
+
|
167
|
+
# Override attributes
|
168
|
+
Factory[:user, age: 24]
|
169
|
+
```
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "tsort"
|
4
4
|
|
5
5
|
module ROM
|
6
6
|
module Factory
|
@@ -47,7 +47,7 @@ module ROM
|
|
47
47
|
|
48
48
|
# @api private
|
49
49
|
def associations
|
50
|
-
self.class.new(elements.select { |e| e.
|
50
|
+
self.class.new(elements.select { |e| e.is_a?(Attributes::Association::Core) })
|
51
51
|
end
|
52
52
|
|
53
53
|
private
|
@@ -5,10 +5,9 @@ module ROM::Factory
|
|
5
5
|
# @api private
|
6
6
|
module Association
|
7
7
|
class << self
|
8
|
-
def new(assoc, builder, *
|
9
|
-
const_get(assoc.definition.type).new(assoc, builder, *
|
8
|
+
def new(assoc, builder, *traits, **options)
|
9
|
+
const_get(assoc.definition.type).new(assoc, builder, *traits, **options)
|
10
10
|
end
|
11
|
-
ruby2_keywords(:new) if respond_to?(:ruby2_keywords, true)
|
12
11
|
end
|
13
12
|
|
14
13
|
# @api private
|
@@ -23,6 +22,11 @@ module ROM::Factory
|
|
23
22
|
@options = options
|
24
23
|
end
|
25
24
|
|
25
|
+
# @api private
|
26
|
+
def through?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
26
30
|
# @api private
|
27
31
|
def builder
|
28
32
|
@__builder__ ||= @builder_proc.call
|
@@ -56,7 +60,7 @@ module ROM::Factory
|
|
56
60
|
else
|
57
61
|
builder.struct(*traits)
|
58
62
|
end
|
59
|
-
tuple = {
|
63
|
+
tuple = {name => struct}
|
60
64
|
assoc.associate(tuple, struct)
|
61
65
|
end
|
62
66
|
end
|
@@ -78,13 +82,13 @@ module ROM::Factory
|
|
78
82
|
association_hash = assoc.associate(attrs, parent)
|
79
83
|
|
80
84
|
if persist
|
81
|
-
builder.persistable.create(*traits, association_hash)
|
85
|
+
builder.persistable.create(*traits, **association_hash)
|
82
86
|
else
|
83
|
-
builder.struct(*traits, attrs
|
87
|
+
builder.struct(*traits, **attrs, **association_hash)
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
87
|
-
{
|
91
|
+
{name => structs}
|
88
92
|
end
|
89
93
|
|
90
94
|
# @api private
|
@@ -103,22 +107,22 @@ module ROM::Factory
|
|
103
107
|
# @api private
|
104
108
|
def call(attrs = EMPTY_HASH, parent, persist: true)
|
105
109
|
# do not associate if count is 0
|
106
|
-
return {
|
110
|
+
return {name => nil} if count.zero?
|
107
111
|
|
108
112
|
return if attrs.key?(name)
|
109
113
|
|
110
114
|
association_hash = assoc.associate(attrs, parent)
|
111
115
|
|
112
116
|
struct = if persist
|
113
|
-
builder.persistable.create(*traits, association_hash)
|
117
|
+
builder.persistable.create(*traits, **association_hash)
|
114
118
|
else
|
115
119
|
belongs_to_name = Dry::Core::Inflector.singularize(assoc.source_alias)
|
116
|
-
belongs_to_associations = {
|
120
|
+
belongs_to_associations = {belongs_to_name.to_sym => parent}
|
117
121
|
final_attrs = attrs.merge(association_hash).merge(belongs_to_associations)
|
118
|
-
builder.struct(*traits, final_attrs)
|
122
|
+
builder.struct(*traits, **final_attrs)
|
119
123
|
end
|
120
124
|
|
121
|
-
{
|
125
|
+
{name => struct}
|
122
126
|
end
|
123
127
|
|
124
128
|
# @api private
|
@@ -126,6 +130,42 @@ module ROM::Factory
|
|
126
130
|
options.fetch(:count, 1)
|
127
131
|
end
|
128
132
|
end
|
133
|
+
|
134
|
+
class OneToOneThrough < Core
|
135
|
+
def call(attrs = EMPTY_HASH, parent, persist: true)
|
136
|
+
return if attrs.key?(name)
|
137
|
+
|
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
|
145
|
+
|
146
|
+
assoc.persist([parent], struct) if persist
|
147
|
+
|
148
|
+
{name => struct}
|
149
|
+
end
|
150
|
+
|
151
|
+
def dependency?(rel)
|
152
|
+
assoc.source == rel
|
153
|
+
end
|
154
|
+
|
155
|
+
def through?
|
156
|
+
true
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def count
|
162
|
+
options.fetch(:count, 1)
|
163
|
+
end
|
164
|
+
|
165
|
+
def tpk
|
166
|
+
assoc.target.primary_key
|
167
|
+
end
|
168
|
+
end
|
129
169
|
end
|
130
170
|
end
|
131
171
|
end
|
@@ -1,14 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/core/constants'
|
4
|
-
|
5
3
|
module ROM
|
6
4
|
module Factory
|
7
5
|
include Dry::Core::Constants
|
8
6
|
end
|
9
7
|
end
|
10
8
|
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
9
|
+
require "rom/factory/attributes/value"
|
10
|
+
require "rom/factory/attributes/callable"
|
11
|
+
require "rom/factory/attributes/sequence"
|
12
|
+
require "rom/factory/attributes/association"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "delegate"
|
4
4
|
|
5
5
|
module ROM
|
6
6
|
module Factory
|
@@ -21,10 +21,8 @@ module ROM
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# @api private
|
24
|
-
def create(*
|
25
|
-
traits, attrs
|
26
|
-
|
27
|
-
tuple = tuple(*traits, attrs)
|
24
|
+
def create(*traits, **attrs)
|
25
|
+
tuple = tuple(*traits, **attrs)
|
28
26
|
validate_keys(traits, attrs)
|
29
27
|
persisted = persist(tuple)
|
30
28
|
|
data/lib/rom/factory/builder.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require 'rom/factory/tuple_evaluator'
|
8
|
-
require 'rom/factory/builder/persistable'
|
3
|
+
require "rom/struct"
|
4
|
+
require "rom/initializer"
|
5
|
+
require "rom/factory/tuple_evaluator"
|
6
|
+
require "rom/factory/builder/persistable"
|
9
7
|
|
10
8
|
module ROM::Factory
|
11
9
|
# @api private
|
@@ -31,18 +29,15 @@ module ROM::Factory
|
|
31
29
|
option :struct_namespace, reader: false
|
32
30
|
|
33
31
|
# @api private
|
34
|
-
def tuple(*
|
35
|
-
traits, attrs = extract_tuple(args)
|
36
|
-
|
32
|
+
def tuple(*traits, **attrs)
|
37
33
|
tuple_evaluator.defaults(traits, attrs)
|
38
34
|
end
|
39
35
|
|
40
36
|
# @api private
|
41
|
-
def struct(*
|
42
|
-
traits, attrs = extract_tuple(args)
|
37
|
+
def struct(*traits, **attrs)
|
43
38
|
validate_keys(traits, attrs, allow_associations: true)
|
44
39
|
|
45
|
-
tuple_evaluator.struct(*
|
40
|
+
tuple_evaluator.struct(*traits, **attrs)
|
46
41
|
end
|
47
42
|
alias_method :create, :struct
|
48
43
|
|
@@ -75,18 +70,13 @@ module ROM::Factory
|
|
75
70
|
tuple_evaluator.relation
|
76
71
|
end
|
77
72
|
|
78
|
-
# @api private
|
79
|
-
def extract_tuple(args)
|
80
|
-
tuple_evaluator.extract_tuple(args)
|
81
|
-
end
|
82
|
-
|
83
73
|
# @api private
|
84
74
|
def validate_keys(traits, tuple, allow_associations: false)
|
85
75
|
schema_keys = relation.schema.attributes.map(&:name)
|
86
76
|
assoc_keys = tuple_evaluator.assoc_names(traits)
|
87
77
|
unknown_keys = tuple.keys - schema_keys - assoc_keys
|
88
78
|
|
89
|
-
unknown_keys
|
79
|
+
unknown_keys -= relation.schema.associations.to_h.keys if allow_associations
|
90
80
|
|
91
81
|
raise UnknownFactoryAttributes, unknown_keys unless unknown_keys.empty?
|
92
82
|
end
|