active_record_data_loader 0.1.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +33 -7
  3. data/.travis.yml +16 -3
  4. data/Appraisals +2 -2
  5. data/CHANGELOG.md +24 -0
  6. data/Gemfile.lock +56 -46
  7. data/README.md +182 -4
  8. data/Rakefile +2 -0
  9. data/active_record_data_loader.gemspec +5 -3
  10. data/config/database.yml +9 -0
  11. data/config/database.yml.travis +5 -0
  12. data/docker-compose.yml +18 -0
  13. data/gemfiles/activerecord_6.gemfile +1 -1
  14. data/gemfiles/rails.gemfile +7 -0
  15. data/lib/active_record_data_loader.rb +23 -14
  16. data/lib/active_record_data_loader/active_record/belongs_to_configuration.rb +13 -4
  17. data/lib/active_record_data_loader/active_record/column_configuration.rb +14 -4
  18. data/lib/active_record_data_loader/active_record/datetime_value_generator.rb +21 -0
  19. data/lib/active_record_data_loader/active_record/enum_value_generator.rb +25 -3
  20. data/lib/active_record_data_loader/active_record/integer_value_generator.rb +1 -1
  21. data/lib/active_record_data_loader/active_record/model_data_generator.rb +20 -4
  22. data/lib/active_record_data_loader/active_record/per_row_value_cache.rb +33 -0
  23. data/lib/active_record_data_loader/active_record/polymorphic_belongs_to_configuration.rb +9 -1
  24. data/lib/active_record_data_loader/active_record/text_value_generator.rb +1 -1
  25. data/lib/active_record_data_loader/bulk_insert_strategy.rb +1 -6
  26. data/lib/active_record_data_loader/configuration.rb +17 -3
  27. data/lib/active_record_data_loader/copy_strategy.rb +11 -14
  28. data/lib/active_record_data_loader/dsl/belongs_to_association.rb +15 -0
  29. data/lib/active_record_data_loader/dsl/model.rb +6 -1
  30. data/lib/active_record_data_loader/dsl/polymorphic_association.rb +4 -2
  31. data/lib/active_record_data_loader/loader.rb +44 -12
  32. data/lib/active_record_data_loader/version.rb +1 -1
  33. metadata +46 -13
  34. data/script/ci_build.sh +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbc38165d9ffa4debdc127641a9e79c4fd3eb382dd9271c7e0eed849992c8c72
4
- data.tar.gz: d2703bbd36cdbcae163dbf19082704e39dc717499689d9754bc570d846895ada
3
+ metadata.gz: 33d3769ad2f8c008cf51e75a62b43ba3333dce8e7e45387e01ab3470f2a5bf6c
4
+ data.tar.gz: 6f2b812d1e3837d05ff565c050cd6c80110929136601fb95436996b14de4e809
5
5
  SHA512:
6
- metadata.gz: a999728b9926d7e87cb93fbf3329902becc4dd0b51139f38b473820010b93987ecb5e87be0be214111f28bd6c64685af6d2b09d61cc35cf4d3c41dd1fc88f026
7
- data.tar.gz: 1b6bcbf2edd88700ce91083ce7da1958ef314f65f5c57932cd978915e36603788696eb3f1cc5894f36dd6870d6d945ca9e72068e2ac05444f5992df099c4760a
6
+ metadata.gz: 8813fa219e7a710f147b79bb3a81826a5fe23ed7e3bef21a95fca310b3c8f3b09c7fb2c89d0cb3a65c825d01941eca8e394ee42900e13941d050e05fcb2c76d5
7
+ data.tar.gz: 43392902671095ffcf3d9f33984b7f77de3a9b2fb95bd99ee65378d046e2d509d34f8b92220de04642e6e33d816926215581caa58d02b0d30ac02011f1956776
data/.rubocop.yml CHANGED
@@ -1,5 +1,20 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.3
2
+ TargetRubyVersion: 2.5
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 110
8
+ Exclude: ["*.gemspec"]
9
+
10
+ Layout/SpaceAroundMethodCallOperator:
11
+ Enabled: true
12
+
13
+ Lint/RaiseException:
14
+ Enabled: true
15
+
16
+ Lint/StructNewOverride:
17
+ Enabled: true
3
18
 
4
19
  Lint/UnusedMethodArgument:
5
20
  AllowUnusedKeywordArguments: true
