rom-factory 0.12.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +15 -16
- data/.postgres.env +4 -0
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +34 -4
- data/Gemfile +17 -7
- data/Gemfile.devtools +1 -1
- data/README.md +2 -2
- data/changelog.yml +28 -5
- data/compose.yml +7 -0
- data/docsite/source/index.html.md +15 -3
- data/lib/rom/factory/attribute_registry.rb +13 -19
- data/lib/rom/factory/attributes/association.rb +38 -62
- data/lib/rom/factory/attributes/callable.rb +6 -0
- data/lib/rom/factory/dsl.rb +59 -33
- data/lib/rom/factory/factories.rb +27 -7
- data/lib/rom/factory/tuple_evaluator.rb +5 -5
- data/lib/rom/factory/version.rb +1 -1
- data/rom-factory.gemspec +12 -12
- metadata +15 -16
- data/CODEOWNERS +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 946c06da46cc1d12ac8e90fc8b79661ce43e9ed2ab2ba0e3ffd93f5c1ac7e419
|
4
|
+
data.tar.gz: fa2c475a578ba75f7574ed5d883a4432fc4f2328849fc4be74813b854a0c6dba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1dea330b287e3b56c2acaea898c9139317b0de709098c0c7525d05215db34571dfa41447337ca39b39f7d1d5ace98fde9f442854a725870fa6496c24d00eee3
|
7
|
+
data.tar.gz: 6279eb190440aeb10dc03c26fe18cfcc67f846e8d0981b7822746172f564936f5d2fd7cbf41a68336be1865516afda03947302804db8d6a1e05040198137beb9
|
data/.github/workflows/ci.yml
CHANGED
@@ -2,20 +2,10 @@
|
|
2
2
|
name: ci
|
3
3
|
on:
|
4
4
|
push:
|
5
|
-
paths:
|
6
|
-
- ".github/workflows/ci.yml"
|
7
|
-
- lib/**
|
8
|
-
- "*.gemspec"
|
9
|
-
- spec/**
|
10
|
-
- Rakefile
|
11
|
-
- Gemfile
|
12
|
-
- Gemfile.devtools
|
13
|
-
- ".rubocop.yml"
|
14
|
-
- project.yml
|
15
5
|
pull_request:
|
16
|
-
branches:
|
17
|
-
- master
|
18
6
|
create:
|
7
|
+
schedule:
|
8
|
+
- cron: "30 4 * * *"
|
19
9
|
jobs:
|
20
10
|
tests:
|
21
11
|
runs-on: ubuntu-latest
|
@@ -23,14 +13,15 @@ jobs:
|
|
23
13
|
fail-fast: false
|
24
14
|
matrix:
|
25
15
|
ruby:
|
16
|
+
- '3.4'
|
26
17
|
- '3.3'
|
27
18
|
- '3.2'
|
28
19
|
- '3.1'
|
29
|
-
- '3.0'
|
30
20
|
env:
|
31
21
|
COVERAGE: "${{matrix.coverage}}"
|
32
22
|
COVERAGE_TOKEN: "${{secrets.CODACY_PROJECT_TOKEN}}"
|
33
23
|
APT_DEPS: libpq-dev libmysqlclient-dev libsqlite3-dev
|
24
|
+
DATABASE_URL: "postgres://rom-factory:rom-factory@localhost:5432/rom_factory"
|
34
25
|
steps:
|
35
26
|
- name: Checkout
|
36
27
|
uses: actions/checkout@v1
|
@@ -56,10 +47,10 @@ jobs:
|
|
56
47
|
coverage-reports: coverage/coverage.xml
|
57
48
|
services:
|
58
49
|
db:
|
59
|
-
image: postgres:
|
50
|
+
image: postgres:16.1
|
60
51
|
env:
|
61
|
-
POSTGRES_USER:
|
62
|
-
POSTGRES_PASSWORD:
|
52
|
+
POSTGRES_USER: rom-factory
|
53
|
+
POSTGRES_PASSWORD: rom-factory
|
63
54
|
POSTGRES_DB: rom_factory
|
64
55
|
ports:
|
65
56
|
- 5432:5432
|
@@ -86,3 +77,11 @@ jobs:
|
|
86
77
|
run: |
|
87
78
|
tag=$(echo $GITHUB_REF | cut -d / -f 3)
|
88
79
|
ossy gh w rom-rb/devtools release --payload "{\"tag\":\"$tag\",\"sha\":\"${{github.sha}}\",\"tag_creator\":\"$GITHUB_ACTOR\",\"repo\":\"$GITHUB_REPOSITORY\",\"repo_name\":\"${{github.event.repository.name}}\"}"
|
80
|
+
|
81
|
+
workflow-keepalive:
|
82
|
+
if: github.event_name == 'schedule'
|
83
|
+
runs-on: ubuntu-latest
|
84
|
+
permissions:
|
85
|
+
actions: write
|
86
|
+
steps:
|
87
|
+
- uses: liskin/gh-workflow-keepalive@v1
|
data/.postgres.env
ADDED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,17 +1,47 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 0.13.0 2025-01-21
|
4
|
+
|
5
|
+
|
6
|
+
### Added
|
7
|
+
|
8
|
+
- Support for unique options in `fake` DSL (via #94) (@sean-dickinson)
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
Factory.define(:user) do |f|
|
12
|
+
f.name { fake(:name, unique: true) }
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
Be advised there's `Faker::UniqueGenerator.clear` to clear the cache of unique values.
|
17
|
+
|
18
|
+
- Support for setting traits with a keyword argument for associations (via #84) (@parndt)
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
Factory.define :category do |f|
|
22
|
+
f.association :image, traits: [:fancy]
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
- Minimum Ruby version is now 3.1 (@flash-gordon)
|
30
|
+
|
31
|
+
[Compare v0.12.0...v0.13.0](https://github.com/rom-rb/rom-factory/compare/v0.12.0...v0.13.0)
|
32
|
+
|
3
33
|
## 0.12.0 2024-01-19
|
4
34
|
|
5
35
|
|
6
36
|
### Added
|
7
37
|
|
8
|
-
- Support for many-to-many and one-to-one-through associations (via
|
9
|
-
- Support for UUID as PKs in associations (via
|
38
|
+
- Support for many-to-many and one-to-one-through associations (via #86) (@solnic)
|
39
|
+
- Support for UUID as PKs in associations (via #87) (@solnic)
|
10
40
|
|
11
41
|
### Fixed
|
12
42
|
|
13
|
-
- Relations without PKs should work too (via
|
14
|
-
- Relations with PK values generated on the Ruby side should work in SQlite too (via
|
43
|
+
- Relations without PKs should work too (via #87) (@solnic)
|
44
|
+
- Relations with PK values generated on the Ruby side should work in SQlite too (via #87) (@solnic)
|
15
45
|
|
16
46
|
|
17
47
|
[Compare v0.11.0...v0.12.0](https://github.com/rom-rb/rom-factory/compare/v0.11.0...v0.12.0)
|
data/Gemfile
CHANGED
@@ -10,24 +10,34 @@ gem "faker", "~> 3.0"
|
|
10
10
|
|
11
11
|
gem "rspec", "~> 3.0"
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
gem "dotenv"
|
14
|
+
|
15
|
+
git "https://github.com/rom-rb/rom.git", branch: "release-5.4" do
|
16
|
+
gem "rom"
|
15
17
|
gem "rom-changeset"
|
18
|
+
gem "rom-core"
|
16
19
|
gem "rom-repository"
|
17
|
-
gem "rom"
|
18
20
|
end
|
19
21
|
|
20
22
|
group :test do
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4")
|
24
|
+
gem "debug"
|
25
|
+
else
|
26
|
+
gem "pry"
|
27
|
+
gem "pry-byebug", "~> 3.8", platforms: :ruby
|
28
|
+
end
|
29
|
+
gem "rom-sql", github: "rom-rb/rom-sql", branch: "release-3.7"
|
24
30
|
|
25
31
|
gem "jdbc-postgres", platforms: :jruby
|
26
32
|
gem "pg", "~> 1.5", platforms: :ruby
|
27
33
|
end
|
28
34
|
|
29
35
|
group :tools do
|
30
|
-
|
36
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4")
|
37
|
+
gem "byebug", platform: :mri
|
38
|
+
else
|
39
|
+
gem "pry-byebug", "~> 3.8", platforms: :ruby
|
40
|
+
end
|
31
41
|
gem "redcarpet" # for yard
|
32
42
|
end
|
33
43
|
|
data/Gemfile.devtools
CHANGED
data/README.md
CHANGED
data/changelog.yml
CHANGED
@@ -1,16 +1,39 @@
|
|
1
1
|
---
|
2
|
+
- version: 0.13.0
|
3
|
+
date: 2025-01-21
|
4
|
+
added:
|
5
|
+
- |
|
6
|
+
Support for unique options in `fake` DSL (via #94) (@sean-dickinson)
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
Factory.define(:user) do |f|
|
10
|
+
f.name { fake(:name, unique: true) }
|
11
|
+
end
|
12
|
+
```
|
13
|
+
|
14
|
+
Be advised there's `Faker::UniqueGenerator.clear` to clear the cache of unique values.
|
15
|
+
- |
|
16
|
+
Support for setting traits with a keyword argument for associations (via #84) (@parndt)
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
Factory.define :category do |f|
|
20
|
+
f.association :image, traits: [:fancy]
|
21
|
+
end
|
22
|
+
```
|
23
|
+
changed:
|
24
|
+
- Minimum Ruby version is now 3.1 (@flash-gordon)
|
2
25
|
- version: 0.12.0
|
3
26
|
date: 2024-01-19
|
4
27
|
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)
|
28
|
+
- "Support for many-to-many and one-to-one-through associations (via #86) (@solnic)"
|
29
|
+
- "Support for UUID as PKs in associations (via #87) (@solnic)"
|
7
30
|
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)
|
31
|
+
- "Relations without PKs should work too (via #87) (@solnic)"
|
32
|
+
- "Relations with PK values generated on the Ruby side should work in SQlite too (via #87) (@solnic)"
|
10
33
|
- version: 0.11.0
|
11
34
|
date: 2022-11-11
|
12
35
|
added:
|
13
|
-
- Support for one-to-one associations (@ianks)
|
36
|
+
- "Support for one-to-one associations (@ianks)"
|
14
37
|
- "[internal] cache for Faker constants (@flash-gordon)"
|
15
38
|
changed:
|
16
39
|
- |
|
data/compose.yml
ADDED
@@ -141,6 +141,14 @@ Factory.define(:user) do |f|
|
|
141
141
|
end
|
142
142
|
```
|
143
143
|
|
144
|
+
##### Unique values with fake
|
145
|
+
Passing the unique: true option will use Faker's [unique](https://github.com/faker-ruby/faker#ensuring-unique-values) feature
|
146
|
+
```ruby
|
147
|
+
Factory.define(:user) do |f|
|
148
|
+
f.email { fake(:internet, :email, unique: true) }
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
144
152
|
#### Dependent attributes
|
145
153
|
|
146
154
|
Attributes can be based on the values of other attributes:
|
@@ -159,11 +167,15 @@ end
|
|
159
167
|
|
160
168
|
```ruby
|
161
169
|
# Create in-memory object
|
162
|
-
Factory.
|
170
|
+
Factory.build(:user)
|
163
171
|
|
164
172
|
# Persist struct in database
|
165
|
-
Factory
|
173
|
+
Factory.create(:user)
|
166
174
|
|
167
175
|
# Override attributes
|
168
|
-
Factory
|
176
|
+
Factory.create(:user, age: 24)
|
177
|
+
|
178
|
+
# Build and Create via #[] accessors
|
179
|
+
Factory.structs[:user]
|
180
|
+
Factory[:user]
|
169
181
|
```
|
@@ -18,14 +18,10 @@ module ROM
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# @api private
|
21
|
-
def each(&
|
22
|
-
elements.each(&block)
|
23
|
-
end
|
21
|
+
def each(&) = elements.each(&)
|
24
22
|
|
25
23
|
# @api private
|
26
|
-
def [](name)
|
27
|
-
detect { |e| e.name.equal?(name) }
|
28
|
-
end
|
24
|
+
def [](name) = detect { |e| e.name.equal?(name) }
|
29
25
|
|
30
26
|
# @api private
|
31
27
|
def <<(element)
|
@@ -36,34 +32,32 @@ module ROM
|
|
36
32
|
end
|
37
33
|
|
38
34
|
# @api private
|
39
|
-
def dup
|
40
|
-
self.class.new(elements.dup)
|
41
|
-
end
|
35
|
+
def dup = self.class.new(elements.dup)
|
42
36
|
|
43
37
|
# @api private
|
44
|
-
def values
|
45
|
-
self.class.new(elements.select(&:value?))
|
46
|
-
end
|
38
|
+
def values = self.class.new(elements.select(&:value?))
|
47
39
|
|
48
40
|
# @api private
|
49
41
|
def associations
|
50
42
|
self.class.new(elements.select { |e| e.is_a?(Attributes::Association::Core) })
|
51
43
|
end
|
52
44
|
|
53
|
-
def reject(&
|
54
|
-
|
45
|
+
def reject(&) = self.class.new(elements.reject(&))
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def inspect
|
49
|
+
"#<#{self.class} #{elements.inspect}>"
|
55
50
|
end
|
51
|
+
alias_method :to_s, :inspect
|
56
52
|
|
57
53
|
private
|
58
54
|
|
59
55
|
# @api private
|
60
|
-
def tsort_each_node(&
|
61
|
-
each(&block)
|
62
|
-
end
|
56
|
+
def tsort_each_node(&) = each(&)
|
63
57
|
|
64
58
|
# @api private
|
65
|
-
def tsort_each_child(attr, &
|
66
|
-
attr.dependency_names.map { |name| self[name] }.compact.each(&
|
59
|
+
def tsort_each_child(attr, &)
|
60
|
+
attr.dependency_names.map { |name| self[name] }.compact.each(&)
|
67
61
|
end
|
68
62
|
end
|
69
63
|
end
|
@@ -6,8 +6,8 @@ module ROM::Factory
|
|
6
6
|
# rubocop:disable Style/OptionalArguments
|
7
7
|
module Association
|
8
8
|
class << self
|
9
|
-
def new(assoc,
|
10
|
-
const_get(assoc.definition.type).new(assoc,
|
9
|
+
def new(assoc, ...)
|
10
|
+
const_get(assoc.definition.type).new(assoc, ...)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -24,9 +24,7 @@ module ROM::Factory
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# @api private
|
27
|
-
def through?
|
28
|
-
false
|
29
|
-
end
|
27
|
+
def through? = false
|
30
28
|
|
31
29
|
# @api private
|
32
30
|
def builder
|
@@ -34,34 +32,22 @@ module ROM::Factory
|
|
34
32
|
end
|
35
33
|
|
36
34
|
# @api private
|
37
|
-
def name
|
38
|
-
assoc.key
|
39
|
-
end
|
35
|
+
def name = assoc.key
|
40
36
|
|
41
37
|
# @api private
|
42
|
-
def dependency?(*)
|
43
|
-
false
|
44
|
-
end
|
38
|
+
def dependency?(*) = false
|
45
39
|
|
46
40
|
# @api private
|
47
|
-
def value?
|
48
|
-
false
|
49
|
-
end
|
41
|
+
def value? = false
|
50
42
|
|
51
43
|
# @api private
|
52
|
-
def factories
|
53
|
-
builder.factories
|
54
|
-
end
|
44
|
+
def factories = builder.factories
|
55
45
|
|
56
46
|
# @api private
|
57
|
-
def foreign_key
|
58
|
-
assoc.foreign_key
|
59
|
-
end
|
47
|
+
def foreign_key = assoc.foreign_key
|
60
48
|
|
61
49
|
# @api private
|
62
|
-
def count
|
63
|
-
options.fetch(:count, 1)
|
64
|
-
end
|
50
|
+
def count = options.fetch(:count, 1)
|
65
51
|
end
|
66
52
|
|
67
53
|
# @api private
|
@@ -73,19 +59,20 @@ module ROM::Factory
|
|
73
59
|
|
74
60
|
assoc_data = attrs.fetch(name, EMPTY_HASH)
|
75
61
|
|
76
|
-
if assoc_data.is_a?(Hash) && assoc_data[assoc.target.primary_key] && !attrs[foreign_key]
|
62
|
+
if assoc_data.is_a?(::Hash) && assoc_data[assoc.target.primary_key] && !attrs[foreign_key]
|
77
63
|
assoc.associate(attrs, attrs[name])
|
78
|
-
elsif assoc_data.is_a?(ROM::Struct)
|
64
|
+
elsif assoc_data.is_a?(::ROM::Struct)
|
79
65
|
assoc.associate(attrs, assoc_data)
|
80
66
|
else
|
81
|
-
parent =
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
67
|
+
parent =
|
68
|
+
if persist && !attrs[foreign_key]
|
69
|
+
builder.persistable.create(*parent_traits, **assoc_data)
|
70
|
+
else
|
71
|
+
builder.struct(
|
72
|
+
*parent_traits,
|
73
|
+
**assoc_data, assoc.target.primary_key => attrs[foreign_key]
|
74
|
+
)
|
75
|
+
end
|
89
76
|
|
90
77
|
tuple = {name => parent}
|
91
78
|
|
@@ -112,7 +99,7 @@ module ROM::Factory
|
|
112
99
|
def call(attrs = EMPTY_HASH, parent, persist: true)
|
113
100
|
return if attrs.key?(name)
|
114
101
|
|
115
|
-
structs = Array.new(count).map do
|
102
|
+
structs = ::Array.new(count).map do
|
116
103
|
# hash which contains the foreign key info, i.e: { user_id: 1 }
|
117
104
|
association_hash = assoc.associate(attrs, parent)
|
118
105
|
|
@@ -127,9 +114,7 @@ module ROM::Factory
|
|
127
114
|
end
|
128
115
|
|
129
116
|
# @api private
|
130
|
-
def dependency?(rel)
|
131
|
-
assoc.source == rel
|
132
|
-
end
|
117
|
+
def dependency?(rel) = assoc.source == rel
|
133
118
|
end
|
134
119
|
|
135
120
|
# @api private
|
@@ -143,14 +128,15 @@ module ROM::Factory
|
|
143
128
|
|
144
129
|
association_hash = assoc.associate(attrs, parent)
|
145
130
|
|
146
|
-
struct =
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
131
|
+
struct =
|
132
|
+
if persist
|
133
|
+
builder.persistable.create(*traits, **association_hash)
|
134
|
+
else
|
135
|
+
belongs_to_name = ::ROM::Inflector.singularize(assoc.source_alias)
|
136
|
+
belongs_to_associations = {belongs_to_name.to_sym => parent}
|
137
|
+
final_attrs = attrs.merge(association_hash).merge(belongs_to_associations)
|
138
|
+
builder.struct(*traits, **final_attrs)
|
139
|
+
end
|
154
140
|
|
155
141
|
{name => struct}
|
156
142
|
end
|
@@ -175,7 +161,7 @@ module ROM::Factory
|
|
175
161
|
if through_factory?
|
176
162
|
structs.each do |child|
|
177
163
|
through_attrs = {
|
178
|
-
|
164
|
+
::ROM::Inflector.singularize(assoc.source.name.key).to_sym => parent,
|
179
165
|
assoc.through.assoc_name => child
|
180
166
|
}
|
181
167
|
|
@@ -191,37 +177,27 @@ module ROM::Factory
|
|
191
177
|
end
|
192
178
|
end
|
193
179
|
|
194
|
-
def result(structs)
|
195
|
-
{name => structs}
|
196
|
-
end
|
180
|
+
def result(structs) = {name => structs}
|
197
181
|
|
198
|
-
def dependency?(rel)
|
199
|
-
assoc.source == rel
|
200
|
-
end
|
182
|
+
def dependency?(rel) = assoc.source == rel
|
201
183
|
|
202
|
-
def through?
|
203
|
-
true
|
204
|
-
end
|
184
|
+
def through? = true
|
205
185
|
|
206
186
|
def through_factory?
|
207
187
|
factories.registry.key?(through_factory_name)
|
208
188
|
end
|
209
189
|
|
210
190
|
def through_factory_name
|
211
|
-
ROM::Inflector.singularize(assoc.definition.through.source).to_sym
|
191
|
+
::ROM::Inflector.singularize(assoc.definition.through.source).to_sym
|
212
192
|
end
|
213
193
|
|
214
194
|
private
|
215
195
|
|
216
|
-
def tpk
|
217
|
-
assoc.target.primary_key
|
218
|
-
end
|
196
|
+
def tpk = assoc.target.primary_key
|
219
197
|
end
|
220
198
|
|
221
199
|
class OneToOneThrough < ManyToMany
|
222
|
-
def result(structs)
|
223
|
-
{name => structs[0]}
|
224
|
-
end
|
200
|
+
def result(structs) = {name => structs[0]}
|
225
201
|
end
|
226
202
|
end
|
227
203
|
end
|
@@ -28,6 +28,12 @@ module ROM::Factory
|
|
28
28
|
def dependency_names
|
29
29
|
block.parameters.map(&:last)
|
30
30
|
end
|
31
|
+
|
32
|
+
# @api private
|
33
|
+
def inspect
|
34
|
+
"#<#{self.class.name} #{name} at #{block.source_location.join(":")}>"
|
35
|
+
end
|
36
|
+
alias_method :to_s, :inspect
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
data/lib/rom/factory/dsl.rb
CHANGED
@@ -12,26 +12,39 @@ module ROM
|
|
12
12
|
|
13
13
|
class << self
|
14
14
|
# @api private
|
15
|
-
def fake(*args, **options)
|
16
|
-
|
15
|
+
def fake(*args, unique: false, **options)
|
16
|
+
factory, produce = fetch_or_store(:faker, unique, *args) do
|
17
17
|
*ns, method_name = args
|
18
18
|
|
19
19
|
const = ns.reduce(::Faker) do |obj, name|
|
20
|
-
obj.const_get(::
|
20
|
+
obj.const_get(::ROM::Inflector.camelize(name))
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
if unique
|
24
|
+
[const.unique, method_name]
|
25
|
+
else
|
26
|
+
[const, method_name]
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
|
-
|
30
|
+
factory.public_send(produce, **options)
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
34
|
# Factory builder DSL
|
31
35
|
#
|
32
36
|
# @api public
|
33
|
-
class DSL < BasicObject
|
34
|
-
|
37
|
+
class DSL < ::BasicObject
|
38
|
+
# @api private
|
39
|
+
module Kernel
|
40
|
+
%i[binding class instance_of? is_a? rand respond_to_missing? singleton_class].each do |meth|
|
41
|
+
define_method(meth, ::Kernel.instance_method(meth))
|
42
|
+
end
|
43
|
+
|
44
|
+
private :respond_to_missing?, :rand, :binding
|
45
|
+
end
|
46
|
+
|
47
|
+
include Kernel
|
35
48
|
|
36
49
|
attr_reader :_name, :_relation, :_attributes, :_factories, :_struct_namespace, :_valid_names
|
37
50
|
attr_reader :_traits
|
@@ -64,17 +77,15 @@ module ROM
|
|
64
77
|
# @param [Symbol] The name of the registered builder
|
65
78
|
#
|
66
79
|
# @api public
|
67
|
-
def create(name, *args)
|
68
|
-
_factories[name, *args]
|
69
|
-
end
|
80
|
+
def create(name, *args) = _factories[name, *args]
|
70
81
|
|
71
82
|
# Create a sequence attribute
|
72
83
|
#
|
73
84
|
# @param [Symbol] name The attribute name
|
74
85
|
#
|
75
86
|
# @api private
|
76
|
-
def sequence(meth, &
|
77
|
-
define_sequence(meth,
|
87
|
+
def sequence(meth, &)
|
88
|
+
define_sequence(meth, &) if _valid_names.include?(meth)
|
78
89
|
end
|
79
90
|
|
80
91
|
# Set timestamp attributes
|
@@ -104,9 +115,13 @@ module ROM
|
|
104
115
|
# @example
|
105
116
|
# f.email { fake(:number, :between, from: 10, to: 100) }
|
106
117
|
#
|
118
|
+
# @example
|
119
|
+
# f.email { fake(:internet, :email, unique: true) }
|
120
|
+
#
|
107
121
|
# @param [Symbol] genre The faker API identifier ie. :internet, :product etc.
|
108
122
|
# @param [Symbol] type The value type to generate
|
109
|
-
# @param [Hash] options Additional arguments
|
123
|
+
# @param [Hash] options Additional arguments, including unique: true will generate unique values
|
124
|
+
#
|
110
125
|
#
|
111
126
|
# @overload fake(genre, subgenre, type, **options)
|
112
127
|
# @example
|
@@ -120,11 +135,9 @@ module ROM
|
|
120
135
|
# @see https://github.com/faker-ruby/faker/tree/master/doc
|
121
136
|
#
|
122
137
|
# @api public
|
123
|
-
def fake(
|
124
|
-
::ROM::Factory.fake(type, *args, **options)
|
125
|
-
end
|
138
|
+
def fake(...) = ::ROM::Factory.fake(...)
|
126
139
|
|
127
|
-
def trait(name, parents = [], &
|
140
|
+
def trait(name, parents = [], &)
|
128
141
|
_traits[name] = DSL.new(
|
129
142
|
"#{_name}_#{name}",
|
130
143
|
attributes: _traits.values_at(*parents).flat_map(&:elements).inject(
|
@@ -133,7 +146,7 @@ module ROM
|
|
133
146
|
relation: _relation,
|
134
147
|
factories: _factories,
|
135
148
|
struct_namespace: _struct_namespace,
|
136
|
-
&
|
149
|
+
&
|
137
150
|
)._attributes
|
138
151
|
end
|
139
152
|
|
@@ -145,12 +158,16 @@ module ROM
|
|
145
158
|
# @example has-many
|
146
159
|
# f.association(:posts, count: 2)
|
147
160
|
#
|
161
|
+
# @example adding traits
|
162
|
+
# f.association(:posts, traits: [:published])
|
163
|
+
#
|
148
164
|
# @param [Symbol] name The name of the configured association
|
149
165
|
# @param [Hash] options Additional options
|
150
166
|
# @option options [Integer] count Number of objects to generate
|
167
|
+
# @option options [Array<Symbol>] traits Traits to apply to the association
|
151
168
|
#
|
152
169
|
# @api public
|
153
|
-
def association(name, *traits, **options)
|
170
|
+
def association(name, *seq_traits, traits: EMPTY_ARRAY, **options)
|
154
171
|
assoc = _relation.associations[name]
|
155
172
|
|
156
173
|
if assoc.is_a?(::ROM::SQL::Associations::OneToOne) && options.fetch(:count, 1) > 1
|
@@ -159,15 +176,25 @@ module ROM
|
|
159
176
|
|
160
177
|
builder = -> { _factories.for_relation(assoc.target) }
|
161
178
|
|
162
|
-
_attributes << attributes::Association.new(
|
179
|
+
_attributes << attributes::Association.new(
|
180
|
+
assoc,
|
181
|
+
builder,
|
182
|
+
*seq_traits,
|
183
|
+
*traits,
|
184
|
+
**options
|
185
|
+
)
|
163
186
|
end
|
164
187
|
|
188
|
+
# @api private
|
189
|
+
def inspect = "#<#{self.class} name=#{_name}>"
|
190
|
+
alias_method :to_s, :inspect
|
191
|
+
|
165
192
|
private
|
166
193
|
|
167
194
|
# @api private
|
168
|
-
def method_missing(meth,
|
195
|
+
def method_missing(meth, ...)
|
169
196
|
if _valid_names.include?(meth)
|
170
|
-
define_attr(meth,
|
197
|
+
define_attr(meth, ...)
|
171
198
|
else
|
172
199
|
super
|
173
200
|
end
|
@@ -175,27 +202,26 @@ module ROM
|
|
175
202
|
|
176
203
|
# @api private
|
177
204
|
def respond_to_missing?(method_name, include_private = false)
|
178
|
-
_valid_names.include?(
|
205
|
+
_valid_names.include?(method_name) || super
|
179
206
|
end
|
180
207
|
|
181
208
|
# @api private
|
182
|
-
def define_sequence(name,
|
183
|
-
_attributes << attributes::Callable.new(name, self, attributes::Sequence.new(name, &
|
209
|
+
def define_sequence(name, &)
|
210
|
+
_attributes << attributes::Callable.new(name, self, attributes::Sequence.new(name, &))
|
184
211
|
end
|
185
212
|
|
186
213
|
# @api private
|
187
214
|
def define_attr(name, *args, &block)
|
188
|
-
_attributes <<
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
215
|
+
_attributes <<
|
216
|
+
if block
|
217
|
+
attributes::Callable.new(name, self, block)
|
218
|
+
else
|
219
|
+
attributes::Value.new(name, *args)
|
220
|
+
end
|
193
221
|
end
|
194
222
|
|
195
223
|
# @api private
|
196
|
-
def attributes
|
197
|
-
::ROM::Factory::Attributes
|
198
|
-
end
|
224
|
+
def attributes = ::ROM::Factory::Attributes
|
199
225
|
end
|
200
226
|
end
|
201
227
|
end
|
@@ -126,7 +126,7 @@ module ROM::Factory
|
|
126
126
|
# @return [ROM::Factory::Builder]
|
127
127
|
#
|
128
128
|
# @api public
|
129
|
-
def define(spec, opts = EMPTY_HASH, &
|
129
|
+
def define(spec, opts = EMPTY_HASH, &)
|
130
130
|
name, parent = spec.is_a?(Hash) ? spec.flatten(1) : spec
|
131
131
|
namespace = opts[:struct_namespace]
|
132
132
|
relation_name = opts.fetch(:relation) { infer_relation(name) }
|
@@ -137,7 +137,7 @@ module ROM::Factory
|
|
137
137
|
|
138
138
|
builder =
|
139
139
|
if parent
|
140
|
-
extend_builder(name, registry[parent], relation_name, namespace, &
|
140
|
+
extend_builder(name, registry[parent], relation_name, namespace, &)
|
141
141
|
else
|
142
142
|
relation = rom.relations[relation_name]
|
143
143
|
DSL.new(
|
@@ -145,7 +145,7 @@ module ROM::Factory
|
|
145
145
|
relation: relation,
|
146
146
|
factories: self,
|
147
147
|
struct_namespace: builder_struct_namespace(namespace),
|
148
|
-
&
|
148
|
+
&
|
149
149
|
).call
|
150
150
|
end
|
151
151
|
|
@@ -170,6 +170,7 @@ module ROM::Factory
|
|
170
170
|
def [](name, *traits, **attrs)
|
171
171
|
registry[name].struct_namespace(struct_namespace).persistable.create(*traits, **attrs)
|
172
172
|
end
|
173
|
+
alias_method :create, :[]
|
173
174
|
|
174
175
|
# Return in-memory struct builder
|
175
176
|
#
|
@@ -180,6 +181,25 @@ module ROM::Factory
|
|
180
181
|
@__structs__ ||= Structs.new(registry, struct_namespace)
|
181
182
|
end
|
182
183
|
|
184
|
+
# Return a new, non-persisted struct
|
185
|
+
#
|
186
|
+
# @example create a struct with default attributes
|
187
|
+
# MyFactory.build(:user)
|
188
|
+
#
|
189
|
+
# @example create a struct with some attributes overridden
|
190
|
+
# MyFactory.build(:uesr, name: "Jane")
|
191
|
+
#
|
192
|
+
# @param [Symbol] name The name of the registered factory
|
193
|
+
# @param [Array<Symbol>] traits List of traits to apply
|
194
|
+
# @param [Hash] attrs optional attributes to override the defaults
|
195
|
+
#
|
196
|
+
# @return [ROM::Struct]
|
197
|
+
#
|
198
|
+
# @api public
|
199
|
+
def build(name, *traits, **attrs)
|
200
|
+
structs[name, *traits, **attrs]
|
201
|
+
end
|
202
|
+
|
183
203
|
# Get factories with a custom struct namespace
|
184
204
|
#
|
185
205
|
# @example
|
@@ -213,16 +233,16 @@ module ROM::Factory
|
|
213
233
|
|
214
234
|
# @api private
|
215
235
|
def infer_factory_name(name)
|
216
|
-
::
|
236
|
+
::ROM::Inflector.singularize(name).to_sym
|
217
237
|
end
|
218
238
|
|
219
239
|
# @api private
|
220
240
|
def infer_relation(name)
|
221
|
-
::
|
241
|
+
::ROM::Inflector.pluralize(name).to_sym
|
222
242
|
end
|
223
243
|
|
224
244
|
# @api private
|
225
|
-
def extend_builder(name, parent, relation_name, ns, &
|
245
|
+
def extend_builder(name, parent, relation_name, ns, &)
|
226
246
|
namespace = parent.options[:struct_namespace]
|
227
247
|
namespace = builder_struct_namespace(ns) if ns
|
228
248
|
relation = rom.relations.fetch(relation_name) { parent.relation }
|
@@ -232,7 +252,7 @@ module ROM::Factory
|
|
232
252
|
relation: relation,
|
233
253
|
factories: self,
|
234
254
|
struct_namespace: namespace,
|
235
|
-
&
|
255
|
+
&
|
236
256
|
).call
|
237
257
|
end
|
238
258
|
end
|
@@ -151,9 +151,9 @@ module ROM
|
|
151
151
|
|
152
152
|
# @api private
|
153
153
|
def evaluate_values(attrs)
|
154
|
-
attributes.values.tsort.each_with_object(
|
155
|
-
deps = attr.dependency_names.
|
156
|
-
result = attr.(
|
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)
|
157
157
|
|
158
158
|
if result
|
159
159
|
h.update(result)
|
@@ -162,7 +162,7 @@ module ROM
|
|
162
162
|
end
|
163
163
|
|
164
164
|
def evaluate_traits(trait_list, attrs, opts)
|
165
|
-
return
|
165
|
+
return EMPTY_HASH if trait_list.empty?
|
166
166
|
|
167
167
|
traits = trait_list.map { |v| v.is_a?(Hash) ? v : {v => true} }.reduce(:merge)
|
168
168
|
|
@@ -208,7 +208,7 @@ module ROM
|
|
208
208
|
|
209
209
|
def select_mergeable_attrs(traits, attrs)
|
210
210
|
unmergeable = assocs(traits).select(&:through?).map do |a|
|
211
|
-
|
211
|
+
::ROM::Inflector.singularize(a.assoc.target.name.to_sym).to_sym
|
212
212
|
end
|
213
213
|
attrs.dup.delete_if { |key, _| unmergeable.include?(key) }
|
214
214
|
end
|
data/lib/rom/factory/version.rb
CHANGED
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(
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
5
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require
|
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 =
|
15
|
-
spec.description =
|
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[
|
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 = ">=
|
28
|
+
spec.required_ruby_version = ">= 3.1.0"
|
29
29
|
|
30
|
-
spec.
|
31
|
-
spec.
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
54
|
+
version: '1.7'
|
56
55
|
- !ruby/object:Gem::Dependency
|
57
56
|
name: faker
|
58
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,14 +78,14 @@ dependencies:
|
|
79
78
|
requirements:
|
80
79
|
- - "~>"
|
81
80
|
- !ruby/object:Gem::Version
|
82
|
-
version: '5.
|
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.
|
88
|
+
version: '5.4'
|
90
89
|
description: ''
|
91
90
|
email:
|
92
91
|
- janjiss@gmail.com
|
@@ -106,12 +105,12 @@ files:
|
|
106
105
|
- ".github/workflows/rubocop.yml"
|
107
106
|
- ".github/workflows/sync_configs.yml"
|
108
107
|
- ".gitignore"
|
108
|
+
- ".postgres.env"
|
109
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
|
-
|
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:
|
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.
|
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
|