seed-fu-discourse 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +63 -0
- data/LICENSE +20 -0
- data/README.md +158 -0
- data/lib/seed-fu.rb +36 -0
- data/lib/seed-fu/active_record_extension.rb +65 -0
- data/lib/seed-fu/block_hash.rb +21 -0
- data/lib/seed-fu/railtie.rb +14 -0
- data/lib/seed-fu/runner.rb +73 -0
- data/lib/seed-fu/seeder.rb +104 -0
- data/lib/seed-fu/version.rb +4 -0
- data/lib/seed-fu/writer.rb +132 -0
- data/lib/tasks/seed_fu.rake +38 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a80f7da73d20fcbc8d6388437e4581ec8b247b1e
|
4
|
+
data.tar.gz: beea3fef73d7dd503ee18e0713ef758767bed09e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc9b5fbb3f30e4426c2600858f93a1dd40f57aafd285d44ee22d5314eaf93929e863cbfd81df07f1424975ca1dd5b3d47d7323484ba52dd5b1b6a1745e19eba6
|
7
|
+
data.tar.gz: ab52d4e1cdf368d7d5efd73550e23cd05c48a72df61119a9b97dc40352a1104b147dfddf34aff0c4a6fb696d4156575d6510964361fb195cd7cf1ee62f0d51cc
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
Unreleased
|
2
|
+
----------
|
3
|
+
|
4
|
+
Features:
|
5
|
+
|
6
|
+
* Rails 4.0.X support added. (by @tkhr, @DanielWright)
|
7
|
+
|
8
|
+
Version 2.1.0
|
9
|
+
-------------
|
10
|
+
|
11
|
+
Features:
|
12
|
+
|
13
|
+
* Deprecations removed
|
14
|
+
|
15
|
+
* Rails 3.1 support added, Rails 3.0 support removed (please use 2.0.X line with 3.0)
|
16
|
+
|
17
|
+
Version 2.0.1
|
18
|
+
-------------
|
19
|
+
|
20
|
+
Bug fixes:
|
21
|
+
|
22
|
+
* Update the primary key sequence in PostgreSQL tables after seeding data. This ensures that id conflicts do not occur when records are subsequently added to the table.
|
23
|
+
|
24
|
+
* Raise ActiveRecord::RecordNotSaved if any of the saves fail (but they won't fail due to validation since saves are done without validation, so this guards against callbacks failing etc.)
|
25
|
+
|
26
|
+
Version 2.0.0
|
27
|
+
-------------
|
28
|
+
|
29
|
+
Features:
|
30
|
+
|
31
|
+
* Depends only on Active Record, not the whole of Rails
|
32
|
+
|
33
|
+
* The `Model.seed_many` syntax is now supported by `Model.seed`, and `Model.seed_many` is deprecated
|
34
|
+
|
35
|
+
* `Model.seed` supports adding multiple records without an explicit array argument. I.e. the following are equivalent:
|
36
|
+
|
37
|
+
Model.seed([
|
38
|
+
{ :name => "Jon" },
|
39
|
+
{ :name => "Emily" }
|
40
|
+
])
|
41
|
+
|
42
|
+
Model.seed(
|
43
|
+
{ :name => "Jon" },
|
44
|
+
{ :name => "Emily }
|
45
|
+
)
|
46
|
+
|
47
|
+
* A side-effect of the above is another option for single seeds:
|
48
|
+
|
49
|
+
Model.seed(:name => "Jon")
|
50
|
+
|
51
|
+
* The `SEED` option to `rake db:seed_fu` is deprecated, and replaced by `FILTER` which works the same way.
|
52
|
+
|
53
|
+
* Added `SeedFu.quiet` boolean option, set to `true` if you don't want any output from Seed Fu.
|
54
|
+
|
55
|
+
* Added `SeedFu.fixture_paths`. Set to an array of paths to look for seed files in. Defaults to `["db/fixtures"]` in general, or `["#{Rails.root}/db/fixtures", "#{Rails.root}/db/fixtures/#{Rails.env}"]` when Seed Fu is installed as a Rails plugin.
|
56
|
+
|
57
|
+
* Added `SeedFu.seed` method which is basically a method equivalent of running `rake db:seed_fu` (the rake task now just basically called `SeedFu.seed`)
|
58
|
+
|
59
|
+
* Simplified and changed the `SeedFu::Writer` API, see docs for details
|
60
|
+
|
61
|
+
Bug fixes:
|
62
|
+
|
63
|
+
* Fix Rails 3 deprecation warnings and make seed-fu fully compatible with being installed as a gem
|
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,158 @@
|
|
1
|
+
Seed Fu
|
2
|
+
=======
|
3
|
+
|
4
|
+
Seed Fu 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.
|
5
|
+
|
6
|
+
Warning: API Changes
|
7
|
+
--------------------
|
8
|
+
|
9
|
+
Version 2.0.0 of Seed Fu introduced API changes. `Seed::Writer` was been completely overhauled and will require you to update your scripts. Some other deprecations were introduced, and support is fully removed in version 2.1.0. Please see the [CHANGELOG](CHANGELOG.md) for details.
|
10
|
+
|
11
|
+
The API documentation is available in full at [http://rubydoc.info/github/mbleigh/seed-fu/master/frames](http://rubydoc.info/github/mbleigh/seed-fu/master/frames).
|
12
|
+
|
13
|
+
Basic Example
|
14
|
+
-------------
|
15
|
+
|
16
|
+
### In `db/fixtures/users.rb`
|
17
|
+
|
18
|
+
User.seed do |s|
|
19
|
+
s.id = 1
|
20
|
+
s.login = "jon"
|
21
|
+
s.email = "jon@example.com"
|
22
|
+
s.name = "Jon"
|
23
|
+
end
|
24
|
+
|
25
|
+
User.seed do |s|
|
26
|
+
s.id = 2
|
27
|
+
s.login = "emily"
|
28
|
+
s.email = "emily@example.com"
|
29
|
+
s.name = "Emily"
|
30
|
+
end
|
31
|
+
|
32
|
+
### To load the data:
|
33
|
+
|
34
|
+
$ rake db:seed_fu
|
35
|
+
== Seed from /path/to/app/db/fixtures/users.rb
|
36
|
+
- User {:id=>1, :login=>"jon", :email=>"jon@example.com", :name=>"Jon"}
|
37
|
+
- User {:id=>2, :login=>"emily", :email=>"emily@example.com", :name=>"Emily"}
|
38
|
+
|
39
|
+
Installation
|
40
|
+
------------
|
41
|
+
|
42
|
+
### Rails 4.0
|
43
|
+
|
44
|
+
The current latest version isn't compatible with Rails 4.0.
|
45
|
+
You will have to use the HEAD of this repo.
|
46
|
+
|
47
|
+
gem 'seed-fu', github: 'mbleigh/seed-fu'
|
48
|
+
|
49
|
+
### Rails 3.1
|
50
|
+
|
51
|
+
Just add `gem 'seed-fu', '~> 2.1.0'` to your `Gemfile`
|
52
|
+
|
53
|
+
Seed Fu depends on Active Record, but doesn't have to be used with a full Rails app. Simply load and require the `seed-fu` gem and you're set.
|
54
|
+
|
55
|
+
### Rails 3.0
|
56
|
+
|
57
|
+
The current version is not backwards compatible with Rails 3.0. Please use `gem 'seed-fu', '~> 2.0.0'`.
|
58
|
+
|
59
|
+
### Rails 2.3
|
60
|
+
|
61
|
+
The current version is not backwards compatible with Rails 2.3. Please use `gem 'seed-fu', '~> 1.2.0'`.
|
62
|
+
|
63
|
+
Constraints
|
64
|
+
-----------
|
65
|
+
|
66
|
+
Constraints are used to identify seeds, so that they can be updated if necessary. For example:
|
67
|
+
|
68
|
+
Point.seed(:x, :y) do |s|
|
69
|
+
s.x = 4
|
70
|
+
s.y = 7
|
71
|
+
s.name = "Home"
|
72
|
+
end
|
73
|
+
|
74
|
+
The first time this seed is loaded, a `Point` record will be created. Now suppose the name is changed:
|
75
|
+
|
76
|
+
Point.seed(:x, :y) do |s|
|
77
|
+
s.x = 4
|
78
|
+
s.y = 7
|
79
|
+
s.name = "Work"
|
80
|
+
end
|
81
|
+
|
82
|
+
When this is run, Seed Fu 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.
|
83
|
+
|
84
|
+
If you do not want seeds to be updated after they have been created, use `seed_once`:
|
85
|
+
|
86
|
+
Point.seed_once(:x, :y) do |s|
|
87
|
+
s.x = 4
|
88
|
+
s.y = 7
|
89
|
+
s.name = "Home"
|
90
|
+
end
|
91
|
+
|
92
|
+
The default constraint just checks the `id` of the record.
|
93
|
+
|
94
|
+
Where to put seed files
|
95
|
+
-----------------------
|
96
|
+
|
97
|
+
By default, seed files are looked for in the following locations:
|
98
|
+
|
99
|
+
* `Rails.root/db/fixtures` and `Rails.root/db/fixtures/Rails.env` in a Rails app
|
100
|
+
* `db/fixtures` when loaded without Rails
|
101
|
+
|
102
|
+
You can change these defaults by modifying the `SeedFu.fixture_paths` array.
|
103
|
+
|
104
|
+
Seed files can be named whatever you like, and are loaded in alphabetical order.
|
105
|
+
|
106
|
+
Terser syntax
|
107
|
+
-------------
|
108
|
+
|
109
|
+
When loading lots of records, the above block-based syntax can be quite verbose. You can use the following instead:
|
110
|
+
|
111
|
+
User.seed(:id,
|
112
|
+
{ :id => 1, :login => "jon", :email => "jon@example.com", :name => "Jon" },
|
113
|
+
{ :id => 2, :login => "emily", :email => "emily@example.com", :name => "Emily" }
|
114
|
+
)
|
115
|
+
|
116
|
+
Rake task
|
117
|
+
---------
|
118
|
+
|
119
|
+
Seed files can be run automatically using `rake db:seed_fu`. There are two options which you can pass:
|
120
|
+
|
121
|
+
* `rake db:seed_fu FIXTURE_PATH=path/to/fixtures` -- Where to find the fixtures
|
122
|
+
* `rake db:seed_fu FILTER=users,articles` -- Only run seed files with a filename matching the `FILTER`
|
123
|
+
|
124
|
+
You can also do a similar thing in your code by calling `SeedFu.seed(fixture_paths, filter)`.
|
125
|
+
|
126
|
+
Disable output
|
127
|
+
--------------
|
128
|
+
|
129
|
+
To disable output from Seed Fu, set `SeedFu.quiet = true`.
|
130
|
+
|
131
|
+
Handling large seed files
|
132
|
+
-------------------------
|
133
|
+
|
134
|
+
Seed files can be huge. To handle large files (over a million rows), try these tricks:
|
135
|
+
|
136
|
+
* Gzip your fixtures. Seed Fu will read .rb.gz files happily. `gzip -9` gives the best compression, and with Seed Fu's repetitive syntax, a 160M file can shrink to 16M.
|
137
|
+
* Add lines reading `# BREAK EVAL` in your big fixtures, and Seed Fu will avoid loading the whole file into memory. If you use `SeedFu::Writer`, these breaks are built into your generated fixtures.
|
138
|
+
* Load a single fixture at a time with the `FILTER` environment variable
|
139
|
+
* If you don't need Seed Fu's ability to update seed with new data, then you may find that [activerecord-import](https://github.com/zdennis/activerecord-import) is faster
|
140
|
+
|
141
|
+
Generating seed files
|
142
|
+
---------------------
|
143
|
+
|
144
|
+
If you need to programmatically generate seed files, for example to convert a CSV file into a seed file, then you can use [`SeedFu::Writer`](../SeedFu/Writer).
|
145
|
+
|
146
|
+
Bugs / Feature requests
|
147
|
+
-----------------------
|
148
|
+
|
149
|
+
Please report them on [Github Issues](https://github.com/mbleigh/seed-fu/issues).
|
150
|
+
|
151
|
+
Contributors
|
152
|
+
------------
|
153
|
+
|
154
|
+
* [Michael Bleigh](http://www.mbleigh.com/) is the original author
|
155
|
+
* [Jon Leighton](http://jonathanleighton.com/) is the current maintainer
|
156
|
+
* Thanks to [Matthew Beale](https://github.com/mixonic) for his great work in adding the writer, making it faster and better.
|
157
|
+
|
158
|
+
Copyright © 2008-2010 Michael Bleigh, released under the MIT license
|
data/lib/seed-fu.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
3
|
+
require 'seed-fu/railtie' if defined?(Rails) && Rails.version >= "3"
|
4
|
+
|
5
|
+
module SeedFu
|
6
|
+
autoload :VERSION, 'seed-fu/version'
|
7
|
+
autoload :Seeder, 'seed-fu/seeder'
|
8
|
+
autoload :ActiveRecordExtension, 'seed-fu/active_record_extension'
|
9
|
+
autoload :BlockHash, 'seed-fu/block_hash'
|
10
|
+
autoload :Runner, 'seed-fu/runner'
|
11
|
+
autoload :Writer, 'seed-fu/writer'
|
12
|
+
|
13
|
+
mattr_accessor :quiet
|
14
|
+
|
15
|
+
# Set `SeedFu.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, SeedFu 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 = SeedFu.fixture_paths, filter = nil)
|
29
|
+
Runner.new(fixture_paths, filter).run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @public
|
34
|
+
class ActiveRecord::Base
|
35
|
+
extend SeedFu::ActiveRecordExtension
|
36
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module SeedFu
|
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
|
+
SeedFu::Seeder.new(self, *parse_seed_fu_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(:id, :id => 1, :name => "Harry") # => Name *not* changed
|
42
|
+
def seed_once(*args, &block)
|
43
|
+
constraints, data = parse_seed_fu_args(args, block)
|
44
|
+
SeedFu::Seeder.new(self, constraints, data, :insert_only => true).seed
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_seed_fu_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, [SeedFu::BlockHash.new(block).to_hash]]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SeedFu
|
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,14 @@
|
|
1
|
+
module SeedFu
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
load "tasks/seed_fu.rake"
|
5
|
+
end
|
6
|
+
|
7
|
+
initializer 'seed_fu.set_fixture_paths' do
|
8
|
+
SeedFu.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 SeedFu
|
5
|
+
# Runs seed files.
|
6
|
+
#
|
7
|
+
# It is not recommended to use this class directly. Instead, use {SeedFu.seed SeedFu.seed}, which creates
|
8
|
+
# an instead of {Runner} and calls {#run #run}.
|
9
|
+
#
|
10
|
+
# @see SeedFu.seed SeedFu.seed
|
11
|
+
class Runner
|
12
|
+
# @param [Array<String>] fixture_paths The paths where fixtures are located. Will use
|
13
|
+
# `SeedFu.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 || SeedFu.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 && !SeedFu.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 SeedFu.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,104 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
|
3
|
+
module SeedFu
|
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 (SeedFu.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] ||= SeedFu.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)
|
75
|
+
record
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_or_initialize_record(data)
|
79
|
+
@model_class.where(constraint_conditions(data)).first ||
|
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"
|
89
|
+
quoted_sequence = "'" + @model_class.sequence_name + "'"
|
90
|
+
next_id = (@model_class.maximum(:id) || 0) + 1
|
91
|
+
|
92
|
+
# nothing to repair, the id sequence starts at 1
|
93
|
+
if(next_id > 1)
|
94
|
+
@model_class.connection.execute(
|
95
|
+
"SELECT pg_catalog.setval(" +
|
96
|
+
"#{quoted_sequence}," +
|
97
|
+
" #{next_id}" +
|
98
|
+
");"
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module SeedFu
|
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 Fu seed file, which can then be
|
4
|
+
# imported.
|
5
|
+
#
|
6
|
+
# @example Basic usage
|
7
|
+
# SeedFu::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 SeedFu::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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'seed-fu'
|
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
|
20
|
+
|
21
|
+
# to load seed files matching orders or customers
|
22
|
+
rake db:seed FILTER=orders,customers
|
23
|
+
|
24
|
+
# to load files from RAILS_ROOT/features/fixtures
|
25
|
+
rake db:seed FIXTURE_PATH=features/fixtures
|
26
|
+
EOS
|
27
|
+
task :seed_fu => :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
|
+
SeedFu.seed(fixture_paths, filter)
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: seed-fu-discourse
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Bleigh
|
8
|
+
- Jon Leighton
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.1'
|
21
|
+
- - <
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '4.1'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.1'
|
31
|
+
- - <
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.1'
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: activesupport
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.1'
|
41
|
+
- - <
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '4.1'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '3.1'
|
51
|
+
- - <
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.1'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rspec
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: pg
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: mysql2
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: sqlite3
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Seed Fu is an attempt to once and for all solve the problem of inserting
|
111
|
+
and maintaining seed data in a database. It uses a variety of techniques gathered
|
112
|
+
from various places around the web and combines them to create what is hopefully
|
113
|
+
the most robust seed data system around.
|
114
|
+
email:
|
115
|
+
- michael@intridea.com
|
116
|
+
- j@jonathanleighton.com
|
117
|
+
executables: []
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- lib/seed-fu.rb
|
122
|
+
- lib/tasks/seed_fu.rake
|
123
|
+
- lib/seed-fu/railtie.rb
|
124
|
+
- lib/seed-fu/writer.rb
|
125
|
+
- lib/seed-fu/runner.rb
|
126
|
+
- lib/seed-fu/block_hash.rb
|
127
|
+
- lib/seed-fu/seeder.rb
|
128
|
+
- lib/seed-fu/active_record_extension.rb
|
129
|
+
- lib/seed-fu/version.rb
|
130
|
+
- LICENSE
|
131
|
+
- README.md
|
132
|
+
- CHANGELOG.md
|
133
|
+
homepage: http://github.com/mbleigh/seed-fu
|
134
|
+
licenses: []
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - '>='
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.0.14
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Easily manage seed data in your Active Record application
|
156
|
+
test_files: []
|