@@ -11,20 +26,31 @@ Metrics/AbcSize:
11
26
  Metrics/BlockLength:
12
27
  Exclude: ["spec/**/*", "*.gemspec"]
13
28
 
14
- Metrics/LineLength:
15
- Max: 110
16
- Exclude: ["*.gemspec"]
17
-
18
29
  Metrics/MethodLength:
19
30
  Max: 15
20
31
  Exclude: ["spec/**/*"]
21
32
 
22
- Style/FrozenStringLiteralComment:
23
- Exclude: ["gemfiles/*"]
33
+ Style/CaseLikeIf:
34
+ Enabled: false
24
35
 
25
36
  Style/Documentation:
26
37
  Enabled: false
27
38
 
39
+ Style/ExponentialNotation:
40
+ Enabled: true
41
+
42
+ Style/FrozenStringLiteralComment:
43
+ Exclude: ["gemfiles/*"]
44
+
45
+ Style/HashEachMethods:
46
+ Enabled: true
47
+
48
+ Style/HashTransformKeys:
49
+ Enabled: true
50
+
51
+ Style/HashTransformValues:
52
+ Enabled: true
53
+
28
54
  Style/StringLiterals:
29
55
  EnforcedStyle: double_quotes
30
56
 
data/.travis.yml CHANGED
@@ -1,11 +1,24 @@
1
1
  sudo: false
2
2
  language: ruby
3
+ env:
4
+ - COVERALLS_PARALLEL=true
3
5
  rvm:
4
- - 2.4.3
6
+ - 2.5.9
7
+ - 2.6.7
8
+ - 2.7.3
9
+ gemfile:
10
+ - gemfiles/activerecord_5.gemfile
11
+ - gemfiles/rails.gemfile
12
+ - gemfiles/faker.gemfile
13
+ - gemfiles/ffaker.gemfile
5
14
  services:
6
15
  - postgresql
7
- before_install: gem install bundler -v 1.16.1
16
+ - mysql
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
22
+ - mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
10
23
  - cp config/database.yml.travis config/database.yml
11
- script: ./script/ci_build.sh
24
+ script: "bundle exec rake"
data/Appraisals CHANGED
@@ -4,8 +4,8 @@ appraise "activerecord-5" do
4
4
  gem "activerecord", "~> 5.0"
5
5
  end
6
6
 
7
- appraise "activerecord-6" do
8
- gem "activerecord", "6.0.0.rc1"
7
+ appraise "rails" do
8
+ gem "rails"
9
9
  end
10
10
 
11
11
  appraise "faker" do
