seed-do 3.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 +7 -0
- data/LICENSE +20 -0
- data/README.md +182 -0
- data/lib/seed-do/active_record_extension.rb +65 -0
- data/lib/seed-do/block_hash.rb +21 -0
- data/lib/seed-do/capistrano.rb +8 -0
- data/lib/seed-do/capistrano3.rb +1 -0
- data/lib/seed-do/railtie.rb +14 -0
- data/lib/seed-do/runner.rb +73 -0
- data/lib/seed-do/seeder.rb +109 -0
- data/lib/seed-do/version.rb +3 -0
- data/lib/seed-do/writer.rb +132 -0
- data/lib/seed-do.rb +36 -0
- data/lib/tasks/seed_do.rake +38 -0
- data/lib/tasks/seed_do_capistrano3.rake +12 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 22380fbfaeb46273c0f58822bf8ce464131a64a044d71a6da6488cbcd4dd3ee3
|
4
|
+
data.tar.gz: 6ff8f43389b63701f3575f2556b167a3788e4c8d3f490383d14044a43c1905b5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3125f1fad5c9f288ce3aa20a8542f43e28dc57725d4a621ac7aa8a1f1caa366e45f8e6f9498019c27bbf456032b281dfb4a89164d05876d4ad093bdfb658f504
|
7
|
+
data.tar.gz: 44d24c0b5551e13ce3f9c3223002999e6072426d5b73c399ba4d7850e9ee858b6cabd433a805393effc08db7eb068127edce8a5419312880fe65c1b11f392715
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2010 Michael Bleigh
|
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,182 @@
|
|
1
|
+
# Seed Do
|
2
|
+
|
3
|
+
This project is a fork of [seed-fu](https://github.com/mbleigh/seed-fu).
|
4
|
+
|
5
|
+
Seed Do is an attempt to once and for all solve the problem of inserting and maintaining seed data in a database. It uses a variety of techniques gathered from various places around the web and combines them to create what is hopefully the most robust seed data system around.
|
6
|
+
|
7
|
+
## Basic Example
|
8
|
+
|
9
|
+
### In `db/fixtures/users.rb`
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
User.seed do |s|
|
13
|
+
s.id = 1
|
14
|
+
s.login = "jon"
|
15
|
+
s.email = "jon@example.com"
|
16
|
+
s.name = "Jon"
|
17
|
+
end
|
18
|
+
|
19
|
+
User.seed do |s|
|
20
|
+
s.id = 2
|
21
|
+
s.login = "emily"
|
22
|
+
s.email = "emily@example.com"
|
23
|
+
s.name = "Emily"
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
### To load the data:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
$ rake db:seed_do
|
31
|
+
== Seed from /path/to/app/db/fixtures/users.rb
|
32
|
+
- User {:id=>1, :login=>"jon", :email=>"jon@example.com", :name=>"Jon"}
|
33
|
+
- User {:id=>2, :login=>"emily", :email=>"emily@example.com", :name=>"Emily"}
|
34
|
+
```
|
35
|
+
|
36
|
+
## Installation
|
37
|
+
|
38
|
+
Just add `gem 'seed-do'` to your `Gemfile`
|
39
|
+
|
40
|
+
## Migrating from seed-fu
|
41
|
+
|
42
|
+
If you are migrating from seed-fu to seed-do, you'll need to make the following changes:
|
43
|
+
|
44
|
+
1. Rename all occurrences of `SeedFu` to `SeedDo` in your code
|
45
|
+
2. Update rake task references from `rake db:seed_fu` to `rake db:seed_do`
|
46
|
+
|
47
|
+
For example:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Before
|
51
|
+
SeedFu.seed
|
52
|
+
rake db:seed_fu
|
53
|
+
|
54
|
+
# After
|
55
|
+
SeedDo.seed
|
56
|
+
rake db:seed_do
|
57
|
+
```
|
58
|
+
|
59
|
+
Capistrano integration also needs to be updated:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# Before
|
63
|
+
require 'seed-fu/capistrano'
|
64
|
+
# or for Capistrano3
|
65
|
+
require 'seed-fu/capistrano3'
|
66
|
+
|
67
|
+
# After
|
68
|
+
require 'seed-do/capistrano'
|
69
|
+
# or for Capistrano3
|
70
|
+
require 'seed-do/capistrano3'
|
71
|
+
```
|
72
|
+
|
73
|
+
## Constraints
|
74
|
+
|
75
|
+
Constraints are used to identify seeds, so that they can be updated if necessary. For example:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Point.seed(:x, :y) do |s|
|
79
|
+
s.x = 4
|
80
|
+
s.y = 7
|
81
|
+
s.name = "Home"
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
The first time this seed is loaded, a `Point` record will be created. Now suppose the name is changed:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
Point.seed(:x, :y) do |s|
|
89
|
+
s.x = 4
|
90
|
+
s.y = 7
|
91
|
+
s.name = "Work"
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
When this is run, Seed Do will look for a `Point` based on the `:x` and `:y` constraints provided. It will see that a matching `Point` already exists and so update its attributes rather than create a new record.
|
96
|
+
|
97
|
+
If you do not want seeds to be updated after they have been created, use `seed_once`:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
Point.seed_once(:x, :y) do |s|
|
101
|
+
s.x = 4
|
102
|
+
s.y = 7
|
103
|
+
s.name = "Home"
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
The default constraint just checks the `id` of the record.
|
108
|
+
|
109
|
+
## Where to put seed files
|
110
|
+
|
111
|
+
By default, seed files are looked for in the following locations:
|
112
|
+
|
113
|
+
- `#{Rails.root}/db/fixtures` and `#{Rails.root}/db/fixtures/#{Rails.env}` in a Rails app
|
114
|
+
- `./db/fixtures` when loaded without Rails
|
115
|
+
|
116
|
+
You can change these defaults by modifying the `SeedDo.fixture_paths` array.
|
117
|
+
|
118
|
+
Seed files can be named whatever you like, and are loaded in alphabetical order.
|
119
|
+
|
120
|
+
## Terser syntax
|
121
|
+
|
122
|
+
When loading lots of records, the above block-based syntax can be quite verbose. You can use the following instead:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
User.seed(:id,
|
126
|
+
{ :id => 1, :login => "jon", :email => "jon@example.com", :name => "Jon" },
|
127
|
+
{ :id => 2, :login => "emily", :email => "emily@example.com", :name => "Emily" }
|
128
|
+
)
|
129
|
+
```
|
130
|
+
|
131
|
+
## Rake task
|
132
|
+
|
133
|
+
Seed files can be run automatically using `rake db:seed_do`. There are two options which you can pass:
|
134
|
+
|
135
|
+
- `rake db:seed_do FIXTURE_PATH=path/to/fixtures` -- Where to find the fixtures
|
136
|
+
- `rake db:seed_do FILTER=users,articles` -- Only run seed files with a filename matching the `FILTER`
|
137
|
+
|
138
|
+
You can also do a similar thing in your code by calling `SeedDo.seed(fixture_paths, filter)`.
|
139
|
+
|
140
|
+
## Disable output
|
141
|
+
|
142
|
+
To disable output from Seed Do, set `SeedDo.quiet = true`.
|
143
|
+
|
144
|
+
## Handling large seed files
|
145
|
+
|
146
|
+
Seed files can be huge. To handle large files (over a million rows), try these tricks:
|
147
|
+
|
148
|
+
- Gzip your fixtures. Seed Do will read .rb.gz files happily. `gzip -9` gives the best compression, and with Seed Do's repetitive syntax, a 160M file can shrink to 16M.
|
149
|
+
- Add lines reading `# BREAK EVAL` in your big fixtures, and Seed Do will avoid loading the whole file into memory. If you use `SeedDo::Writer`, these breaks are built into your generated fixtures.
|
150
|
+
- Load a single fixture at a time with the `FILTER` environment variable
|
151
|
+
- If you don't need Seed Do's ability to update seed with new data, then you may find that [activerecord-import](https://github.com/zdennis/activerecord-import) is faster
|
152
|
+
|
153
|
+
## Generating seed files
|
154
|
+
|
155
|
+
If you need to programmatically generate seed files, for example to convert a CSV file into a seed file, then you can use [`SeedDo::Writer`](lib/seed-do/writer.rb).
|
156
|
+
|
157
|
+
## Capistrano deployment
|
158
|
+
|
159
|
+
SeedDo has included Capistrano [deploy script](lib/seed-do/capistrano.rb), you just need require that
|
160
|
+
in `config/deploy.rb`:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
require 'seed-do/capistrano'
|
164
|
+
|
165
|
+
# Trigger the task after update_code
|
166
|
+
after 'deploy:update_code', 'db:seed_do'
|
167
|
+
```
|
168
|
+
|
169
|
+
If you use Capistrano3, you should require another file.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
require 'seed-do/capistrano3'
|
173
|
+
|
174
|
+
# Trigger the task before publishing
|
175
|
+
before 'deploy:publishing', 'db:seed_do'
|
176
|
+
```
|
177
|
+
|
178
|
+
## Original Author
|
179
|
+
|
180
|
+
[Michael Bleigh](http://www.mbleigh.com/) is the original author
|
181
|
+
|
182
|
+
Copyright © 2008-2010 Michael Bleigh, released under the MIT license
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module SeedDo
|
2
|
+
module ActiveRecordExtension
|
3
|
+
# Load some seed data. There are two ways to do this.
|
4
|
+
#
|
5
|
+
# Verbose syntax
|
6
|
+
# --------------
|
7
|
+
#
|
8
|
+
# This will seed a single record. The `:id` parameter ensures that if a record already exists
|
9
|
+
# in the database with the same id, then it will be updated with the name and age, rather
|
10
|
+
# than created from scratch.
|
11
|
+
#
|
12
|
+
# Person.seed(:id) do |s|
|
13
|
+
# s.id = 1
|
14
|
+
# s.name = "Jon"
|
15
|
+
# s.age = 21
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Note that `:id` is the default attribute used to identify a seed, so it need not be
|
19
|
+
# specified.
|
20
|
+
#
|
21
|
+
# Terse syntax
|
22
|
+
# ------------
|
23
|
+
#
|
24
|
+
# This is a more succinct way to load multiple records. Note that both `:x` and `:y` are being
|
25
|
+
# used to identify a seed here.
|
26
|
+
#
|
27
|
+
# Point.seed(:x, :y,
|
28
|
+
# { :x => 3, :y => 10, :name => "Home" },
|
29
|
+
# { :x => 5, :y => 9, :name => "Office" }
|
30
|
+
# )
|
31
|
+
def seed(*args, &block)
|
32
|
+
SeedDo::Seeder.new(self, *parse_seed_do_args(args, block)).seed
|
33
|
+
end
|
34
|
+
|
35
|
+
# Has the same syntax as {#seed}, but if a record already exists with the same values for
|
36
|
+
# constraining attributes, it will not be updated.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# Person.seed(:id, :id => 1, :name => "Jon") # => Record created
|
40
|
+
# Person.seed(:id, :id => 1, :name => "Bob") # => Name changed
|
41
|
+
# Person.seed_once(:id, :id => 1, :name => "Harry") # => Name *not* changed
|
42
|
+
def seed_once(*args, &block)
|
43
|
+
constraints, data = parse_seed_do_args(args, block)
|
44
|
+
SeedDo::Seeder.new(self, constraints, data, :insert_only => true).seed
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_seed_do_args(args, block)
|
50
|
+
if block.nil?
|
51
|
+
if args.last.is_a?(Array)
|
52
|
+
# Last arg is an array of data, so assume the rest of the args are constraints
|
53
|
+
data = args.pop
|
54
|
+
[args, data]
|
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
|
59
|
+
else
|
60
|
+
# We have a block, so assume the args are all constraints
|
61
|
+
[args, [SeedDo::BlockHash.new(block).to_hash]]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SeedDo
|
2
|
+
# @private
|
3
|
+
class BlockHash
|
4
|
+
def initialize(proc)
|
5
|
+
@hash = {}
|
6
|
+
proc.call(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash
|
10
|
+
@hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_name, *args, &block)
|
14
|
+
if method_name.to_s =~ /^(.*)=$/ && args.length == 1 && block.nil?
|
15
|
+
@hash[$1] = args.first
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
load File.expand_path('../../tasks/seed_do_capistrano3.rake', __FILE__)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SeedDo
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
load "tasks/seed_do.rake"
|
5
|
+
end
|
6
|
+
|
7
|
+
initializer 'seed_do.set_fixture_paths' do
|
8
|
+
SeedDo.fixture_paths = [
|
9
|
+
Rails.root.join('db/fixtures').to_s,
|
10
|
+
Rails.root.join('db/fixtures/' + Rails.env).to_s
|
11
|
+
]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
require 'active_support/core_ext/array/wrap'
|
3
|
+
|
4
|
+
module SeedDo
|
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}.
|
9
|
+
#
|
10
|
+
# @see SeedDo.seed SeedDo.seed
|
11
|
+
class Runner
|
12
|
+
# @param [Array<String>] fixture_paths The paths where fixtures are located. Will use
|
13
|
+
# `SeedDo.fixture_paths` if {nil}. If the argument is not an array, it will be wrapped by one.
|
14
|
+
# @param [Regexp] filter If given, only seed files with a file name matching this pattern will
|
15
|
+
# be used
|
16
|
+
def initialize(fixture_paths = nil, filter = nil)
|
17
|
+
@fixture_paths = Array.wrap(fixture_paths || SeedDo.fixture_paths)
|
18
|
+
@filter = filter
|
19
|
+
end
|
20
|
+
|
21
|
+
# Run the seed files.
|
22
|
+
def run
|
23
|
+
puts "\n== Filtering seed files against regexp: #{@filter.inspect}" if @filter && !SeedDo.quiet
|
24
|
+
|
25
|
+
filenames.each do |filename|
|
26
|
+
run_file(filename)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def run_file(filename)
|
33
|
+
puts "\n== Seed from #{filename}" unless SeedDo.quiet
|
34
|
+
|
35
|
+
ActiveRecord::Base.transaction do
|
36
|
+
open(filename) do |file|
|
37
|
+
chunked_ruby = ''
|
38
|
+
file.each_line do |line|
|
39
|
+
if line == "# BREAK EVAL\n"
|
40
|
+
eval(chunked_ruby)
|
41
|
+
chunked_ruby = ''
|
42
|
+
else
|
43
|
+
chunked_ruby << line
|
44
|
+
end
|
45
|
+
end
|
46
|
+
eval(chunked_ruby) unless chunked_ruby == ''
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def open(filename)
|
52
|
+
if filename[-3..-1] == '.gz'
|
53
|
+
Zlib::GzipReader.open(filename) do |file|
|
54
|
+
yield file
|
55
|
+
end
|
56
|
+
else
|
57
|
+
File.open(filename) do |file|
|
58
|
+
yield file
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def filenames
|
64
|
+
filenames = []
|
65
|
+
@fixture_paths.each do |path|
|
66
|
+
filenames += (Dir[File.join(path, '*.rb')] + Dir[File.join(path, '*.rb.gz')]).sort
|
67
|
+
end
|
68
|
+
filenames.uniq!
|
69
|
+
filenames = filenames.find_all { |filename| filename =~ @filter } if @filter
|
70
|
+
filenames
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
|
3
|
+
module SeedDo
|
4
|
+
# Creates or updates seed records with data.
|
5
|
+
#
|
6
|
+
# It is not recommended to use this class directly. Instead, use `Model.seed`, and `Model.seed_once`,
|
7
|
+
# where `Model` is your Active Record model.
|
8
|
+
#
|
9
|
+
# @see ActiveRecordExtension
|
10
|
+
class Seeder
|
11
|
+
# @param [ActiveRecord::Base] model_class The model to be seeded
|
12
|
+
# @param [Array<Symbol>] constraints A list of attributes which identify a particular seed. If
|
13
|
+
# a record with these attributes already exists then it will be updated rather than created.
|
14
|
+
# @param [Array<Hash>] data Each item in this array is a hash containing attributes for a
|
15
|
+
# particular record.
|
16
|
+
# @param [Hash] options
|
17
|
+
# @option options [Boolean] :quiet (SeedDo.quiet) If true, output will be silenced
|
18
|
+
# @option options [Boolean] :insert_only (false) If true then existing records which match the
|
19
|
+
# constraints will not be updated, even if the seed data has changed
|
20
|
+
def initialize(model_class, constraints, data, options = {})
|
21
|
+
@model_class = model_class
|
22
|
+
@constraints = constraints.to_a.empty? ? [:id] : constraints
|
23
|
+
@data = data.to_a || []
|
24
|
+
@options = options.symbolize_keys
|
25
|
+
|
26
|
+
@options[:quiet] ||= SeedDo.quiet
|
27
|
+
|
28
|
+
validate_constraints!
|
29
|
+
validate_data!
|
30
|
+
end
|
31
|
+
|
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
|
+
records = @model_class.transaction do
|
36
|
+
@data.map { |record_data| seed_record(record_data.symbolize_keys) }
|
37
|
+
end
|
38
|
+
update_id_sequence
|
39
|
+
records
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def validate_constraints!
|
45
|
+
unknown_columns = @constraints.map(&:to_s) - @model_class.column_names
|
46
|
+
unless unknown_columns.empty?
|
47
|
+
raise(ArgumentError,
|
48
|
+
"Your seed constraints contained unknown columns: #{column_list(unknown_columns)}. " +
|
49
|
+
"Valid columns are: #{column_list(@model_class.column_names)}.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_data!
|
54
|
+
raise ArgumentError, "Seed data missing" if @data.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
def column_list(columns)
|
58
|
+
'`' + columns.join("`, `") + '`'
|
59
|
+
end
|
60
|
+
|
61
|
+
def seed_record(data)
|
62
|
+
record = find_or_initialize_record(data)
|
63
|
+
return if @options[:insert_only] && !record.new_record?
|
64
|
+
|
65
|
+
puts " - #{@model_class} #{data.inspect}" unless @options[:quiet]
|
66
|
+
|
67
|
+
# Rails 3 or Rails 4 + rails/protected_attributes
|
68
|
+
if record.class.respond_to?(:protected_attributes) && record.class.respond_to?(:accessible_attributes)
|
69
|
+
record.assign_attributes(data, :without_protection => true)
|
70
|
+
# Rails 4 without rails/protected_attributes
|
71
|
+
else
|
72
|
+
record.assign_attributes(data)
|
73
|
+
end
|
74
|
+
record.save(:validate => false) || raise(ActiveRecord::RecordNotSaved, 'Record not saved!')
|
75
|
+
record
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_or_initialize_record(data)
|
79
|
+
@model_class.where(constraint_conditions(data)).take ||
|
80
|
+
@model_class.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def constraint_conditions(data)
|
84
|
+
Hash[@constraints.map { |c| [c, data[c.to_sym]] }]
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_id_sequence
|
88
|
+
if @model_class.connection.adapter_name == "PostgreSQL" or @model_class.connection.adapter_name == "PostGIS"
|
89
|
+
return if @model_class.primary_key.nil? || @model_class.sequence_name.nil?
|
90
|
+
|
91
|
+
quoted_id = @model_class.connection.quote_column_name(@model_class.primary_key)
|
92
|
+
sequence = @model_class.sequence_name
|
93
|
+
|
94
|
+
# TODO postgresql_version was made public in Rails 5.0.0, remove #send when support for earlier versions are dropped
|
95
|
+
if @model_class.connection.send(:postgresql_version) >= 100000
|
96
|
+
sql =<<-EOS
|
97
|
+
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)
|
98
|
+
EOS
|
99
|
+
else
|
100
|
+
sql =<<-EOS
|
101
|
+
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)
|
102
|
+
EOS
|
103
|
+
end
|
104
|
+
|
105
|
+
@model_class.connection.execute sql
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module SeedDo
|
2
|
+
# {Writer} is used to programmatically generated seed files. For example, you might want to write
|
3
|
+
# a script which converts data in a CSV file to a valid Seed Do seed file, which can then be
|
4
|
+
# imported.
|
5
|
+
#
|
6
|
+
# @example Basic usage
|
7
|
+
# SeedDo::Writer.write('path/to/file.rb', :class_name => 'Person', :constraints => [:first_name, :last_name]) do |writer|
|
8
|
+
# writer.add(:first_name => 'Jon', :last_name => 'Smith', :age => 21)
|
9
|
+
# writer.add(:first_name => 'Emily', :last_name => 'McDonald', :age => 24)
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# # Writes the following to the file:
|
13
|
+
# #
|
14
|
+
# # Person.seed(:first_name, :last_name,
|
15
|
+
# # {:first_name=>"Jon", :last_name=>"Smith", :age=>21},
|
16
|
+
# # {:first_name=>"Emily", :last_name=>"McDonald", :age=>24}
|
17
|
+
# # )
|
18
|
+
class Writer
|
19
|
+
cattr_accessor :default_options
|
20
|
+
@@default_options = {
|
21
|
+
:chunk_size => 100,
|
22
|
+
:constraints => [:id],
|
23
|
+
:seed_type => :seed
|
24
|
+
}
|
25
|
+
|
26
|
+
# @param [Hash] options
|
27
|
+
# @option options [String] :class_name *Required* The name of the Active Record model to
|
28
|
+
# generate seeds for
|
29
|
+
# @option options [Fixnum] :chunk_size (100) The number of seeds to write before generating a
|
30
|
+
# `# BREAK EVAL` line. (Chunking reduces memory usage when loading seeds.)
|
31
|
+
# @option options [:seed, :seed_once] :seed_type (:seed) The method to use when generating
|
32
|
+
# seeds. See {ActiveRecordExtension} for details.
|
33
|
+
# @option options [Array<Symbol>] :constraints ([:id]) The constraining attributes for the seeds
|
34
|
+
def initialize(options = {})
|
35
|
+
@options = self.class.default_options.merge(options)
|
36
|
+
raise ArgumentError, "missing option :class_name" unless @options[:class_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates a new instance of {Writer} with the `options`, and then calls {#write} with the
|
40
|
+
# `io_or_filename` and `block`
|
41
|
+
def self.write(io_or_filename, options = {}, &block)
|
42
|
+
new(options).write(io_or_filename, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Writes the necessary headers and footers, and yields to a block within which the actual
|
46
|
+
# seed data should be writting using the `#<<` method.
|
47
|
+
#
|
48
|
+
# @param [IO] io_or_filename The IO to which writes will be made. (If an `IO` is given, it is
|
49
|
+
# your responsibility to close it after writing.)
|
50
|
+
# @param [String] io_or_filename The filename of a file to make writes to. (Will be opened and
|
51
|
+
# closed automatically.)
|
52
|
+
# @yield [self] make calls to `#<<` within the block
|
53
|
+
def write(io_or_filename, &block)
|
54
|
+
raise ArgumentError, "missing block" unless block_given?
|
55
|
+
|
56
|
+
if io_or_filename.respond_to?(:write)
|
57
|
+
write_to_io(io_or_filename, &block)
|
58
|
+
else
|
59
|
+
File.open(io_or_filename, 'w') do |file|
|
60
|
+
write_to_io(file, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add a seed. Must be called within a block passed to {#write}.
|
66
|
+
# @param [Hash] seed The attributes for the seed
|
67
|
+
def <<(seed)
|
68
|
+
raise "You must add seeds inside a SeedDo::Writer#write block" unless @io
|
69
|
+
|
70
|
+
buffer = ''
|
71
|
+
|
72
|
+
if chunk_this_seed?
|
73
|
+
buffer << seed_footer
|
74
|
+
buffer << "# BREAK EVAL\n"
|
75
|
+
buffer << seed_header
|
76
|
+
end
|
77
|
+
|
78
|
+
buffer << ",\n"
|
79
|
+
buffer << ' ' + seed.inspect
|
80
|
+
|
81
|
+
@io.write(buffer)
|
82
|
+
|
83
|
+
@count += 1
|
84
|
+
end
|
85
|
+
alias_method :add, :<<
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def write_to_io(io)
|
90
|
+
@io, @count = io, 0
|
91
|
+
@io.write(file_header)
|
92
|
+
@io.write(seed_header)
|
93
|
+
yield(self)
|
94
|
+
@io.write(seed_footer)
|
95
|
+
@io.write(file_footer)
|
96
|
+
ensure
|
97
|
+
@io, @count = nil, nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def file_header
|
101
|
+
<<-END
|
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
|
+
END
|
111
|
+
end
|
112
|
+
|
113
|
+
def file_footer
|
114
|
+
<<-END
|
115
|
+
# End auto-generated file.
|
116
|
+
END
|
117
|
+
end
|
118
|
+
|
119
|
+
def seed_header
|
120
|
+
constraints = @options[:constraints] && @options[:constraints].map(&:inspect).join(', ')
|
121
|
+
"#{@options[:class_name]}.#{@options[:seed_type]}(#{constraints}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def seed_footer
|
125
|
+
"\n)\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
def chunk_this_seed?
|
129
|
+
@count != 0 && (@count % @options[:chunk_size]) == 0
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/seed-do.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
3
|
+
require 'seed-do/railtie' if defined?(Rails) && Rails.version >= "3"
|
4
|
+
|
5
|
+
module SeedDo
|
6
|
+
autoload :VERSION, 'seed-do/version'
|
7
|
+
autoload :Seeder, 'seed-do/seeder'
|
8
|
+
autoload :ActiveRecordExtension, 'seed-do/active_record_extension'
|
9
|
+
autoload :BlockHash, 'seed-do/block_hash'
|
10
|
+
autoload :Runner, 'seed-do/runner'
|
11
|
+
autoload :Writer, 'seed-do/writer'
|
12
|
+
|
13
|
+
mattr_accessor :quiet
|
14
|
+
|
15
|
+
# Set `SeedDo.quiet = true` to silence all output
|
16
|
+
@@quiet = false
|
17
|
+
|
18
|
+
mattr_accessor :fixture_paths
|
19
|
+
|
20
|
+
# Set this to be an array of paths to directories containing your seed files. If used as a Rails
|
21
|
+
# plugin, SeedDo will set to to contain `Rails.root/db/fixtures` and
|
22
|
+
# `Rails.root/db/fixtures/Rails.env`
|
23
|
+
@@fixture_paths = ['db/fixtures']
|
24
|
+
|
25
|
+
# Load seed data from files
|
26
|
+
# @param [Array] fixture_paths The paths to look for seed files in
|
27
|
+
# @param [Regexp] filter If given, only filenames matching this expression will be loaded
|
28
|
+
def self.seed(fixture_paths = SeedDo.fixture_paths, filter = nil)
|
29
|
+
Runner.new(fixture_paths, filter).run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @public
|
34
|
+
class ActiveRecord::Base
|
35
|
+
extend SeedDo::ActiveRecordExtension
|
36
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'seed-do'
|
2
|
+
|
3
|
+
namespace :db do
|
4
|
+
desc <<-EOS
|
5
|
+
Loads seed data for the current environment. It will look for
|
6
|
+
ruby seed files in <RAILS_ROOT>/db/fixtures/ and
|
7
|
+
<RAILS_ROOT>/db/fixtures/<RAILS_ENV>/.
|
8
|
+
|
9
|
+
By default it will load any ruby files found. You can filter the files
|
10
|
+
loaded by passing in the FILTER environment variable with a comma-delimited
|
11
|
+
list of patterns to include. Any files not matching the pattern will
|
12
|
+
not be loaded.
|
13
|
+
|
14
|
+
You can also change the directory where seed files are looked for
|
15
|
+
with the FIXTURE_PATH environment variable.
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
# default, to load all seed files for the current environment
|
19
|
+
rake db:seed_do
|
20
|
+
|
21
|
+
# to load seed files matching orders or customers
|
22
|
+
rake db:seed_do FILTER=orders,customers
|
23
|
+
|
24
|
+
# to load files from RAILS_ROOT/features/fixtures
|
25
|
+
rake db:seed_do FIXTURE_PATH=features/fixtures
|
26
|
+
EOS
|
27
|
+
task :seed_do => :environment do
|
28
|
+
if ENV["FILTER"]
|
29
|
+
filter = /#{ENV["FILTER"].gsub(/,/, "|")}/
|
30
|
+
end
|
31
|
+
|
32
|
+
if ENV["FIXTURE_PATH"]
|
33
|
+
fixture_paths = [ENV["FIXTURE_PATH"], ENV["FIXTURE_PATH"] + '/' + Rails.env]
|
34
|
+
end
|
35
|
+
|
36
|
+
SeedDo.seed(fixture_paths, filter)
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: seed-do
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shinichi Maeshima
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: activerecord
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '7.1'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '7.1'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activesupport
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7.1'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '7.1'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rspec
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
description: Seed Do is an attempt to once and for all solve the problem of inserting
|
55
|
+
and maintaining seed data in a database. It uses a variety of techniques gathered
|
56
|
+
from various places around the web and combines them to create what is hopefully
|
57
|
+
the most robust seed data system around.
|
58
|
+
email:
|
59
|
+
- netwillnet@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- lib/seed-do.rb
|
67
|
+
- lib/seed-do/active_record_extension.rb
|
68
|
+
- lib/seed-do/block_hash.rb
|
69
|
+
- lib/seed-do/capistrano.rb
|
70
|
+
- lib/seed-do/capistrano3.rb
|
71
|
+
- lib/seed-do/railtie.rb
|
72
|
+
- lib/seed-do/runner.rb
|
73
|
+
- lib/seed-do/seeder.rb
|
74
|
+
- lib/seed-do/version.rb
|
75
|
+
- lib/seed-do/writer.rb
|
76
|
+
- lib/tasks/seed_do.rake
|
77
|
+
- lib/tasks/seed_do_capistrano3.rake
|
78
|
+
homepage: http://github.com/willnet/seed-do
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.2'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubygems_version: 3.6.7
|
97
|
+
specification_version: 4
|
98
|
+
summary: Easily manage seed data in your Active Record application
|
99
|
+
test_files: []
|