rom-factory 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +22 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +21 -0
- data/README.md +236 -0
- data/Rakefile +6 -0
- data/lib/rom-factory.rb +1 -0
- data/lib/rom/factory.rb +14 -0
- data/lib/rom/factory/attributes/callable.rb +20 -0
- data/lib/rom/factory/attributes/regular.rb +13 -0
- data/lib/rom/factory/attributes/sequence.rb +18 -0
- data/lib/rom/factory/builder.rb +55 -0
- data/lib/rom/factory/dsl.rb +96 -0
- data/lib/rom/factory/factories.rb +68 -0
- data/lib/rom/factory/struct.rb +21 -0
- data/lib/rom/factory/version.rb +5 -0
- data/rom-factory.gemspec +34 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fb5feab75eb72799779fea6582a2cd96c84f7e65
|
4
|
+
data.tar.gz: 771008f99234f6bd33b07581995783118c415241
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 76efd0835cc269ab37364c1d164698d5707733a932cd3bb0594b6bf7be697c6f708dc553ecaa4fb6c074d1483084d23bf09896207ccb5cfa7eca6e07e4208710
|
7
|
+
data.tar.gz: 66e2d00acfbcc2147299f34df216f07c8e240d7a9af4b97f6fe590483a6023c77ac9d2f591354e3303232f7cd51ecd0a0023034e798118a8a6a5aad0c6c3f129
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
language: ruby
|
2
|
+
dist: trusty
|
3
|
+
sudo: required
|
4
|
+
cache: bundler
|
5
|
+
bundler_args: --without tools
|
6
|
+
script: "bundle exec rake spec"
|
7
|
+
after_success:
|
8
|
+
- '[ "${TRAVIS_JOB_NUMBER#*.}" = "1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
|
9
|
+
rvm:
|
10
|
+
- 2.4.0
|
11
|
+
- 2.3
|
12
|
+
- 2.2
|
13
|
+
- rbx-3
|
14
|
+
- jruby-9.1.6.0
|
15
|
+
env:
|
16
|
+
global:
|
17
|
+
- COVERAGE='true'
|
18
|
+
notifications:
|
19
|
+
webhooks:
|
20
|
+
urls:
|
21
|
+
- https://webhooks.gitter.im/e/39e1225f489f38b0bd09
|
22
|
+
on_success: change
|
23
|
+
on_failure: always
|
24
|
+
on_start: false
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
## v0.4.0 2017-03-03
|
2
|
+
|
3
|
+
This is a revamp of the original `rom_factory` gem which adds new features and
|
4
|
+
improves internals.
|
5
|
+
|
6
|
+
### Added
|
7
|
+
|
8
|
+
* Support for defining multiple factories via `MyFactory = ROM::Factory.configure { |c| ... }` (solnic)
|
9
|
+
* Support for builder inheritence via `define(admin: :user) { |f| ... }` (solnic)
|
10
|
+
* Support for generating in-memory structs via `MyFactory.structs[:user]` that are not persisted (solnic)
|
11
|
+
* Support for `belongs_to` associations via `f.association(:user)` (solnic)
|
12
|
+
* New DSL for defining builders `MyFactory.define(:user) { |f| ... }` which infers default relation name (solnic)
|
13
|
+
* New factory method `MyFactory#[]` ie `MyFactory[:user, name: "Jane"]` (solnic)
|
14
|
+
* New `fake` helper which uses faker gem under the hood ie `f.email { fake(:internet, :email) }` (solnic)
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
* `Rom::Factory::Config.configure` was replaced with `ROM::Factory.configure` (solnic)
|
19
|
+
* Global factory config and builders are gone (solnic)
|
20
|
+
* Structs are now based on dry-struct (solnic)
|
21
|
+
|
22
|
+
[Compare v0.3.1...v0.4.0](https://github.com/rom-rb/rom-factory/compare/v0.3.1...v0.4.0)
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at janjiss@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'rake', '~> 12.0'
|
6
|
+
gem 'simplecov', require: false, platform: :mri
|
7
|
+
gem 'codeclimate-test-reporter', require: false, platform: :mri
|
8
|
+
|
9
|
+
gem 'rspec', '~> 3.0'
|
10
|
+
gem 'rom', '~> 3.0'
|
11
|
+
gem 'rom-sql', '~> 1.0'
|
12
|
+
gem 'sqlite3', '~> 1.3', platforms: [:mri, :rbx]
|
13
|
+
gem 'jdbc-sqlite3', platforms: :jruby
|
14
|
+
|
15
|
+
group :tools do
|
16
|
+
gem 'byebug', platform: :mri
|
17
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Janis Miezitis
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
[gem]: https://rubygems.org/gems/rom-factory
|
2
|
+
[travis]: https://travis-ci.org/rom-rb/rom-factory
|
3
|
+
[gemnasium]: https://gemnasium.com/rom-rb/rom-factory
|
4
|
+
[codeclimate]: https://codeclimate.com/github/rom-rb/rom-factory
|
5
|
+
[inchpages]: http://inch-ci.org/github/rom-rb/rom-factory
|
6
|
+
|
7
|
+
# rom-factory
|
8
|
+
|
9
|
+
[![Gem Version](https://badge.fury.io/rb/rom-factory.svg)][gem]
|
10
|
+
[![Build Status](https://travis-ci.org/rom-rb/rom-factory.svg?branch=master)][travis]
|
11
|
+
[![Dependency Status](https://gemnasium.com/rom-rb/rom-factory.svg)][gemnasium]
|
12
|
+
[![Code Climate](https://codeclimate.com/github/rom-rb/rom-factory/badges/gpa.svg)][codeclimate]
|
13
|
+
[![Test Coverage](https://codeclimate.com/github/rom-rb/rom-factory/badges/coverage.svg)][codeclimate]
|
14
|
+
[![Inline docs](http://inch-ci.org/github/rom-rb/rom-factory.svg?branch=master)][inchpages]
|
15
|
+
|
16
|
+
Data generator with support for persistence backends, built on top of [rom-rb](http://rom-rb.org) and [dry-rb](http://dry-rb.org).
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'rom-factory'
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install rom-factory
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
First, you have to define ROM container:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
rom = ROM.container(:sql, 'sqlite::memory') do |conf|
|
40
|
+
conf.default.create_table(:users) do
|
41
|
+
primary_key :id
|
42
|
+
column :last_name, String, null: false
|
43
|
+
column :first_name, String, null: false
|
44
|
+
column :email, String, null: false
|
45
|
+
column :admin, TrueClass, null: false, default: false
|
46
|
+
column :created_at, Time, null: false
|
47
|
+
column :updated_at, Time, null: false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
> Notice that if you're using ROM in your application, then simply set up factory using your existing ROM container
|
53
|
+
|
54
|
+
Once that is done, you will have to specify which container ROM::Factory will use:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
Factory = ROM::Factory.configure do |config|
|
58
|
+
config.rom = rom
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
This returns a new factory ready to be used.
|
63
|
+
|
64
|
+
## Simple use case
|
65
|
+
|
66
|
+
After configuration is done, you can define builders using your `Factory`:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
Factory.define(:user) do |f|
|
70
|
+
f.first_name "Janis"
|
71
|
+
f.last_name "Miezitis"
|
72
|
+
f.email "janjiss@gmail.com"
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
Then you can use it to generate data:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
user = Factory[:user]
|
80
|
+
user.email #=> "janjiss@gmail.com"
|
81
|
+
```
|
82
|
+
|
83
|
+
### Callable properties
|
84
|
+
|
85
|
+
You can easily define dynamic (callbale) properties if value needs to change every time it needs to be called.
|
86
|
+
Anything that responds to `.call` can be a dynamic property.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Factory.define(:user) do |f|
|
90
|
+
f.first_name "Janis"
|
91
|
+
f.last_name "Miezitis"
|
92
|
+
f.email "janjiss@gmail.com"
|
93
|
+
f.created_at {Time.now}
|
94
|
+
end
|
95
|
+
|
96
|
+
user = Factory[:user]
|
97
|
+
user.created_at #=> 2016-08-27 18:17:08 -0500
|
98
|
+
```
|
99
|
+
|
100
|
+
### Sequencing
|
101
|
+
|
102
|
+
If you want attributes to be unique each time you generate data, you can use sequence to achieve that:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Factory.define(:user) do |f|
|
106
|
+
f.first_name "Janis"
|
107
|
+
f.last_name "Miezitis"
|
108
|
+
f.sequence :email do |n|
|
109
|
+
"janjiss#{n}@gmail.com"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
user1 = Factory[:user]
|
114
|
+
user2 = Factory[:user]
|
115
|
+
|
116
|
+
user1.email #=> janjiss1@gmail.com
|
117
|
+
user2.email #=> janjiss2@gmail.com
|
118
|
+
```
|
119
|
+
|
120
|
+
### Timestamps
|
121
|
+
|
122
|
+
There is a support for timestamps for `created_at` and `updated_at` attributes:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Factory.define(:user) do |f|
|
126
|
+
f.first_name "Janis"
|
127
|
+
f.last_name "Miezitis"
|
128
|
+
f.email "janjiss@gmail.com"
|
129
|
+
f.timestamps
|
130
|
+
end
|
131
|
+
|
132
|
+
user = Factory[:user]
|
133
|
+
user.created_at #=> 2016-08-27 18:17:08 -0500
|
134
|
+
user.updated_at #=> 2016-08-27 18:17:10 -0500
|
135
|
+
```
|
136
|
+
|
137
|
+
### Associations
|
138
|
+
|
139
|
+
If you defined associations in your relations, you can use `association` builder:
|
140
|
+
|
141
|
+
``` ruby
|
142
|
+
factories.define(:user) do |f|
|
143
|
+
f.first_name 'Jane'
|
144
|
+
f.last_name 'Doe'
|
145
|
+
f.email 'jane@doe.org'
|
146
|
+
f.timestamps
|
147
|
+
end
|
148
|
+
|
149
|
+
factories.define(:task) do |f|
|
150
|
+
f.title 'A task'
|
151
|
+
f.association(:user)
|
152
|
+
end
|
153
|
+
|
154
|
+
task = factories[:task]
|
155
|
+
```
|
156
|
+
|
157
|
+
> Currently only `belongs_to` is supported
|
158
|
+
|
159
|
+
### Fake data generator
|
160
|
+
|
161
|
+
There's a builtin support for [Faker](https://github.com/stympy/faker) gem with a `fake` shortcut in the DSL:
|
162
|
+
|
163
|
+
|
164
|
+
``` ruby
|
165
|
+
factories.define(:user) do |f|
|
166
|
+
f.first_name { fake(:name, :first_name) }
|
167
|
+
f.last_name { fake(:name, :last_name) }
|
168
|
+
f.email { fake(:internet, :email) }
|
169
|
+
f.timestamps
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
### Extending existing builders
|
174
|
+
|
175
|
+
You can create a hierarchy of builders easily, which is useful if you want to share data generation logic across
|
176
|
+
multiple definitions:
|
177
|
+
|
178
|
+
``` ruby
|
179
|
+
Factory.define(:user) do |f|
|
180
|
+
f.first_name "Janis"
|
181
|
+
f.last_name "Miezitis"
|
182
|
+
f.email "janjiss@gmail.com"
|
183
|
+
f.admin false
|
184
|
+
f.timestamps
|
185
|
+
end
|
186
|
+
|
187
|
+
Factory.define(admin: :user) do |f|
|
188
|
+
f.admin true
|
189
|
+
end
|
190
|
+
|
191
|
+
user = Factory[:admin]
|
192
|
+
|
193
|
+
user.admin # true
|
194
|
+
```
|
195
|
+
|
196
|
+
### Setting up relation backend explicitly
|
197
|
+
|
198
|
+
By default, relation is configured automatically based on the builder name. For example if your builder is called `:user`, then `:users`
|
199
|
+
relation name will be inferred. If you want to set up a relation explicitly, use `:relation` option:
|
200
|
+
|
201
|
+
``` ruby
|
202
|
+
Factory.define(:user, relation: :people) do |f|
|
203
|
+
f.first_name "Janis"
|
204
|
+
f.last_name "Miezitis"
|
205
|
+
f.email "janjiss@gmail.com"
|
206
|
+
f.admin false
|
207
|
+
f.timestamps
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
### Generating structs without persistence
|
212
|
+
|
213
|
+
You can generate struct objects without persisting them using `#structs` generator:
|
214
|
+
|
215
|
+
``` ruby
|
216
|
+
Factory.define(:user) do |f|
|
217
|
+
f.first_name "Janis"
|
218
|
+
f.last_name "Miezitis"
|
219
|
+
f.email "janjiss@gmail.com"
|
220
|
+
f.admin false
|
221
|
+
f.timestamps
|
222
|
+
end
|
223
|
+
|
224
|
+
user = Factory.structs[:user]
|
225
|
+
|
226
|
+
user.id # auto-generated fake PK
|
227
|
+
user.first_name # "Janis"
|
228
|
+
```
|
229
|
+
|
230
|
+
## Credits
|
231
|
+
|
232
|
+
This project was originally created by [Jānis Miezītis](https://github.com/janjiss) and eventually moved to `rom-rb` organization.
|
233
|
+
|
234
|
+
## License
|
235
|
+
|
236
|
+
See `LICENSE.txt` file.
|
data/Rakefile
ADDED
data/lib/rom-factory.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rom/factory'
|
data/lib/rom/factory.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dry/core/class_builder'
|
2
|
+
require 'rom/factory/factories'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module Factory
|
6
|
+
DEFAULT_NAME = 'Factories'.freeze
|
7
|
+
|
8
|
+
def self.configure(name = DEFAULT_NAME, &block)
|
9
|
+
Dry::Core::ClassBuilder.new(name: name, parent: Factories).call do |klass|
|
10
|
+
klass.configure(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ROM::Factory
|
2
|
+
module Attributes
|
3
|
+
class Callable
|
4
|
+
attr_reader :dsl, :block
|
5
|
+
|
6
|
+
def initialize(dsl, block)
|
7
|
+
@dsl = dsl
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
if block.is_a?(Proc)
|
13
|
+
dsl.instance_exec(&block)
|
14
|
+
else
|
15
|
+
block.call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'rom/factory/struct'
|
3
|
+
|
4
|
+
module ROM::Factory
|
5
|
+
class Builder
|
6
|
+
attr_reader :schema, :relation
|
7
|
+
|
8
|
+
def initialize(schema, relation)
|
9
|
+
@schema = schema
|
10
|
+
@relation = relation
|
11
|
+
@sequence = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def tuple(attrs)
|
15
|
+
schema.map {|k, v| [k, v.call] }.to_h.merge(attrs)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(attrs = {})
|
19
|
+
struct(tuple(attrs.merge(primary_key => next_id)))
|
20
|
+
end
|
21
|
+
|
22
|
+
def struct(attrs)
|
23
|
+
Struct.define(relation.name.relation, relation.schema.project(*attrs.keys)).new(attrs)
|
24
|
+
end
|
25
|
+
|
26
|
+
def persistable
|
27
|
+
Persistable.new(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def primary_key
|
31
|
+
relation.primary_key
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def next_id
|
37
|
+
@sequence += 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Persistable < SimpleDelegator
|
42
|
+
attr_reader :builder, :relation
|
43
|
+
|
44
|
+
def initialize(builder, relation = builder.relation)
|
45
|
+
super(builder)
|
46
|
+
@builder = builder
|
47
|
+
@relation = relation
|
48
|
+
end
|
49
|
+
|
50
|
+
def create(attrs = {})
|
51
|
+
tuple = builder.tuple(attrs)
|
52
|
+
struct(tuple.merge(primary_key => relation.insert(tuple)))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'faker'
|
2
|
+
require 'dry/core/inflector'
|
3
|
+
|
4
|
+
require 'rom/factory/builder'
|
5
|
+
require 'rom/factory/attributes/regular'
|
6
|
+
require 'rom/factory/attributes/callable'
|
7
|
+
require 'rom/factory/attributes/sequence'
|
8
|
+
|
9
|
+
module ROM
|
10
|
+
module Factory
|
11
|
+
def self.fake(type, *args)
|
12
|
+
api = Faker.const_get(Dry::Core::Inflector.classify(type.to_s))
|
13
|
+
meth, *rest = args
|
14
|
+
|
15
|
+
if meth.is_a?(Symbol)
|
16
|
+
api.public_send(meth, *rest)
|
17
|
+
else
|
18
|
+
api.public_send(type, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class DSL < BasicObject
|
23
|
+
attr_reader :_name, :_relation, :_schema, :_factories, :_valid_names
|
24
|
+
|
25
|
+
def initialize(name, schema: {}, relation:, factories:, &block)
|
26
|
+
@_name = name
|
27
|
+
@_relation = relation
|
28
|
+
@_factories = factories
|
29
|
+
@_schema = schema.dup
|
30
|
+
@_valid_names = _relation.schema.attributes.map(&:name)
|
31
|
+
yield(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def call
|
35
|
+
::ROM::Factory::Builder.new(_schema, _relation)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(name, *args)
|
39
|
+
_factories[name, *args]
|
40
|
+
end
|
41
|
+
|
42
|
+
def sequence(meth, &block)
|
43
|
+
if _valid_names.include?(meth)
|
44
|
+
define_sequence(meth, block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def timestamps
|
49
|
+
created_at { ::Time.now }
|
50
|
+
updated_at { ::Time.now }
|
51
|
+
end
|
52
|
+
|
53
|
+
def fake(*args)
|
54
|
+
::ROM::Factory.fake(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def association(name)
|
58
|
+
assoc = _relation.associations[name]
|
59
|
+
other = _relation.__registry__[assoc.target]
|
60
|
+
|
61
|
+
fk = _relation.foreign_key(other)
|
62
|
+
pk = other.primary_key
|
63
|
+
|
64
|
+
block = -> { create(name)[pk] }
|
65
|
+
|
66
|
+
_schema[fk] = attributes::Callable.new(self, block)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def method_missing(meth, *args, &block)
|
72
|
+
if _valid_names.include?(meth)
|
73
|
+
define_attr(meth, *args, &block)
|
74
|
+
else
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def define_sequence(name, block)
|
80
|
+
_schema[name] = attributes::Callable.new(self, attributes::Sequence.new(&block))
|
81
|
+
end
|
82
|
+
|
83
|
+
def define_attr(name, *args, &block)
|
84
|
+
if block
|
85
|
+
_schema[name] = attributes::Callable.new(self, block)
|
86
|
+
else
|
87
|
+
_schema[name] = attributes::Regular.new(*args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def attributes
|
92
|
+
::ROM::Factory::Attributes
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'dry/configurable'
|
2
|
+
require 'dry/core/inflector'
|
3
|
+
|
4
|
+
require 'rom/factory/dsl'
|
5
|
+
|
6
|
+
module ROM::Factory
|
7
|
+
class Structs
|
8
|
+
attr_reader :registry
|
9
|
+
|
10
|
+
def initialize(registry)
|
11
|
+
@registry = registry
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](name, attrs = {})
|
15
|
+
registry[name].create(attrs)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Factories
|
20
|
+
extend Dry::Configurable
|
21
|
+
|
22
|
+
setting :rom
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_reader :registry
|
26
|
+
|
27
|
+
attr_reader :structs
|
28
|
+
|
29
|
+
def inherited(klass)
|
30
|
+
registry = {}
|
31
|
+
klass.instance_variable_set(:'@registry', registry)
|
32
|
+
klass.instance_variable_set(:'@structs', Structs.new(registry))
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def define(spec, **opts, &block)
|
37
|
+
name, parent = spec.is_a?(Hash) ? spec.flatten(1) : spec
|
38
|
+
|
39
|
+
if registry.key?(name)
|
40
|
+
raise ArgumentError, "#{name.inspect} factory has been already defined"
|
41
|
+
end
|
42
|
+
|
43
|
+
builder =
|
44
|
+
if parent
|
45
|
+
extend_builder(name, registry[parent], &block)
|
46
|
+
else
|
47
|
+
relation_name = opts.fetch(:relation) { infer_relation(name) }
|
48
|
+
relation = config.rom.relations[relation_name]
|
49
|
+
DSL.new(name, relation: relation, factories: self, &block).call
|
50
|
+
end
|
51
|
+
|
52
|
+
registry[name] = builder
|
53
|
+
end
|
54
|
+
|
55
|
+
def infer_relation(name)
|
56
|
+
::Dry::Core::Inflector.pluralize(name).to_sym
|
57
|
+
end
|
58
|
+
|
59
|
+
def extend_builder(name, parent, &block)
|
60
|
+
DSL.new(name, schema: parent.schema, relation: parent.relation, factories: self, &block).call
|
61
|
+
end
|
62
|
+
|
63
|
+
def [](name, attrs = {})
|
64
|
+
registry[name].persistable.create(attrs)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'dry/struct'
|
2
|
+
require 'dry/core/cache'
|
3
|
+
require 'dry/core/class_builder'
|
4
|
+
|
5
|
+
module ROM::Factory
|
6
|
+
class Struct < Dry::Struct
|
7
|
+
extend Dry::Core::Cache
|
8
|
+
|
9
|
+
def self.define(name, schema)
|
10
|
+
fetch_or_store(schema) do
|
11
|
+
id = Dry::Core::Inflector.classify(Dry::Core::Inflector.singularize(name))
|
12
|
+
|
13
|
+
Dry::Core::ClassBuilder.new(name: "ROM::Factory::Struct[#{id}]", parent: self).call do |klass|
|
14
|
+
schema.each do |attr|
|
15
|
+
klass.attribute attr.name, attr.type
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/rom-factory.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rom/factory/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rom-factory"
|
8
|
+
spec.version = ROM::Factory::VERSION
|
9
|
+
spec.authors = ["Janis Miezitis", "Piotr Solnica"]
|
10
|
+
spec.email = ["janjiss@gmail.com", "piotr.solnica@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{ROM based builder library to make your specs awesome. DSL partially inspired by FactoryGirl.}
|
13
|
+
spec.description = %q{}
|
14
|
+
spec.homepage = "https://github.com/rom-rb/rom-factory"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_dependency "dry-configurable", "~> 0.1"
|
31
|
+
spec.add_dependency "dry-core", "~> 0.2"
|
32
|
+
spec.add_dependency "dry-struct", "~> 0.2"
|
33
|
+
spec.add_dependency "faker", "~> 1.7"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rom-factory
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Janis Miezitis
|
8
|
+
- Piotr Solnica
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-03-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: dry-configurable
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.1'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.1'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: dry-core
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0.2'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.2'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: dry-struct
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.2'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.2'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: faker
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.7'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.7'
|
70
|
+
description: ''
|
71
|
+
email:
|
72
|
+
- janjiss@gmail.com
|
73
|
+
- piotr.solnica@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".rspec"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CHANGELOG.md
|
82
|
+
- CODE_OF_CONDUCT.md
|
83
|
+
- Gemfile
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- lib/rom-factory.rb
|
88
|
+
- lib/rom/factory.rb
|
89
|
+
- lib/rom/factory/attributes/callable.rb
|
90
|
+
- lib/rom/factory/attributes/regular.rb
|
91
|
+
- lib/rom/factory/attributes/sequence.rb
|
92
|
+
- lib/rom/factory/builder.rb
|
93
|
+
- lib/rom/factory/dsl.rb
|
94
|
+
- lib/rom/factory/factories.rb
|
95
|
+
- lib/rom/factory/struct.rb
|
96
|
+
- lib/rom/factory/version.rb
|
97
|
+
- rom-factory.gemspec
|
98
|
+
homepage: https://github.com/rom-rb/rom-factory
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata:
|
102
|
+
allowed_push_host: https://rubygems.org
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.6.9
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: ROM based builder library to make your specs awesome. DSL partially inspired
|
123
|
+
by FactoryGirl.
|
124
|
+
test_files: []
|