data/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Change log
2
+
3
+ ## [v1.0.2] - 2019-07-05
4
+
5
+ [Diff](https://github.com/abeiderman/active_record_data_loader/compare/v1.0.1...v1.0.2)
6
+
7
+ ### Enhancements:
8
+ * Add support for MySQL enums
9
+ * Accept a connection factory lambda as part of the configuration
10
+
11
+ ## [v1.0.1] - 2019-06-16
12
+
13
+ [Diff](https://github.com/abeiderman/active_record_data_loader/compare/v1.0.0...v1.0.1)
14
+
15
+ ### Enhancements:
16
+ * Generate values for datetime column types. This also fixes the fact that `created_at` and `updated_at` were not being populated by default.
17
+
18
+ ## [v1.0.0] - 2019-06-15
19
+
20
+ Initial stable release
21
+
22
+ [v1.0.0]: https://github.com/abeiderman/active_record_data_loader/releases/tag/v1.0.0
23
+ [v1.0.1]: https://github.com/abeiderman/active_record_data_loader/releases/tag/v1.0.1
24
+ [v1.0.2]: https://github.com/abeiderman/active_record_data_loader/releases/tag/v1.0.2
data/Gemfile.lock CHANGED
@@ -1,19 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_record_data_loader (0.1.1)
5
- activerecord (>= 4.0)
4
+ active_record_data_loader (1.1.0)
5
+ activerecord (>= 5.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activemodel (5.2.3)
11
- activesupport (= 5.2.3)
12
- activerecord (5.2.3)
13
- activemodel (= 5.2.3)
14
- activesupport (= 5.2.3)
10
+ activemodel (5.2.4.5)
11
+ activesupport (= 5.2.4.5)
12
+ activerecord (5.2.4.5)
13
+ activemodel (= 5.2.4.5)
14
+ activesupport (= 5.2.4.5)
15
15
  arel (>= 9.0)
16
- activesupport (5.2.3)
16
+ activesupport (5.2.4.5)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 0.7, < 2)
19
19
  minitest (~> 5.1)
@@ -23,9 +23,9 @@ GEM
23
23
  rake
24
24
  thor (>= 0.14.0)
25
25
  arel (9.0.0)
26
- ast (2.4.0)
26
+ ast (2.4.1)
27
27
  coderay (1.1.2)
28
- concurrent-ruby (1.1.5)
28
+ concurrent-ruby (1.1.8)
29
29
  coveralls (0.8.23)
30
30
  json (>= 1.8, < 3)
31
31
  simplecov (~> 0.16.1)
@@ -33,45 +33,51 @@ GEM
33
33
  thor (>= 0.19.4, < 2.0)
34
34
  tins (~> 1.6)
35
35
  diff-lcs (1.3)
36
- docile (1.3.1)
37
- i18n (1.6.0)
36
+ docile (1.3.2)
37
+ i18n (1.8.10)
38
38
  concurrent-ruby (~> 1.0)
39
- jaro_winkler (1.5.2)
40
- json (2.2.0)
41
- method_source (0.9.2)
42
- minitest (5.11.3)
43
- parallel (1.17.0)
44
- parser (2.6.3.0)
45
- ast (~> 2.4.0)
46
- pg (1.1.4)
47
- pry (0.12.2)
48
- coderay (~> 1.1.0)
49
- method_source (~> 0.9.0)
39
+ json (2.3.1)
40
+ method_source (1.0.0)
41
+ minitest (5.14.4)
42
+ mysql2 (0.5.3)
43
+ parallel (1.20.1)
44
+ parser (2.7.2.0)
45
+ ast (~> 2.4.1)
46
+ pg (1.2.3)
47
+ pry (0.13.1)
48
+ coderay (~> 1.1)
49
+ method_source (~> 1.0)
50
50
  rainbow (3.0.0)
51
- rake (12.3.2)
52
- rspec (3.8.0)
53
- rspec-core (~> 3.8.0)
54
- rspec-expectations (~> 3.8.0)
55
- rspec-mocks (~> 3.8.0)
56
- rspec-collection_matchers (1.1.3)
51
+ rake (13.0.1)
52
+ regexp_parser (2.0.0)
53
+ rexml (3.2.5)
54
+ rspec (3.9.0)
55
+ rspec-core (~> 3.9.0)
56
+ rspec-expectations (~> 3.9.0)
57
+ rspec-mocks (~> 3.9.0)
58
+ rspec-collection_matchers (1.2.0)
57
59
  rspec-expectations (>= 2.99.0.beta1)
58
- rspec-core (3.8.0)
59
- rspec-support (~> 3.8.0)
60
- rspec-expectations (3.8.3)
60
+ rspec-core (3.9.2)
61
+ rspec-support (~> 3.9.3)
62
+ rspec-expectations (3.9.1)
61
63
  diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.8.0)
63
- rspec-mocks (3.8.0)
64
+ rspec-support (~> 3.9.0)
65
+ rspec-mocks (3.9.1)
64
66
  diff-lcs (>= 1.2.0, < 2.0)
65
- rspec-support (~> 3.8.0)
66
- rspec-support (3.8.0)
67
- rubocop (0.68.1)
68
- jaro_winkler (~> 1.5.1)
67
+ rspec-support (~> 3.9.0)
68
+ rspec-support (3.9.3)
69
+ rubocop (1.6.1)
69
70
  parallel (~> 1.10)
70
- parser (>= 2.5, != 2.5.1.1)
71
+ parser (>= 2.7.1.5)
71
72
  rainbow (>= 2.2.2, < 4.0)
73
+ regexp_parser (>= 1.8, < 3.0)
74
+ rexml
75
+ rubocop-ast (>= 1.2.0, < 2.0)
72
76
  ruby-progressbar (~> 1.7)
73
- unicode-display_width (>= 1.4.0, < 1.6)
74
- ruby-progressbar (1.10.0)
77
+ unicode-display_width (>= 1.4.0, < 2.0)
78
+ rubocop-ast (1.3.0)
79
+ parser (>= 2.7.1.5)
80
+ ruby-progressbar (1.10.1)
75
81
  simplecov (0.16.1)
76
82
  docile (~> 1.1)
77
83
  json (>= 1.8, < 3)
@@ -82,26 +88,30 @@ GEM
82
88
  tins (~> 1.0)
83
89
  thor (0.20.3)
84
90
  thread_safe (0.3.6)
85
- tins (1.20.3)
86
- tzinfo (1.2.5)
91
+ timecop (0.9.1)
92
+ tins (1.21.0)
93
+ tzinfo (1.2.9)
87
94
  thread_safe (~> 0.1)
88
- unicode-display_width (1.5.0)
95
+ unicode-display_width (1.7.0)
89
96
 
90
97
  PLATFORMS
91
98
  ruby
99
+ x86_64-darwin-19
92
100
 
93
101
  DEPENDENCIES
94
102
  active_record_data_loader!
95
103
  appraisal
96
104
  bundler (>= 1.16)
97
105
  coveralls
106
+ mysql2
98
107
  pg
99
108
  pry
100
- rake (~> 12.0)
109
+ rake (~> 13.0)
101
110
  rspec (~> 3.0)
102
111
  rspec-collection_matchers
103
112
  rubocop
104
113
  sqlite3
114
+ timecop
105
115
 
106
116
  BUNDLED WITH
107
- 2.0.1
117
+ 2.2.1
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # ActiveRecord Data Loader
1
+ # active_record_data_loader
2
2
 
3
3
  [![Build Status](https://travis-ci.org/abeiderman/active_record_data_loader.svg?branch=master)](https://travis-ci.org/abeiderman/active_record_data_loader)
4
- [![Coverage Status](https://coveralls.io/repos/github/abeiderman/active_record_data_loader/badge.svg?branch=master)](https://coveralls.io/github/abeiderman/active_record_data_loader?branch=master)
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)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/338904b3f7e8d19a3cb1/maintainability)](https://codeclimate.com/github/abeiderman/active_record_data_loader/maintainability)
6
6
 
7
7
  Efficiently bulk load data for your ActiveRecord models with a simple DSL.
@@ -10,6 +10,10 @@ Efficiently bulk load data for your ActiveRecord models with a simple DSL.
10
10
 
11
11
  Load, performance, and stress tests often require setting up a realistic amount of data in your database. This gem is intended to help organize that data load and make it more maintainable than having a collection of SQL scripts.
12
12
 
13
+ #### How is this different from using _factory_bot_?
14
+
15
+ This gem is not a replacement for [factory_bot](https://github.com/thoughtbot/factory_bot). It solves a different use case. While _factory_bot_ is great for organizing test data and reducing duplication in your functional tests, _active_record_data_loader_ is focused around bulk loading data for performance tests. The purpose of _active_record_data_loader_ is loading large amounts of data as efficiently as possible while providing a DSL that helps with maintainability.
16
+
13
17
  ## Installation
14
18
 
15
19
  Add this line to your application's Gemfile:
@@ -28,7 +32,181 @@ Or install it yourself as:
28
32
 
29
33
  ## Usage
30
34
 
31
- TODO: Write usage instructions here
35
+ 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.
36
+
37
+ `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.
38
+
39
+ Polymorphic associations need to be defined explicitly as shown in [Polymorphic associations](#polymorphic-associations).
40
+
41
+ ### Basic usage
42
+
43
+ Let's say you have the following models:
44
+ ```ruby
45
+ class Customer < ApplicationRecord
46
+ end
47
+
48
+ class Order < ApplicationRecord
49
+ belongs_to :customer
50
+ end
51
+ ```
52
+
53
+ The following code will create 10,000 customers and 100,000 orders, and will associate the orders to those customers evenly:
54
+ ```ruby
55
+ data_loader = ActiveRecordDataLoader.define do
56
+ model Customer do |m|
57
+ m.count 10_000
58
+ end
59
+
60
+ model Order do |m|
61
+ m.count 100_000
62
+ end
63
+ end
64
+
65
+ data_loader.load_data
66
+ ```
67
+
68
+ #### Overriding column values
69
+ To provide your own values for columns your can provide a lambda or a constant value:
70
+ ```ruby
71
+ data_loader = ActiveRecordDataLoader.define do
72
+ model Customer do |m|
73
+ m.count 10_000
74
+ m.column :name, -> { %w[Jane John Mary Matt].sample }
75
+ m.column :country, "USA"
76
+ m.column :terminated_at, nil
77
+ end
78
+
79
+ ...
80
+ end
81
+
82
+ data_loader.load_data
83
+ ```
84
+
85
+ ### Controlling associations
86
+ 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.
87
+
88
+ 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.
89
+
90
+ ```ruby
91
+ data_loader = ActiveRecordDataLoader.define do
92
+ model Customer do |m|
93
+ m.count 10_000
94
+ m.column :country, -> { %w[CAN MXN USA].sample }
95
+ end
96
+
97
+ model Order do |m|
98
+ m.count 25_000
99
+ m.column :currency, "CAD"
100
+ m.belongs_to :customer, eligible_set: -> { Customer.where(country: "CAN") }
101
+ end
102
+
103
+ model Order do |m|
104
+ m.count 25_000
105
+ m.column :currency, "MXN"
106
+ m.belongs_to :customer, eligible_set: -> { Customer.where(country: "MEX") }
107
+ end
108
+
109
+ model Order do |m|
110
+ m.count 50_000
111
+ m.column :currency, "USD"
112
+ m.belongs_to :customer, eligible_set: -> { Customer.where(country: "USA") }
113
+ end
114
+ end
115
+
116
+ data_loader.load_data
117
+ ```
118
+
119
+ ### Polymorphic associations
120
+
121
+ If you have a polymorphic `belongs_to` association, you will need to define that explicitly for it to be populated.
122
+
123
+ Let's assume the following models where an order could belong to either a person or a business:
124
+ ```ruby
125
+ class Person < ApplicationRecord
126
+ has_many :orders
127
+ end
128
+
129
+ class Business < ApplicationRecord
130
+ has_many :orders
131
+ end
132
+
133
+ class Order < ApplicationRecord
134
+ belongs_to :customer, polymorphic: true
135
+ end
136
+ ```
137
+
138
+ In order to populate the `customer` association in orders, you would specify them like this:
139
+ ```ruby
140
+ data_loader = ActiveRecordDataLoader.define do
141
+ model Person do |m|
142
+ m.count 5_000
143
+ end
144
+
145
+ model Business do |m|
146
+ m.count 5_000
147
+ end
148
+
149
+ model Order do |m|
150
+ m.count 100_000
151
+
152
+ m.polymorphic :customer do |c|
153
+ c.model Person
154
+ c.model Business
155
+ end
156
+ end
157
+ end
158
+
159
+ data_loader.load_data
160
+ ```
161
+
162
+ 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:
163
+ ```ruby
164
+ data_loader = ActiveRecordDataLoader.define do
165
+ model Person do |m|
166
+ m.count 5_000
167
+ end
168
+
169
+ model Business do |m|
170
+ m.count 5_000
171
+ end
172
+
173
+ model Order do |m|
174
+ m.count 100_000
175
+
176
+ m.polymorphic :customer do |c|
177
+ c.model Person, weight: 2
178
+ c.model Business, weight: 1
179
+ end
180
+ end
181
+ end
182
+
183
+ data_loader.load_data
184
+ ```
185
+
186
+ Additionaly, you can also provide an `eligible_set` to control which records to limit the association to:
187
+ ```ruby
188
+ data_loader = ActiveRecordDataLoader.define do
189
+ model Person do |m|
190
+ m.count 5_000
191
+ end
192
+
193
+ model Business do |m|
194
+ m.count 5_000
195
+ m.column :country, -> { %w[CAN USA].sample }
196
+ end
197
+
198
+ model Order do |m|
199
+ m.count 100_000
200
+
201
+ m.polymorphic :customer do |c|
202
+ c.model Person, weight: 2
203
+ c.model Business, weight: 1, eligible_set: -> { Business.where(country: "USA") }
204
+ end
205
+ end
206
+ end
207
+
208
+ data_loader.load_data
209
+ ```
32
210
 
33
211
  ## Development
34
212
 
@@ -46,4 +224,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
46
224
 
47
225
  ## Code of Conduct
48
226
 
49
- Everyone interacting in the ActiveRecord Data Loader project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/abeiderman/active_record_data_loader/blob/master/CODE_OF_CONDUCT.md).
227
+ Everyone interacting in the _active_record_data_loader_ project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/abeiderman/active_record_data_loader/blob/master/CODE_OF_CONDUCT.md).