active_record_data_loader 0.1.2 → 1.0.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 +15 -3
- data/Appraisals +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +175 -2
- data/gemfiles/rails.gemfile +7 -0
- data/lib/active_record_data_loader/active_record/polymorphic_belongs_to_configuration.rb +9 -1
- data/lib/active_record_data_loader/configuration.rb +12 -2
- data/lib/active_record_data_loader/dsl/polymorphic_association.rb +4 -2
- data/lib/active_record_data_loader/loader.rb +3 -0
- data/lib/active_record_data_loader/version.rb +1 -1
- metadata +3 -3
- data/script/ci_build.sh +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c2971599904ef04249cbada28e924103f7a7f37e0d397e047502063bebd4297
|
4
|
+
data.tar.gz: 4b9d6471bb8c01937b459ca21cd8fcff18d2f13950102a63bebb13aa20584fb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c324c5783427ca9aacb88496e2b76669ade344fdb3d9eb4d640d2579cf9c472eee2a62367478d5211aead5572c155f4beb3ce12c5202503bb80e6f134a565743
|
7
|
+
data.tar.gz: f6f558c48d05973eed5cd9659b5204af780075ee19b8e046ddc1428385e1155a266cdacb052220b1b5354a5e7a50ea04e12c4eefac77002f2fa302bd772ec1e7
|
data/.travis.yml
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
|
+
env:
|
4
|
+
- COVERALLS_PARALLEL=true
|
3
5
|
rvm:
|
4
|
-
- 2.
|
6
|
+
- 2.3.8
|
7
|
+
- 2.4.6
|
8
|
+
- 2.5.5
|
9
|
+
- 2.6.3
|
10
|
+
gemfile:
|
11
|
+
- gemfiles/activerecord_5.gemfile
|
12
|
+
- gemfiles/rails.gemfile
|
13
|
+
- gemfiles/faker.gemfile
|
14
|
+
- gemfiles/ffaker.gemfile
|
5
15
|
services:
|
6
16
|
- postgresql
|
7
|
-
|
17
|
+
notifications:
|
18
|
+
webhooks: https://coveralls.io/webhook
|
19
|
+
before_install: "gem update --system && gem install bundler"
|
8
20
|
before_script:
|
9
21
|
- psql -c 'create database test;' -U postgres
|
10
22
|
- cp config/database.yml.travis config/database.yml
|
11
|
-
script:
|
23
|
+
script: "bundle exec rake"
|
data/Appraisals
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# ActiveRecord Data Loader
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/active_record_data_loader.svg)](https://badge.fury.io/rb/active_record_data_loader)
|
4
3
|
[![Build Status](https://travis-ci.org/abeiderman/active_record_data_loader.svg?branch=master)](https://travis-ci.org/abeiderman/active_record_data_loader)
|
5
4
|
[![Coverage Status](https://coveralls.io/repos/github/abeiderman/active_record_data_loader/badge.svg?branch=master&service=github)](https://coveralls.io/github/abeiderman/active_record_data_loader?branch=master)
|
6
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/338904b3f7e8d19a3cb1/maintainability)](https://codeclimate.com/github/abeiderman/active_record_data_loader/maintainability)
|
@@ -29,7 +28,181 @@ Or install it yourself as:
|
|
29
28
|
|
30
29
|
## Usage
|
31
30
|
|
32
|
-
|
31
|
+
The gem will recognize most commonly used column types and attempt to populate with sensible values by default. You can override this behavior as you will see further below.
|
32
|
+
|
33
|
+
`belongs_to` associations are recognized automatically. However, data is loaded in the order you define, so you want to make sure the parent model is loaded first.
|
34
|
+
|
35
|
+
Polymorphic associations need to be defined explicitly as shown in [Polymorphic associations](#polymorphic-associations).
|
36
|
+
|
37
|
+
### Basic usage
|
38
|
+
|
39
|
+
Let's say you have the following models:
|
40
|
+
```ruby
|
41
|
+
class Customer < ApplicationRecord
|
42
|
+
end
|
43
|
+
|
44
|
+
class Order < ApplicationRecord
|
45
|
+
belongs_to :customer
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
The following code will create 10,000 customers and 100,000 orders, and will associate the orders to those customers evenly:
|
50
|
+
```ruby
|
51
|
+
data_loader = ActiveRecordDataLoader.define do
|
52
|
+
model Customer do |m|
|
53
|
+
m.count 10_000
|
54
|
+
end
|
55
|
+
|
56
|
+
model Order do |m|
|
57
|
+
m.count 100_000
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
data_loader.load_data
|
62
|
+
```
|
63
|
+
|
64
|
+
#### Overriding column values
|
65
|
+
To provide your own values for columns your can provide a lambda or a constant value:
|
66
|
+
```ruby
|
67
|
+
data_loader = ActiveRecordDataLoader.define do
|
68
|
+
model Customer do |m|
|
69
|
+
m.count 10_000
|
70
|
+
m.column :name, -> { %w[Jane John Mary Matt].sample }
|
71
|
+
m.column :country, "USA"
|
72
|
+
m.column :terminated_at, nil
|
73
|
+
end
|
74
|
+
|
75
|
+
...
|
76
|
+
end
|
77
|
+
|
78
|
+
data_loader.load_data
|
79
|
+
```
|
80
|
+
|
81
|
+
### Controlling associations
|
82
|
+
Let's say that you have certain restrictions on orders depending on country. You would want to test data to follow those restrictions which means orders cannot be randomly associated to any customer. You can control that by providing an `eligible_set` on the association.
|
83
|
+
|
84
|
+
In this example, we are creating 25K orders for customers in CAN with a CAD currency, 25K for customers in MEX with a MXN currency, and 50K for those in USA with a USD currency.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
data_loader = ActiveRecordDataLoader.define do
|
88
|
+
model Customer do |m|
|
89
|
+
m.count 10_000
|
90
|
+
m.column :country, -> { %w[CAN MXN USA].sample }
|
91
|
+
end
|
92
|
+
|
93
|
+
model Order do |m|
|
94
|
+
m.count 25_000
|
95
|
+
m.column :currency, "CAD"
|
96
|
+
m.belongs_to :customer, eligible_set: -> { Customer.where(country: "CAN") }
|
97
|
+
end
|
98
|
+
|
99
|
+
model Order do |m|
|
100
|
+
m.count 25_000
|
101
|
+
m.column :currency, "MXN"
|
102
|
+
m.belongs_to :customer, eligible_set: -> { Customer.where(country: "MEX") }
|
103
|
+
end
|
104
|
+
|
105
|
+
model Order do |m|
|
106
|
+
m.count 50_000
|
107
|
+
m.column :currency, "USD"
|
108
|
+
m.belongs_to :customer, eligible_set: -> { Customer.where(country: "USA") }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
data_loader.load_data
|
113
|
+
```
|
114
|
+
|
115
|
+
### Polymorphic associations
|
116
|
+
|
117
|
+
If you have a polymorphic `belongs_to` association, you will need to define that explicitly for it to be populated.
|
118
|
+
|
119
|
+
Let's assume the following models where an order could belong to either a person or a business:
|
120
|
+
```ruby
|
121
|
+
class Person < ApplicationRecord
|
122
|
+
has_many :orders
|
123
|
+
end
|
124
|
+
|
125
|
+
class Business < ApplicationRecord
|
126
|
+
has_many :orders
|
127
|
+
end
|
128
|
+
|
129
|
+
class Order < ApplicationRecord
|
130
|
+
belongs_to :customer, polymorphic: true
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
In order to populate the `customer` association in orders, you would specify them like this:
|
135
|
+
```ruby
|
136
|
+
data_loader = ActiveRecordDataLoader.define do
|
137
|
+
model Person do |m|
|
138
|
+
m.count 5_000
|
139
|
+
end
|
140
|
+
|
141
|
+
model Business do |m|
|
142
|
+
m.count 5_000
|
143
|
+
end
|
144
|
+
|
145
|
+
model Order do |m|
|
146
|
+
m.count 100_000
|
147
|
+
|
148
|
+
m.polymorphic :customer do |c|
|
149
|
+
c.model Person
|
150
|
+
c.model Business
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
data_loader.load_data
|
156
|
+
```
|
157
|
+
|
158
|
+
You can also provide a `weight` to each of the target models if you want to control how they are distributed. If you wanted to have twice as many orders for `Person` than for `Business`, it would look like this:
|
159
|
+
```ruby
|
160
|
+
data_loader = ActiveRecordDataLoader.define do
|
161
|
+
model Person do |m|
|
162
|
+
m.count 5_000
|
163
|
+
end
|
164
|
+
|
165
|
+
model Business do |m|
|
166
|
+
m.count 5_000
|
167
|
+
end
|
168
|
+
|
169
|
+
model Order do |m|
|
170
|
+
m.count 100_000
|
171
|
+
|
172
|
+
m.polymorphic :customer do |c|
|
173
|
+
c.model Person, weight: 2
|
174
|
+
c.model Business, weight: 1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
data_loader.load_data
|
180
|
+
```
|
181
|
+
|
182
|
+
Additionaly, you can also provide an `eligible_set` to control which records to limit the association to:
|
183
|
+
```ruby
|
184
|
+
data_loader = ActiveRecordDataLoader.define do
|
185
|
+
model Person do |m|
|
186
|
+
m.count 5_000
|
187
|
+
end
|
188
|
+
|
189
|
+
model Business do |m|
|
190
|
+
m.count 5_000
|
191
|
+
m.column :country, -> { %w[CAN USA].sample }
|
192
|
+
end
|
193
|
+
|
194
|
+
model Order do |m|
|
195
|
+
m.count 100_000
|
196
|
+
|
197
|
+
m.polymorphic :customer do |c|
|
198
|
+
c.model Person, weight: 2
|
199
|
+
c.model Business, weight: 1, eligible_set: -> { Business.where(country: "USA") }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
data_loader.load_data
|
205
|
+
```
|
33
206
|
|
34
207
|
## Development
|
35
208
|
|
@@ -38,12 +38,20 @@ module ActiveRecordDataLoader
|
|
38
38
|
def possible_values
|
39
39
|
@possible_values ||= begin
|
40
40
|
values = @settings.models.keys.map do |klass|
|
41
|
-
[klass.name, klass.
|
41
|
+
[klass.name, base_query(klass).pluck(klass.primary_key).to_a]
|
42
42
|
end.to_h
|
43
43
|
|
44
44
|
@settings.weighted_models.map { |klass| [klass.name, values[klass.name]] }
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
def base_query(klass)
|
49
|
+
if @settings.queries[klass]&.respond_to?(:call)
|
50
|
+
@settings.queries[klass].call.all
|
51
|
+
else
|
52
|
+
klass.all
|
53
|
+
end
|
54
|
+
end
|
47
55
|
end
|
48
56
|
end
|
49
57
|
end
|
@@ -7,13 +7,23 @@ module ActiveRecordDataLoader
|
|
7
7
|
def initialize(
|
8
8
|
default_batch_size: 100_000,
|
9
9
|
default_row_count: 1,
|
10
|
-
logger:
|
10
|
+
logger: nil,
|
11
11
|
statement_timeout: "2min"
|
12
12
|
)
|
13
13
|
@default_batch_size = default_batch_size
|
14
14
|
@default_row_count = default_row_count
|
15
|
-
@logger = logger
|
15
|
+
@logger = logger || default_logger
|
16
16
|
@statement_timeout = statement_timeout
|
17
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def default_logger
|
22
|
+
if defined?(Rails) && Rails.respond_to?(:logger)
|
23
|
+
Rails.logger
|
24
|
+
else
|
25
|
+
Logger.new(STDOUT, level: :info)
|
26
|
+
end
|
27
|
+
end
|
18
28
|
end
|
19
29
|
end
|
@@ -3,16 +3,18 @@
|
|
3
3
|
module ActiveRecordDataLoader
|
4
4
|
module Dsl
|
5
5
|
class PolymorphicAssociation
|
6
|
-
attr_reader :model_class, :name, :models
|
6
|
+
attr_reader :model_class, :name, :models, :queries
|
7
7
|
|
8
8
|
def initialize(model_class, name)
|
9
9
|
@model_class = model_class
|
10
10
|
@name = name
|
11
11
|
@models = {}
|
12
|
+
@queries = {}
|
12
13
|
end
|
13
14
|
|
14
|
-
def model(klass, weight: 1)
|
15
|
+
def model(klass, weight: 1, eligible_set: nil)
|
15
16
|
@models[klass] = weight.to_i
|
17
|
+
@queries[klass] = eligible_set if eligible_set
|
16
18
|
end
|
17
19
|
|
18
20
|
def weighted_models
|
@@ -39,6 +39,7 @@ module ActiveRecordDataLoader
|
|
39
39
|
batch_count = (total_rows / batch_size.to_f).ceil
|
40
40
|
|
41
41
|
logger.info(
|
42
|
+
"[ActiveRecordDataLoader] "\
|
42
43
|
"Loading #{total_rows} row(s) into '#{strategy.table_name}' via #{strategy.name}. "\
|
43
44
|
"#{batch_size} row(s) per batch, #{batch_count} batch(es)."
|
44
45
|
)
|
@@ -46,6 +47,7 @@ module ActiveRecordDataLoader
|
|
46
47
|
load_in_batches(batch_size, total_rows, batch_count)
|
47
48
|
end
|
48
49
|
logger.info(
|
50
|
+
"[ActiveRecordDataLoader] "\
|
49
51
|
"Completed loading #{total_rows} row(s) into '#{strategy.table_name}' "\
|
50
52
|
"in #{total_time} seconds."
|
51
53
|
)
|
@@ -61,6 +63,7 @@ module ActiveRecordDataLoader
|
|
61
63
|
time = Benchmark.realtime { strategy.load_batch(row_numbers, connection) }
|
62
64
|
|
63
65
|
logger.debug(
|
66
|
+
"[ActiveRecordDataLoader] "\
|
64
67
|
"Completed batch #{i + 1}/#{batch_count}, #{row_numbers.count} row(s) in #{time} seconds"
|
65
68
|
)
|
66
69
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_data_loader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alejandro Beiderman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -194,6 +194,7 @@ files:
|
|
194
194
|
- gemfiles/activerecord_6.gemfile
|
195
195
|
- gemfiles/faker.gemfile
|
196
196
|
- gemfiles/ffaker.gemfile
|
197
|
+
- gemfiles/rails.gemfile
|
197
198
|
- lib/active_record_data_loader.rb
|
198
199
|
- lib/active_record_data_loader/active_record/belongs_to_configuration.rb
|
199
200
|
- lib/active_record_data_loader/active_record/column_configuration.rb
|
@@ -213,7 +214,6 @@ files:
|
|
213
214
|
- lib/active_record_data_loader/loader.rb
|
214
215
|
- lib/active_record_data_loader/version.rb
|
215
216
|
- log/.keep
|
216
|
-
- script/ci_build.sh
|
217
217
|
homepage:
|
218
218
|
licenses:
|
219
219
|
- MIT
|
data/script/ci_build.sh
DELETED