rom-factory 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|