seed-do 3.1.0 → 4.0.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/README.md +40 -0
- data/lib/seed-do/active_record_extension.rb +14 -15
- data/lib/seed-do/block_hash.rb +1 -1
- data/lib/seed-do/bulk_seeder.rb +66 -0
- data/lib/seed-do/capistrano.rb +2 -2
- data/lib/seed-do/capistrano3.rb +1 -1
- data/lib/seed-do/railtie.rb +1 -1
- data/lib/seed-do/runner.rb +55 -38
- data/lib/seed-do/seeder.rb +56 -58
- data/lib/seed-do/version.rb +1 -1
- data/lib/seed-do/writer.rb +49 -47
- data/lib/seed-do.rb +16 -11
- data/lib/tasks/seed_do.rake +4 -8
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5486ba5dbc636b111be4e69d838b7f317b233ea9cf4b516d611fdf5070f0780
|
|
4
|
+
data.tar.gz: a04c1e585a72163d13184a317aecf059eb4c21f24d9b3e51fd208debf452a574
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5d0cbf12b3790a33f0c362967c4a7b20a85ef7cdb3e9a0c15b47111d0a4fab0ae5912728c1fbab4be32697669f4ed672d221badc00ea5abca06999117e3a4fb
|
|
7
|
+
data.tar.gz: 0011f72cf7c27fcfef5d8dad96ca74525582eaf9ddc01e66dd0b1b575e255d28c72c49db6b81f0523704991ab12545d7a4ebfc75c83fe46ef8ede8a71448d423
|
data/README.md
CHANGED
|
@@ -142,6 +142,30 @@ Seed files can be run automatically using `rake db:seed_do`. There are two optio
|
|
|
142
142
|
|
|
143
143
|
You can also do a similar thing in your code by calling `SeedDo.seed(fixture_paths, filter)`.
|
|
144
144
|
|
|
145
|
+
## Bulk upsert
|
|
146
|
+
|
|
147
|
+
If you load a large amount of seed data, you can enable bulk upsert mode:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
SeedDo.seed(bulk: true)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
In bulk mode, SeedDo buffers seed data for each file and writes it with `upsert_all`, which can significantly reduce the number of queries.
|
|
154
|
+
|
|
155
|
+
You can also control the batch size per `upsert_all` call. The default is `1000`.
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
SeedDo.seed(bulk: { batch_size: 100 })
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Requirements and caveats
|
|
162
|
+
|
|
163
|
+
- Bulk mode relies on Rails `upsert_all`
|
|
164
|
+
- The seed constraints are passed to `unique_by`, so the related columns must have a unique index
|
|
165
|
+
- Bulk mode is supported only on databases that support conflict targets, such as PostgreSQL and SQLite
|
|
166
|
+
- Bulk mode is not available on MySQL
|
|
167
|
+
- `SeedDo.seed` and `SeedDo.seed(bulk: true)` may not always produce the same record order, so verify the behavior carefully before enabling it in an existing production environment
|
|
168
|
+
|
|
145
169
|
## Disable output
|
|
146
170
|
|
|
147
171
|
To disable output from Seed Do, set `SeedDo.quiet = true`.
|
|
@@ -200,6 +224,22 @@ DB=postgresql bundle exec rspec
|
|
|
200
224
|
|
|
201
225
|
The connection paramaters for each of these are specified in spec/connections/, which you can edit if necessary (for example to change the username/password).
|
|
202
226
|
|
|
227
|
+
### Dev Container
|
|
228
|
+
|
|
229
|
+
This repository includes a Dev Container setup for running the test suite locally with Ruby, SQLite, MySQL, and PostgreSQL ready to use.
|
|
230
|
+
|
|
231
|
+
1. Open the repository in a Dev Container.
|
|
232
|
+
2. Wait for `bundle install` to finish.
|
|
233
|
+
3. Run the specs you want:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
bundle exec rspec
|
|
237
|
+
DB=mysql2 bundle exec rspec
|
|
238
|
+
DB=postgresql bundle exec rspec
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The Dev Container config also sets `MYSQL_HOST=mysql` and `POSTGRES_HOST=postgres` automatically, so the database-backed specs work without editing local machine settings.
|
|
242
|
+
|
|
203
243
|
## Original Author
|
|
204
244
|
|
|
205
245
|
[Michael Bleigh](http://www.mbleigh.com/) is the original author
|
|
@@ -29,7 +29,7 @@ module SeedDo
|
|
|
29
29
|
# { :x => 5, :y => 9, :name => "Office" }
|
|
30
30
|
# )
|
|
31
31
|
def seed(*args, &block)
|
|
32
|
-
SeedDo
|
|
32
|
+
SeedDo.current_seeder.seed(self, *parse_seed_do_args(args, block))
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Has the same syntax as {#seed}, but if a record already exists with the same values for
|
|
@@ -40,26 +40,25 @@ module SeedDo
|
|
|
40
40
|
# Person.seed(:id, :id => 1, :name => "Bob") # => Name changed
|
|
41
41
|
# Person.seed_once(:id, :id => 1, :name => "Harry") # => Name *not* changed
|
|
42
42
|
def seed_once(*args, &block)
|
|
43
|
-
|
|
44
|
-
SeedDo::Seeder.new(self, constraints, data, :insert_only => true).seed
|
|
43
|
+
SeedDo.current_seeder.seed_once(self, *parse_seed_do_args(args, block))
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
private
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else
|
|
56
|
-
# Partition the args, assuming the first hash is the start of the data
|
|
57
|
-
args.partition { |arg| !arg.is_a?(Hash) }
|
|
58
|
-
end
|
|
48
|
+
def parse_seed_do_args(args, block)
|
|
49
|
+
if block.nil?
|
|
50
|
+
if args.last.is_a?(Array)
|
|
51
|
+
# Last arg is an array of data, so assume the rest of the args are constraints
|
|
52
|
+
data = args.pop
|
|
53
|
+
[args, data]
|
|
59
54
|
else
|
|
60
|
-
#
|
|
61
|
-
|
|
55
|
+
# Partition the args, assuming the first hash is the start of the data
|
|
56
|
+
args.partition { |arg| !arg.is_a?(Hash) }
|
|
62
57
|
end
|
|
58
|
+
else
|
|
59
|
+
# We have a block, so assume the args are all constraints
|
|
60
|
+
[args, [SeedDo::BlockHash.new(block).to_hash]]
|
|
63
61
|
end
|
|
62
|
+
end
|
|
64
63
|
end
|
|
65
64
|
end
|
data/lib/seed-do/block_hash.rb
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module SeedDo
|
|
2
|
+
# Buffers seeds during a file run and flushes them in bulk.
|
|
3
|
+
class BulkSeeder
|
|
4
|
+
attr_reader :buffer
|
|
5
|
+
|
|
6
|
+
def initialize(batch_size: 1000)
|
|
7
|
+
@batch_size = batch_size
|
|
8
|
+
@buffer = nil
|
|
9
|
+
|
|
10
|
+
validate_support!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def seed(model, constraints, data)
|
|
14
|
+
puts " - #{model} #{data.inspect}" unless SeedDo.quiet
|
|
15
|
+
buffer[:seed] << { model: model, constraints: constraints, data: data }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def seed_once(model, constraints, data)
|
|
19
|
+
puts " - #{model} #{data.inspect}" unless SeedDo.quiet
|
|
20
|
+
buffer[:seed_once] << { model: model, constraints: constraints, data: data }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def with_seed_file
|
|
24
|
+
@buffer = { seed: [], seed_once: [] }
|
|
25
|
+
yield
|
|
26
|
+
process_buffer
|
|
27
|
+
ensure
|
|
28
|
+
@buffer = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def validate_support!
|
|
34
|
+
return if ActiveRecord::Base.connection.supports_insert_conflict_target?
|
|
35
|
+
|
|
36
|
+
raise ArgumentError,
|
|
37
|
+
"Bulk mode is not supported for #{ActiveRecord::Base.connection.adapter_name}. " \
|
|
38
|
+
'The database does not support upsert operations with conflict targets. ' \
|
|
39
|
+
'Please use SeedDo.seed without the bulk option.'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def process_buffer
|
|
43
|
+
return unless buffer
|
|
44
|
+
|
|
45
|
+
buffer.each do |type, operations|
|
|
46
|
+
next if operations.empty?
|
|
47
|
+
|
|
48
|
+
operations.chunk { |operation| [operation[:model], operation[:constraints]] }
|
|
49
|
+
.each do |(model, constraints), chunk|
|
|
50
|
+
flush_chunk(model, constraints, type, chunk)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def flush_chunk(model, constraints, type, chunk)
|
|
56
|
+
all_data = chunk.flat_map { |operation| operation[:data] }
|
|
57
|
+
|
|
58
|
+
options = { unique_by: constraints }
|
|
59
|
+
options[:on_duplicate] = :skip if type == :seed_once
|
|
60
|
+
|
|
61
|
+
all_data.each_slice(@batch_size) do |batch|
|
|
62
|
+
model.upsert_all(batch, **options)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/seed-do/capistrano.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Capistrano::Configuration.instance.load do
|
|
2
2
|
namespace :db do
|
|
3
|
-
desc
|
|
4
|
-
task :seed_do, :
|
|
3
|
+
desc 'Load seed data into database'
|
|
4
|
+
task :seed_do, roles: :db, only: { primary: true } do
|
|
5
5
|
run "cd #{release_path} && bundle exec rake RAILS_ENV=#{rails_env} db:seed_do"
|
|
6
6
|
end
|
|
7
7
|
end
|
data/lib/seed-do/capistrano3.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
load File.expand_path('
|
|
1
|
+
load File.expand_path('../tasks/seed_do_capistrano3.rake', __dir__)
|
data/lib/seed-do/railtie.rb
CHANGED
data/lib/seed-do/runner.rb
CHANGED
|
@@ -3,9 +3,7 @@ require 'active_support/core_ext/array/wrap'
|
|
|
3
3
|
|
|
4
4
|
module SeedDo
|
|
5
5
|
# Runs seed files.
|
|
6
|
-
#
|
|
7
|
-
# It is not recommended to use this class directly. Instead, use {SeedDo.seed SeedDo.seed}, which creates
|
|
8
|
-
# an instead of {Runner} and calls {#run #run}.
|
|
6
|
+
# It is not recommended to use this class directly. Instead, use {SeedDo.seed SeedDo.seed}, which creates an instead of {Runner} and calls {#run #run}.
|
|
9
7
|
#
|
|
10
8
|
# @see SeedDo.seed SeedDo.seed
|
|
11
9
|
class Runner
|
|
@@ -13,61 +11,80 @@ module SeedDo
|
|
|
13
11
|
# `SeedDo.fixture_paths` if {nil}. If the argument is not an array, it will be wrapped by one.
|
|
14
12
|
# @param [Regexp] filter If given, only seed files with a file name matching this pattern will
|
|
15
13
|
# be used
|
|
16
|
-
|
|
14
|
+
# @param [Boolean, Hash] bulk If true, use upsert_all to insert/update records in bulk.
|
|
15
|
+
# If a hash, can include :batch_size (default: 1000) to control the number of records
|
|
16
|
+
# per upsert_all call.
|
|
17
|
+
def initialize(fixture_paths = nil, filter = nil, bulk: false)
|
|
17
18
|
@fixture_paths = Array.wrap(fixture_paths || SeedDo.fixture_paths)
|
|
18
19
|
@filter = filter
|
|
20
|
+
@seeder = build_seeder(bulk)
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# Run the seed files.
|
|
22
24
|
def run
|
|
25
|
+
SeedDo.current_seeder = @seeder
|
|
23
26
|
puts "\n== Filtering seed files against regexp: #{@filter.inspect}" if @filter && !SeedDo.quiet
|
|
24
27
|
|
|
25
|
-
filenames.each
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
filenames.each { |filename| run_file(filename) }
|
|
29
|
+
ensure
|
|
30
|
+
SeedDo.current_seeder = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def seed(model, constraints, data)
|
|
34
|
+
@seeder.seed(model, constraints, data)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def seed_once(model, constraints, data)
|
|
38
|
+
@seeder.seed_once(model, constraints, data)
|
|
28
39
|
end
|
|
29
40
|
|
|
30
41
|
private
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
def run_file(filename)
|
|
44
|
+
puts "\n== Seed from #{filename}" unless SeedDo.quiet
|
|
45
|
+
|
|
46
|
+
ActiveRecord::Base.transaction do
|
|
47
|
+
@seeder.with_seed_file { _run_file(filename) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def build_seeder(bulk)
|
|
52
|
+
return SeedDo::BulkSeeder.new(batch_size: bulk.fetch(:batch_size, 1000)) if bulk.is_a?(Hash)
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
|
|
54
|
+
bulk ? SeedDo::BulkSeeder.new : SeedDo::Seeder.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _run_file(filename)
|
|
58
|
+
open(filename) do |file|
|
|
59
|
+
chunked_ruby = +''
|
|
60
|
+
file.each_line do |line|
|
|
61
|
+
if line == "# BREAK EVAL\n"
|
|
62
|
+
eval(chunked_ruby)
|
|
37
63
|
chunked_ruby = +''
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
eval(chunked_ruby)
|
|
41
|
-
chunked_ruby = +''
|
|
42
|
-
else
|
|
43
|
-
chunked_ruby << line
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
eval(chunked_ruby) unless chunked_ruby == ''
|
|
64
|
+
else
|
|
65
|
+
chunked_ruby << line
|
|
47
66
|
end
|
|
48
67
|
end
|
|
68
|
+
eval(chunked_ruby) unless chunked_ruby == ''
|
|
49
69
|
end
|
|
70
|
+
end
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
else
|
|
57
|
-
File.open(filename) do |file|
|
|
58
|
-
yield file
|
|
59
|
-
end
|
|
60
|
-
end
|
|
72
|
+
def open(filename, &)
|
|
73
|
+
if filename[-3..-1] == '.gz'
|
|
74
|
+
Zlib::GzipReader.open(filename, &)
|
|
75
|
+
else
|
|
76
|
+
File.open(filename, &)
|
|
61
77
|
end
|
|
78
|
+
end
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
filenames.uniq!
|
|
69
|
-
filenames = filenames.find_all { |filename| filename =~ @filter } if @filter
|
|
70
|
-
filenames
|
|
80
|
+
def filenames
|
|
81
|
+
filenames = []
|
|
82
|
+
@fixture_paths.each do |path|
|
|
83
|
+
filenames += (Dir[File.join(path, '*.rb')] + Dir[File.join(path, '*.rb.gz')]).sort
|
|
71
84
|
end
|
|
85
|
+
filenames.uniq!
|
|
86
|
+
filenames = filenames.find_all { |filename| filename =~ @filter } if @filter
|
|
87
|
+
filenames
|
|
88
|
+
end
|
|
72
89
|
end
|
|
73
90
|
end
|
data/lib/seed-do/seeder.rb
CHANGED
|
@@ -8,30 +8,31 @@ module SeedDo
|
|
|
8
8
|
#
|
|
9
9
|
# @see ActiveRecordExtension
|
|
10
10
|
class Seeder
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
def seed(model_class, constraints, data)
|
|
12
|
+
seed_records(model_class, constraints, data)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def seed_once(model_class, constraints, data)
|
|
16
|
+
seed_records(model_class, constraints, data, insert_only: true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def with_seed_file
|
|
20
|
+
yield
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Insert/update the records as appropriate. Validation is skipped while saving.
|
|
26
|
+
# @return [Array<ActiveRecord::Base>] The records which have been seeded
|
|
27
|
+
def seed_records(model_class, constraints, data, options = {})
|
|
21
28
|
@model_class = model_class
|
|
22
29
|
@constraints = constraints.to_a.empty? ? [:id] : constraints
|
|
23
30
|
@data = data.to_a || []
|
|
24
31
|
@options = options.symbolize_keys
|
|
25
32
|
|
|
26
|
-
@options[:quiet] ||= SeedDo.quiet
|
|
27
|
-
|
|
28
33
|
validate_constraints!
|
|
29
34
|
validate_data!
|
|
30
|
-
end
|
|
31
35
|
|
|
32
|
-
# Insert/update the records as appropriate. Validation is skipped while saving.
|
|
33
|
-
# @return [Array<ActiveRecord::Base>] The records which have been seeded
|
|
34
|
-
def seed
|
|
35
36
|
records = @model_class.transaction do
|
|
36
37
|
@data.map { |record_data| seed_record(record_data.symbolize_keys) }
|
|
37
38
|
end
|
|
@@ -39,64 +40,61 @@ module SeedDo
|
|
|
39
40
|
records
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
def validate_constraints!
|
|
44
|
+
unknown_columns = @constraints.map(&:to_s) - @model_class.column_names
|
|
45
|
+
return if unknown_columns.empty?
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
unknown_columns = @constraints.map(&:to_s) - @model_class.column_names
|
|
46
|
-
unless unknown_columns.empty?
|
|
47
|
-
raise(ArgumentError,
|
|
47
|
+
raise(ArgumentError,
|
|
48
48
|
"Your seed constraints contained unknown columns: #{column_list(unknown_columns)}. " +
|
|
49
49
|
"Valid columns are: #{column_list(@model_class.column_names)}.")
|
|
50
|
-
|
|
51
|
-
end
|
|
50
|
+
end
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
def validate_data!
|
|
53
|
+
raise ArgumentError, 'Seed data missing' if @data.empty?
|
|
54
|
+
end
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
def column_list(columns)
|
|
57
|
+
'`' + columns.join('`, `') + '`'
|
|
58
|
+
end
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
def seed_record(data)
|
|
61
|
+
record = find_or_initialize_record(data)
|
|
62
|
+
return if @options[:insert_only] && !record.new_record?
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
puts " - #{@model_class} #{data.inspect}" unless SeedDo.quiet
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
record.assign_attributes(data)
|
|
67
|
+
record.save(validate: false) || raise(ActiveRecord::RecordNotSaved, 'Record not saved!')
|
|
68
|
+
record
|
|
69
|
+
end
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
def find_or_initialize_record(data)
|
|
72
|
+
@model_class.where(constraint_conditions(data)).take ||
|
|
74
73
|
@model_class.new
|
|
75
|
-
|
|
74
|
+
end
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
def constraint_conditions(data)
|
|
77
|
+
@constraints.to_h { |c| [c, data[c.to_sym]] }
|
|
78
|
+
end
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
def update_id_sequence
|
|
81
|
+
return unless %w[PostgreSQL PostGIS].include?(@model_class.connection.adapter_name)
|
|
82
|
+
return if @model_class.primary_key.nil? || @model_class.sequence_name.nil?
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
quoted_id = @model_class.connection.quote_column_name(@model_class.primary_key)
|
|
85
|
+
sequence = @model_class.sequence_name
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
if @model_class.connection.postgresql_version >= 100_000
|
|
88
|
+
sql = <<-EOS
|
|
90
89
|
SELECT setval('#{sequence}', (SELECT GREATEST(MAX(#{quoted_id})+(SELECT seqincrement FROM pg_sequence WHERE seqrelid = '#{sequence}'::regclass), (SELECT seqmin FROM pg_sequence WHERE seqrelid = '#{sequence}'::regclass)) FROM #{@model_class.quoted_table_name}), false)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
EOS
|
|
91
|
+
else
|
|
92
|
+
sql = <<-EOS
|
|
94
93
|
SELECT setval('#{sequence}', (SELECT GREATEST(MAX(#{quoted_id})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{@model_class.quoted_table_name}), false)
|
|
95
|
-
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
@model_class.connection.execute sql
|
|
99
|
-
end
|
|
94
|
+
EOS
|
|
100
95
|
end
|
|
96
|
+
|
|
97
|
+
@model_class.connection.execute sql
|
|
98
|
+
end
|
|
101
99
|
end
|
|
102
100
|
end
|
data/lib/seed-do/version.rb
CHANGED
data/lib/seed-do/writer.rb
CHANGED
|
@@ -18,9 +18,9 @@ module SeedDo
|
|
|
18
18
|
class Writer
|
|
19
19
|
cattr_accessor :default_options
|
|
20
20
|
@@default_options = {
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
21
|
+
chunk_size: 100,
|
|
22
|
+
constraints: [:id],
|
|
23
|
+
seed_type: :seed
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
# @param [Hash] options
|
|
@@ -33,13 +33,13 @@ module SeedDo
|
|
|
33
33
|
# @option options [Array<Symbol>] :constraints ([:id]) The constraining attributes for the seeds
|
|
34
34
|
def initialize(options = {})
|
|
35
35
|
@options = self.class.default_options.merge(options)
|
|
36
|
-
raise ArgumentError,
|
|
36
|
+
raise ArgumentError, 'missing option :class_name' unless @options[:class_name]
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# Creates a new instance of {Writer} with the `options`, and then calls {#write} with the
|
|
40
40
|
# `io_or_filename` and `block`
|
|
41
|
-
def self.write(io_or_filename, options = {}, &
|
|
42
|
-
new(options).write(io_or_filename, &
|
|
41
|
+
def self.write(io_or_filename, options = {}, &)
|
|
42
|
+
new(options).write(io_or_filename, &)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Writes the necessary headers and footers, and yields to a block within which the actual
|
|
@@ -51,7 +51,7 @@ module SeedDo
|
|
|
51
51
|
# closed automatically.)
|
|
52
52
|
# @yield [self] make calls to `#<<` within the block
|
|
53
53
|
def write(io_or_filename, &block)
|
|
54
|
-
raise ArgumentError,
|
|
54
|
+
raise ArgumentError, 'missing block' unless block_given?
|
|
55
55
|
|
|
56
56
|
if io_or_filename.respond_to?(:write)
|
|
57
57
|
write_to_io(io_or_filename, &block)
|
|
@@ -65,7 +65,7 @@ module SeedDo
|
|
|
65
65
|
# Add a seed. Must be called within a block passed to {#write}.
|
|
66
66
|
# @param [Hash] seed The attributes for the seed
|
|
67
67
|
def <<(seed)
|
|
68
|
-
raise
|
|
68
|
+
raise 'You must add seeds inside a SeedDo::Writer#write block' unless @io
|
|
69
69
|
|
|
70
70
|
buffer = +''
|
|
71
71
|
|
|
@@ -76,57 +76,59 @@ module SeedDo
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
buffer << ",\n"
|
|
79
|
-
buffer << ' ' + seed.inspect
|
|
79
|
+
buffer << (' ' + seed.inspect)
|
|
80
80
|
|
|
81
81
|
@io.write(buffer)
|
|
82
82
|
|
|
83
83
|
@count += 1
|
|
84
84
|
end
|
|
85
|
-
|
|
85
|
+
alias add <<
|
|
86
86
|
|
|
87
87
|
private
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
def write_to_io(io)
|
|
90
|
+
@io = io
|
|
91
|
+
@count = 0
|
|
92
|
+
@io.write(file_header)
|
|
93
|
+
@io.write(seed_header)
|
|
94
|
+
yield(self)
|
|
95
|
+
@io.write(seed_footer)
|
|
96
|
+
@io.write(file_footer)
|
|
97
|
+
ensure
|
|
98
|
+
@io = nil
|
|
99
|
+
@count = nil
|
|
100
|
+
end
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# DO NOT MODIFY THIS FILE, it was auto-generated.
|
|
103
|
-
#
|
|
104
|
-
# Date: #{Time.now}
|
|
105
|
-
# Seeding #{@options[:class_name]}
|
|
106
|
-
# Written with the command:
|
|
107
|
-
#
|
|
108
|
-
# #{$0} #{$*.join}
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
def file_header
|
|
103
|
+
<<~END
|
|
104
|
+
# DO NOT MODIFY THIS FILE, it was auto-generated.
|
|
105
|
+
#
|
|
106
|
+
# Date: #{Time.now}
|
|
107
|
+
# Seeding #{@options[:class_name]}
|
|
108
|
+
# Written with the command:
|
|
109
|
+
#
|
|
110
|
+
# #{$0} #{$*.join}
|
|
111
|
+
#
|
|
112
|
+
END
|
|
113
|
+
end
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# End auto-generated file.
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
def file_footer
|
|
116
|
+
<<~END
|
|
117
|
+
# End auto-generated file.
|
|
118
|
+
END
|
|
119
|
+
end
|
|
118
120
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
def seed_header
|
|
122
|
+
constraints = @options[:constraints] && @options[:constraints].map(&:inspect).join(', ')
|
|
123
|
+
"#{@options[:class_name]}.#{@options[:seed_type]}(#{constraints}"
|
|
124
|
+
end
|
|
123
125
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
def seed_footer
|
|
127
|
+
"\n)\n"
|
|
128
|
+
end
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
def chunk_this_seed?
|
|
131
|
+
@count != 0 && (@count % @options[:chunk_size]) == 0
|
|
132
|
+
end
|
|
131
133
|
end
|
|
132
134
|
end
|
data/lib/seed-do.rb
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
require 'active_record'
|
|
2
2
|
require 'active_support/core_ext/module/attribute_accessors'
|
|
3
|
-
require 'seed-do/railtie' if defined?(Rails)
|
|
3
|
+
require 'seed-do/railtie' if defined?(Rails)
|
|
4
4
|
|
|
5
5
|
module SeedDo
|
|
6
6
|
autoload :VERSION, 'seed-do/version'
|
|
7
7
|
autoload :Seeder, 'seed-do/seeder'
|
|
8
8
|
autoload :ActiveRecordExtension, 'seed-do/active_record_extension'
|
|
9
9
|
autoload :BlockHash, 'seed-do/block_hash'
|
|
10
|
+
autoload :BulkSeeder, 'seed-do/bulk_seeder'
|
|
10
11
|
autoload :Runner, 'seed-do/runner'
|
|
11
12
|
autoload :Writer, 'seed-do/writer'
|
|
12
13
|
|
|
13
|
-
mattr_accessor :quiet
|
|
14
|
-
|
|
15
14
|
# Set `SeedDo.quiet = true` to silence all output
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
mattr_accessor :fixture_paths
|
|
15
|
+
mattr_accessor :quiet, default: false
|
|
19
16
|
|
|
20
17
|
# Set this to be an array of paths to directories containing your seed files. If used as a Rails
|
|
21
|
-
# plugin, SeedDo will set
|
|
18
|
+
# plugin, SeedDo will set it to contain `Rails.root/db/fixtures` and
|
|
22
19
|
# `Rails.root/db/fixtures/Rails.env`
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
mattr_accessor :fixture_paths, default: ['db/fixtures']
|
|
25
21
|
# Load seed data from files
|
|
26
22
|
# @param [Array] fixture_paths The paths to look for seed files in
|
|
27
23
|
# @param [Regexp] filter If given, only filenames matching this expression will be loaded
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
# @param [Boolean] bulk If true, bulk insert/upsert will be used
|
|
25
|
+
def self.seed(fixture_paths = SeedDo.fixture_paths, filter = nil, bulk: false)
|
|
26
|
+
Runner.new(fixture_paths, filter, bulk: bulk).run
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.current_seeder
|
|
30
|
+
@current_seeder || Seeder.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.current_seeder=(seeder)
|
|
34
|
+
@current_seeder = seeder
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
37
|
|
data/lib/tasks/seed_do.rake
CHANGED
|
@@ -24,14 +24,10 @@ namespace :db do
|
|
|
24
24
|
# to load files from RAILS_ROOT/features/fixtures
|
|
25
25
|
rake db:seed_do FIXTURE_PATH=features/fixtures
|
|
26
26
|
EOS
|
|
27
|
-
task :
|
|
28
|
-
if ENV[
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if ENV["FIXTURE_PATH"]
|
|
33
|
-
fixture_paths = [ENV["FIXTURE_PATH"], ENV["FIXTURE_PATH"] + '/' + Rails.env]
|
|
34
|
-
end
|
|
27
|
+
task seed_do: :environment do
|
|
28
|
+
filter = /#{ENV['FILTER'].tr(',', '|')}/ if ENV['FILTER']
|
|
29
|
+
|
|
30
|
+
fixture_paths = [ENV['FIXTURE_PATH'], ENV['FIXTURE_PATH'] + '/' + Rails.env] if ENV['FIXTURE_PATH']
|
|
35
31
|
|
|
36
32
|
SeedDo.seed(fixture_paths, filter)
|
|
37
33
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: seed-do
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shinichi Maeshima
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- lib/seed-do.rb
|
|
67
67
|
- lib/seed-do/active_record_extension.rb
|
|
68
68
|
- lib/seed-do/block_hash.rb
|
|
69
|
+
- lib/seed-do/bulk_seeder.rb
|
|
69
70
|
- lib/seed-do/capistrano.rb
|
|
70
71
|
- lib/seed-do/capistrano3.rb
|
|
71
72
|
- lib/seed-do/railtie.rb
|
|
@@ -78,7 +79,8 @@ files:
|
|
|
78
79
|
homepage: http://github.com/willnet/seed-do
|
|
79
80
|
licenses:
|
|
80
81
|
- MIT
|
|
81
|
-
metadata:
|
|
82
|
+
metadata:
|
|
83
|
+
rubygems_mfa_required: 'true'
|
|
82
84
|
rdoc_options: []
|
|
83
85
|
require_paths:
|
|
84
86
|
- lib
|
|
@@ -93,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
93
95
|
- !ruby/object:Gem::Version
|
|
94
96
|
version: '0'
|
|
95
97
|
requirements: []
|
|
96
|
-
rubygems_version:
|
|
98
|
+
rubygems_version: 4.0.6
|
|
97
99
|
specification_version: 4
|
|
98
100
|
summary: Easily manage seed data in your Active Record application
|
|
99
101
|
test_files: []
|