seed_dump_citus 1.0.2
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 +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
|