seed_dump_citus 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/Gemfile +20 -0
- data/MIT-LICENSE +20 -0
- data/README.md +131 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/lib/seed_dump_citus.rb +11 -0
- data/lib/seed_dump_citus/dump_methods.rb +129 -0
- data/lib/seed_dump_citus/dump_methods/enumeration.rb +78 -0
- data/lib/seed_dump_citus/environment.rb +139 -0
- data/lib/seed_dump_citus/railtie.rb +9 -0
- data/lib/tasks/seed_dump_citus.rake +11 -0
- data/seed_dump_citus.gemspec +74 -0
- data/spec/dump_methods_spec.rb +176 -0
- data/spec/environment_spec.rb +144 -0
- data/spec/factories/another_samples.rb +14 -0
- data/spec/factories/samples.rb +16 -0
- data/spec/factories/yet_another_samples.rb +14 -0
- data/spec/helpers.rb +92 -0
- data/spec/spec_helper.rb +36 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 589295ccb53fca1a66f410a15a6fce7c179be89a0ab6ce311967bd6a130b12e0
|
4
|
+
data.tar.gz: 6649359765019a7faf1ee7f1655565841fd011a8b98ad3714f2d23dd4ff2afed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3598300d5a6b4f61465ba26708ffc4ce24ac7088a32ff1c329ba3c15f292c80f9f01c80a3545d01b938affe83b826605cf36b40dd3b24a97511c2e0df4f1dda8
|
7
|
+
data.tar.gz: be3d6452a2ea1f69cbda6cd48f51d349d3f155d5787db34f4f7e7c6681abbf34d378372a76a762b45a496e051eaefd2fd26037976d34a8cc575674d626106ebe
|
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'activesupport', '>= 4'
|
4
|
+
gem 'activerecord', '>= 4'
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'byebug', '~> 2.0'
|
8
|
+
gem 'factory_bot', '~> 4.8.2'
|
9
|
+
gem 'activerecord-import', '~> 0.4'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :development do
|
13
|
+
gem 'jeweler', '~> 2.0'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'rspec', '~> 3.7.0'
|
18
|
+
gem 'sqlite3', '~> 1.0'
|
19
|
+
gem 'database_cleaner', '~> 1.0'
|
20
|
+
end
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Ryan Oblak
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
Seed Dump
|
2
|
+
========
|
3
|
+
|
4
|
+
Seed Dump is a Rails 4 and 5 plugin that adds a rake task named `db:seed:dump`.
|
5
|
+
|
6
|
+
It allows you to create seed data files from the existing data in your database.
|
7
|
+
|
8
|
+
You can also use Seed Dump from the Rails console. See below for usage examples.
|
9
|
+
|
10
|
+
Note: if you want to use Seed Dump with Rails 3 or earlier, use [version 0.5.3](http://rubygems.org/gems/seed_dump/versions/0.5.3).
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
Add it to your Gemfile with:
|
16
|
+
```ruby
|
17
|
+
gem 'seed_dump_citus'
|
18
|
+
```
|
19
|
+
Or install it by hand:
|
20
|
+
```sh
|
21
|
+
$ gem install seed_dump
|
22
|
+
```
|
23
|
+
Examples
|
24
|
+
--------
|
25
|
+
|
26
|
+
### Rake task
|
27
|
+
|
28
|
+
Dump all data directly to `db/seeds.rb`:
|
29
|
+
```sh
|
30
|
+
$ rake db:seed:dump
|
31
|
+
```
|
32
|
+
Result:
|
33
|
+
```ruby
|
34
|
+
Product.create!([
|
35
|
+
{ category_id: 1, description: "Long Sleeve Shirt", name: "Long Sleeve Shirt" },
|
36
|
+
{ category_id: 3, description: "Plain White Tee Shirt", name: "Plain T-Shirt" }
|
37
|
+
])
|
38
|
+
User.create!([
|
39
|
+
{ password: "123456", username: "test_1" },
|
40
|
+
{ password: "234567", username: "test_2" }
|
41
|
+
])
|
42
|
+
```
|
43
|
+
|
44
|
+
Dump only data from the users table and dump a maximum of 1 record:
|
45
|
+
```sh
|
46
|
+
$ rake db:seed:dump MODELS=User LIMIT=1
|
47
|
+
```
|
48
|
+
|
49
|
+
Result:
|
50
|
+
```ruby
|
51
|
+
User.create!([
|
52
|
+
{ password: "123456", username: "test_1" }
|
53
|
+
])
|
54
|
+
```
|
55
|
+
|
56
|
+
Append to `db/seeds.rb` instead of overwriting it:
|
57
|
+
```sh
|
58
|
+
rake db:seed:dump APPEND=true
|
59
|
+
```
|
60
|
+
|
61
|
+
Use another output file instead of `db/seeds.rb`:
|
62
|
+
```sh
|
63
|
+
rake db:seed:dump FILE=db/seeds/users.rb
|
64
|
+
```
|
65
|
+
|
66
|
+
Exclude `name` and `age` from the dump:
|
67
|
+
```sh
|
68
|
+
rake db:seed:dump EXCLUDE=name,age
|
69
|
+
```
|
70
|
+
|
71
|
+
There are more options that can be set— see below for all of them.
|
72
|
+
|
73
|
+
### Console
|
74
|
+
|
75
|
+
Output a dump of all User records:
|
76
|
+
```ruby
|
77
|
+
irb(main):001:0> puts SeedDump.dump(User)
|
78
|
+
User.create!([
|
79
|
+
{ password: "123456", username: "test_1" },
|
80
|
+
{ password: "234567", username: "test_2" }
|
81
|
+
])
|
82
|
+
```
|
83
|
+
|
84
|
+
Write the dump to a file:
|
85
|
+
```ruby
|
86
|
+
irb(main):002:0> SeedDump.dump(User, file: 'db/seeds.rb')
|
87
|
+
```
|
88
|
+
|
89
|
+
Append the dump to a file:
|
90
|
+
```ruby
|
91
|
+
irb(main):003:0> SeedDump.dump(User, file: 'db/seeds.rb', append: true)
|
92
|
+
```
|
93
|
+
|
94
|
+
Exclude `name` and `age` from the dump:
|
95
|
+
```ruby
|
96
|
+
irb(main):004:0> SeedDump.dump(User, exclude: [:name, :age])
|
97
|
+
```
|
98
|
+
|
99
|
+
Options are specified as a Hash for the second argument.
|
100
|
+
|
101
|
+
In the console, any relation of ActiveRecord rows can be dumped (not individual objects though)
|
102
|
+
```ruby
|
103
|
+
irb(main):001:0> puts SeedDump.dump(User.where(is_admin: false))
|
104
|
+
User.create!([
|
105
|
+
{ password: "123456", username: "test_1", is_admin: false },
|
106
|
+
{ password: "234567", username: "test_2", is_admin: false }
|
107
|
+
])
|
108
|
+
```
|
109
|
+
|
110
|
+
Options
|
111
|
+
-------
|
112
|
+
|
113
|
+
Options are common to both the Rake task and the console, except where noted.
|
114
|
+
|
115
|
+
`append`: If set to `true`, append the data to the file instead of overwriting it. Default: `false`.
|
116
|
+
|
117
|
+
`batch_size`: Controls the number of records that are written to file at a given time. Default: 1000. If you're running out of memory when dumping, try decreasing this. If things are dumping too slow, trying increasing this.
|
118
|
+
|
119
|
+
`exclude`: Attributes to be excluded from the dump. Pass a comma-separated list to the Rake task (i.e. `name,age`) and an array on the console (i.e. `[:name, :age]`). Default: `[:id, :created_at, :updated_at]`.
|
120
|
+
|
121
|
+
`file`: Write to the specified output file. The Rake task default is `db/seeds.rb`. The console returns the dump as a string by default.
|
122
|
+
|
123
|
+
`import`: If `true`, output will be in the format needed by the [activerecord-import](https://github.com/zdennis/activerecord-import) gem, rather than the default format. Default: `false`.
|
124
|
+
|
125
|
+
`limit`: Dump no more than this amount of data. Default: no limit. Rake task only. In the console just pass in an ActiveRecord::Relation with the appropriate limit (e.g. `SeedDump.dump(User.limit(5))`).
|
126
|
+
|
127
|
+
`conditions`: Dump only specific records. In the console just pass in an ActiveRecord::Relation with the appropriate conditions (e.g. `SeedDump.dump(User.where(state: :active))`).
|
128
|
+
|
129
|
+
`model[s]`: Restrict the dump to the specified comma-separated list of models. Default: all models. If you are using a Rails engine you can dump a specific model by passing "EngineName::ModelName". Rake task only. Example: `rake db:seed:dump MODELS="User, Position, Function"`
|
130
|
+
|
131
|
+
`models_exclude`: Exclude the specified comma-separated list of models from the dump. Default: no models excluded. Rake task only. Example: `rake db:seed:dump MODELS_EXCLUDE="User"`
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "seed_dump_citus"
|
8
|
+
gem.summary = "{Seed Dumper for Rails}"
|
9
|
+
gem.description = %Q{Dump (parts) of your database to db/seeds.rb to get a headstart creating a meaningful seeds.rb file}
|
10
|
+
gem.email = 'rroblak@gmail.com'
|
11
|
+
gem.homepage = 'https://github.com/rroblak/seed_dump_citus'
|
12
|
+
gem.authors = ['Rob Halff', 'Ryan Oblak']
|
13
|
+
gem.license = 'MIT'
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rdoc/task'
|
21
|
+
Rake::RDocTask.new do |rdoc|
|
22
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
23
|
+
|
24
|
+
rdoc.rdoc_dir = 'rdoc'
|
25
|
+
rdoc.title = "seed_dump_citus #{version}"
|
26
|
+
rdoc.rdoc_files.include('README*')
|
27
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
RSpec::Core::RakeTask.new(:spec)
|
32
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.2
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'seed_dump_citus/dump_methods/enumeration'
|
3
|
+
require 'seed_dump_citus/dump_methods'
|
4
|
+
require 'seed_dump_citus/environment'
|
5
|
+
|
6
|
+
class SeedDumpCitus
|
7
|
+
extend Environment
|
8
|
+
extend DumpMethods
|
9
|
+
|
10
|
+
require 'seed_dump_citus/railtie' if defined?(Rails)
|
11
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class SeedDumpCitus
|
2
|
+
module DumpMethods
|
3
|
+
include Enumeration
|
4
|
+
|
5
|
+
def dump(records, options = {})
|
6
|
+
return nil if records.count == 0
|
7
|
+
|
8
|
+
io = open_io(options)
|
9
|
+
|
10
|
+
write_records_to_io(records, io, options)
|
11
|
+
|
12
|
+
ensure
|
13
|
+
io.close if io.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def dump_record(record, options)
|
19
|
+
attribute_strings = []
|
20
|
+
|
21
|
+
# We select only string attribute names to avoid conflict
|
22
|
+
# with the composite_primary_keys gem (it returns composite
|
23
|
+
# primary key attribute names as hashes).
|
24
|
+
record.attributes.select {|key| key.is_a?(String) || key.is_a?(Symbol) }.each do |attribute, value|
|
25
|
+
attribute_strings << dump_attribute_new(attribute, value, options) unless options[:exclude].include?(attribute.to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
open_character, close_character = options[:import] ? ['[', ']'] : ['{', '}']
|
29
|
+
|
30
|
+
"#{open_character}#{attribute_strings.join(", ")}#{close_character}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def dump_attribute_new(attribute, value, options)
|
34
|
+
options[:import] ? value_to_s(value) : "#{attribute}: #{value_to_s(value)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def value_to_s(value)
|
38
|
+
value = case value
|
39
|
+
when BigDecimal, IPAddr
|
40
|
+
value.to_s
|
41
|
+
when Date, Time, DateTime
|
42
|
+
value.to_s(:db)
|
43
|
+
when Range
|
44
|
+
range_to_string(value)
|
45
|
+
when ->(v) { v.class.ancestors.map(&:to_s).include?('RGeo::Feature::Instance') }
|
46
|
+
value.to_s
|
47
|
+
else
|
48
|
+
value
|
49
|
+
end
|
50
|
+
|
51
|
+
value.inspect
|
52
|
+
end
|
53
|
+
|
54
|
+
def range_to_string(object)
|
55
|
+
from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
|
56
|
+
to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
|
57
|
+
"[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def open_io(options)
|
61
|
+
if options[:file].present?
|
62
|
+
mode = options[:append] ? 'a+' : 'w+'
|
63
|
+
|
64
|
+
File.open(options[:file], mode)
|
65
|
+
else
|
66
|
+
StringIO.new('', 'w+')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def write_records_to_io(records, io, options)
|
71
|
+
options[:exclude] ||= [:id, :created_at, :updated_at]
|
72
|
+
|
73
|
+
method = options[:import] ? 'import' : 'create!'
|
74
|
+
io.write("#{model_for(records)}.#{method}(")
|
75
|
+
if options[:import]
|
76
|
+
io.write("[#{attribute_names(records, options).map {|name| name.to_sym.inspect}.join(', ')}], ")
|
77
|
+
end
|
78
|
+
io.write("[\n ")
|
79
|
+
|
80
|
+
enumeration_method = if records.is_a?(ActiveRecord::Relation) || records.is_a?(Class)
|
81
|
+
:active_record_enumeration
|
82
|
+
else
|
83
|
+
:enumerable_enumeration
|
84
|
+
end
|
85
|
+
|
86
|
+
send(enumeration_method, records, io, options) do |record_strings, last_batch|
|
87
|
+
io.write(record_strings.join(",\n "))
|
88
|
+
|
89
|
+
io.write(",\n ") unless last_batch
|
90
|
+
end
|
91
|
+
|
92
|
+
io.write("\n]#{active_record_import_options(options)})\n")
|
93
|
+
|
94
|
+
if options[:file].present?
|
95
|
+
nil
|
96
|
+
else
|
97
|
+
io.rewind
|
98
|
+
io.read
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def active_record_import_options(options)
|
103
|
+
return unless options[:import] && options[:import].is_a?(Hash)
|
104
|
+
|
105
|
+
', ' + options[:import].map { |key, value| "#{key}: #{value}" }.join(', ')
|
106
|
+
end
|
107
|
+
|
108
|
+
def attribute_names(records, options)
|
109
|
+
attribute_names = if records.is_a?(ActiveRecord::Relation) || records.is_a?(Class)
|
110
|
+
records.attribute_names
|
111
|
+
else
|
112
|
+
records[0].attribute_names
|
113
|
+
end
|
114
|
+
|
115
|
+
attribute_names.select {|name| !options[:exclude].include?(name.to_sym)}
|
116
|
+
end
|
117
|
+
|
118
|
+
def model_for(records)
|
119
|
+
if records.is_a?(Class)
|
120
|
+
records
|
121
|
+
elsif records.respond_to?(:model)
|
122
|
+
records.model
|
123
|
+
else
|
124
|
+
records[0].class
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class SeedDumpCitus
|
2
|
+
module DumpMethods
|
3
|
+
module Enumeration
|
4
|
+
def active_record_enumeration(records, io, options)
|
5
|
+
# If the records don't already have an order,
|
6
|
+
# order them by primary key ascending.
|
7
|
+
if !records.respond_to?(:arel) || records.arel.orders.blank?
|
8
|
+
records.order("#{records.quoted_table_name}.#{records.quoted_primary_key} ASC")
|
9
|
+
end
|
10
|
+
|
11
|
+
num_of_batches, batch_size, last_batch_size = batch_params_from(records, options)
|
12
|
+
|
13
|
+
# Loop through each batch
|
14
|
+
TransactionAccessor.acceptable_non_sharded_read do
|
15
|
+
|
16
|
+
(1..num_of_batches).each do |batch_number|
|
17
|
+
|
18
|
+
record_strings = []
|
19
|
+
|
20
|
+
last_batch = (batch_number == num_of_batches)
|
21
|
+
|
22
|
+
cur_batch_size = if last_batch
|
23
|
+
last_batch_size
|
24
|
+
else
|
25
|
+
batch_size
|
26
|
+
end
|
27
|
+
|
28
|
+
# Loop through the records of the current batch
|
29
|
+
records.offset((batch_number - 1) * batch_size).limit(cur_batch_size).each do |record|
|
30
|
+
record_strings << dump_record(record, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
yield record_strings, last_batch
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def enumerable_enumeration(records, io, options)
|
39
|
+
num_of_batches, batch_size = batch_params_from(records, options)
|
40
|
+
|
41
|
+
record_strings = []
|
42
|
+
|
43
|
+
batch_number = 1
|
44
|
+
|
45
|
+
records.each_with_index do |record, i|
|
46
|
+
record_strings << dump_record(record, options)
|
47
|
+
|
48
|
+
last_batch = (i == records.length - 1)
|
49
|
+
|
50
|
+
if (record_strings.length == batch_size) || last_batch
|
51
|
+
yield record_strings, last_batch
|
52
|
+
|
53
|
+
record_strings = []
|
54
|
+
batch_number += 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def batch_params_from(records, options)
|
60
|
+
batch_size = batch_size_from(records, options)
|
61
|
+
|
62
|
+
count = records.count
|
63
|
+
|
64
|
+
remainder = count % batch_size
|
65
|
+
|
66
|
+
[((count.to_f / batch_size).ceil), batch_size, (remainder == 0 ? batch_size : remainder)]
|
67
|
+
end
|
68
|
+
|
69
|
+
def batch_size_from(records, options)
|
70
|
+
if options[:batch_size].present?
|
71
|
+
options[:batch_size].to_i
|
72
|
+
else
|
73
|
+
1000
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|