rom-factory 0.4.0 → 0.5.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/.travis.yml +11 -10
- data/.yardopts +7 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +30 -4
- data/README.md +20 -190
- data/benchmarks/basic.rb +70 -0
- data/lib/rom/factory.rb +15 -0
- data/lib/rom/factory/attribute_registry.rb +64 -0
- data/lib/rom/factory/attributes.rb +12 -0
- data/lib/rom/factory/attributes/association.rb +98 -0
- data/lib/rom/factory/attributes/callable.rb +20 -8
- data/lib/rom/factory/attributes/sequence.rb +15 -3
- data/lib/rom/factory/attributes/value.rb +31 -0
- data/lib/rom/factory/builder.rb +26 -37
- data/lib/rom/factory/builder/persistable.rb +51 -0
- data/lib/rom/factory/dsl.rb +81 -18
- data/lib/rom/factory/factories.rb +121 -5
- data/lib/rom/factory/tuple_evaluator.rb +104 -0
- data/lib/rom/factory/version.rb +1 -1
- data/rom-factory.gemspec +4 -3
- metadata +37 -11
- data/lib/rom/factory/attributes/regular.rb +0 -13
- data/lib/rom/factory/struct.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0e9af2bccde24058817960957099aa19a54cda9
|
4
|
+
data.tar.gz: f99643318992b7df49913638a47043086a992519
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f8a9de64072548a7e3597000b27898d4a6041f6e999be10837ef917a56ae0bade2437bc8cd85b260e9bee006e8f7042ceb6792e65e22e602dd356bea54d6c59
|
7
|
+
data.tar.gz: a809541e1f0bb8a36b8c6b7ee7c3f41f0e36c9964bb70aebd072cfc1c78658fb8b4d31e46b912bcfbae5e5a629d57e3d9c8ba4139c6c6fc636efc0198033161e
|
data/.travis.yml
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
sudo: false
|
3
|
+
services:
|
4
|
+
- postgresql
|
5
|
+
- mysql
|
6
|
+
bundler_args: --without tools benchmarks
|
7
|
+
before_script:
|
8
|
+
- psql -c 'create database rom_factory;' -U postgres
|
6
9
|
script: "bundle exec rake spec"
|
7
10
|
after_success:
|
8
|
-
- '[
|
11
|
+
- '[ -d coverage ] && bundle exec codeclimate-test-reporter'
|
9
12
|
rvm:
|
10
|
-
- 2.4
|
11
|
-
- 2.
|
12
|
-
-
|
13
|
-
- rbx-3
|
14
|
-
- jruby-9.1.6.0
|
13
|
+
- 2.3.4
|
14
|
+
- 2.4.1
|
15
|
+
- jruby-9.1.13.0
|
15
16
|
env:
|
16
17
|
global:
|
17
18
|
- COVERAGE='true'
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## 0.5.0 to-be-released
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Updated to rom 4.0 (solnic)
|
6
|
+
* Support for `has_many` and `has_one` associations (solnic)
|
7
|
+
* Support for attributes depending on values from other attributes (solnic)
|
8
|
+
* Support for `rand` inside the generator block (flash-gordon)
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
* Depends on `rom-core` now (solnic)
|
13
|
+
|
14
|
+
[Compare v0.4.0...v0.5.0](https://github.com/rom-rb/rom-factory/compare/v0.4.0...v0.5.0)
|
15
|
+
|
1
16
|
## v0.4.0 2017-03-03
|
2
17
|
|
3
18
|
This is a revamp of the original `rom_factory` gem which adds new features and
|
data/Gemfile
CHANGED
@@ -7,11 +7,37 @@ gem 'simplecov', require: false, platform: :mri
|
|
7
7
|
gem 'codeclimate-test-reporter', require: false, platform: :mri
|
8
8
|
|
9
9
|
gem 'rspec', '~> 3.0'
|
10
|
-
|
11
|
-
|
12
|
-
gem '
|
13
|
-
gem '
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem 'rom-sql', '~> 2.1'
|
13
|
+
gem 'inflecto'
|
14
|
+
gem 'pry-byebug', platforms: :mri
|
15
|
+
gem 'pry', platforms: %i(jruby rbx)
|
16
|
+
gem 'codeclimate-test-reporter', require: false
|
17
|
+
gem 'simplecov', require: false
|
18
|
+
|
19
|
+
if RUBY_ENGINE == 'rbx'
|
20
|
+
gem 'pg', '~> 0.18.0', platforms: :rbx
|
21
|
+
else
|
22
|
+
gem 'pg', platforms: :mri
|
23
|
+
end
|
24
|
+
|
25
|
+
gem 'mysql2', platforms: [:mri, :rbx]
|
26
|
+
gem 'jdbc-postgres', platforms: :jruby
|
27
|
+
gem 'jdbc-mysql', platforms: :jruby
|
28
|
+
gem 'sqlite3', '~> 1.3', platforms: [:mri, :rbx]
|
29
|
+
gem 'jdbc-sqlite3', platforms: :jruby
|
30
|
+
end
|
14
31
|
|
15
32
|
group :tools do
|
16
33
|
gem 'byebug', platform: :mri
|
34
|
+
gem 'redcarpet' # for yard
|
35
|
+
end
|
36
|
+
|
37
|
+
group :benchmarks do
|
38
|
+
gem 'activerecord'
|
39
|
+
gem 'benchmark-ips'
|
40
|
+
gem 'factory_girl'
|
41
|
+
gem 'fabrication'
|
42
|
+
gem 'pg', platforms: :mri
|
17
43
|
end
|
data/README.md
CHANGED
@@ -15,6 +15,10 @@
|
|
15
15
|
|
16
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
17
|
|
18
|
+
More information:
|
19
|
+
|
20
|
+
- [API docs](http://rubydoc.info/gems/rom-factory)
|
21
|
+
|
18
22
|
## Installation
|
19
23
|
|
20
24
|
Add this line to your application's Gemfile:
|
@@ -31,201 +35,27 @@ Or install it yourself as:
|
|
31
35
|
|
32
36
|
$ gem install rom-factory
|
33
37
|
|
34
|
-
|
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:
|
38
|
+
### Performance
|
162
39
|
|
40
|
+
Seems like this thing is a bit faster than other popular factory gems:
|
163
41
|
|
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
42
|
```
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
43
|
+
Warming up --------------------------------------
|
44
|
+
rom-factory 1.000 i/100ms
|
45
|
+
factory_girl 1.000 i/100ms
|
46
|
+
fabrication 1.000 i/100ms
|
47
|
+
Calculating -------------------------------------
|
48
|
+
rom-factory 1.550 (± 0.0%) i/s - 8.000 in 5.166227s
|
49
|
+
factory_girl 0.982 (± 0.0%) i/s - 5.000 in 5.098193s
|
50
|
+
fabrication 1.011 (± 0.0%) i/s - 6.000 in 5.942209s
|
51
|
+
|
52
|
+
Comparison:
|
53
|
+
rom-factory: 1.5 i/s
|
54
|
+
fabrication: 1.0 i/s - 1.53x slower
|
55
|
+
factory_girl: 1.0 i/s - 1.58x slower
|
194
56
|
```
|
195
57
|
|
196
|
-
|
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
|
-
```
|
58
|
+
> See [benchmarks/basic.rb](https://github.com/rom-rb/rom-factory/blob/master/benchmarks/basic.rb)
|
229
59
|
|
230
60
|
## Credits
|
231
61
|
|
data/benchmarks/basic.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rom-factory'
|
2
|
+
require 'rom-core'
|
3
|
+
require 'active_record'
|
4
|
+
require 'factory_girl'
|
5
|
+
require 'fabrication'
|
6
|
+
require 'benchmark/ips'
|
7
|
+
|
8
|
+
DATABASE_URL = 'postgres://localhost/rom_factory_bench'.freeze
|
9
|
+
|
10
|
+
rom = ROM.container(:sql, DATABASE_URL) do |conf|
|
11
|
+
conf.default.connection.create_table?(:users) do
|
12
|
+
primary_key :id
|
13
|
+
column :last_name, String, null: false
|
14
|
+
column :first_name, String, null: false
|
15
|
+
column :admin, TrueClass
|
16
|
+
end
|
17
|
+
|
18
|
+
conf.relation(:users) do
|
19
|
+
schema(infer: true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
factory = ROM::Factory.configure { |c| c.rom = rom }
|
24
|
+
|
25
|
+
factory.define(:user) do |f|
|
26
|
+
f.first_name "John"
|
27
|
+
f.last_name "Doe"
|
28
|
+
f.admin false
|
29
|
+
end
|
30
|
+
|
31
|
+
class User < ActiveRecord::Base
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveRecord::Base.establish_connection(DATABASE_URL)
|
35
|
+
|
36
|
+
FactoryGirl.define do
|
37
|
+
factory(:user) do
|
38
|
+
first_name "John"
|
39
|
+
last_name "Doe"
|
40
|
+
admin false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Fabricator(:user) do
|
45
|
+
first_name "John"
|
46
|
+
last_name "Doe"
|
47
|
+
admin false
|
48
|
+
end
|
49
|
+
|
50
|
+
Benchmark.ips do |x|
|
51
|
+
x.report('rom-factory persisted struct') do
|
52
|
+
1000.times do
|
53
|
+
factory[:user]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
x.report('factory_girl') do
|
58
|
+
1000.times do
|
59
|
+
FactoryGirl.create(:user)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
x.report('fabrication') do
|
64
|
+
1000.times do
|
65
|
+
Fabricate(:user)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
x.compare!
|
70
|
+
end
|
data/lib/rom/factory.rb
CHANGED
@@ -2,9 +2,24 @@ require 'dry/core/class_builder'
|
|
2
2
|
require 'rom/factory/factories'
|
3
3
|
|
4
4
|
module ROM
|
5
|
+
# Main ROM::Factory API
|
6
|
+
#
|
7
|
+
# @api public
|
5
8
|
module Factory
|
6
9
|
DEFAULT_NAME = 'Factories'.freeze
|
7
10
|
|
11
|
+
# Configure a new factory
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# MyFactory = ROM::Factory.configure do |config|
|
15
|
+
# config.rom = my_rom_container
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @param [Symbol] name An optional factory class name
|
19
|
+
#
|
20
|
+
# @return [Class]
|
21
|
+
#
|
22
|
+
# @api public
|
8
23
|
def self.configure(name = DEFAULT_NAME, &block)
|
9
24
|
Dry::Core::ClassBuilder.new(name: name, parent: Factories).call do |klass|
|
10
25
|
klass.configure(&block)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Factory
|
5
|
+
# @api private
|
6
|
+
class AttributeRegistry
|
7
|
+
include Enumerable
|
8
|
+
include TSort
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
attr_reader :elements
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def initialize(elements = [])
|
15
|
+
@elements = elements
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def each(&block)
|
20
|
+
elements.each(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def [](name)
|
25
|
+
detect { |e| e.name.equal?(name) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
def <<(element)
|
30
|
+
existing = self[element.name]
|
31
|
+
elements.delete(existing) if existing
|
32
|
+
elements << element
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def dup
|
38
|
+
self.class.new(elements.dup)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def values
|
43
|
+
self.class.new(elements.select(&:value?))
|
44
|
+
end
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def associations
|
48
|
+
self.class.new(elements.select { |e| e.kind_of?(Attributes::Association::Core) })
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
def tsort_each_node(&block)
|
55
|
+
each(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
def tsort_each_child(attr, &block)
|
60
|
+
attr.dependency_names.map { |name| self[name] }.compact.each(&block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|