mbleigh-seed-fu 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,149 @@
1
+ = Seed Fu
2
+
3
+ Seed Fu is an attempt to once and for all solve the problem of inserting and
4
+ maintaining seed data in a database. It uses a variety of techniques gathered
5
+ from various places around the web and combines them to create what is
6
+ hopefully the most robust seed data system around.
7
+
8
+
9
+ == Simple Usage
10
+
11
+ Seed data is taken from the <tt>db/fixtures</tt> directory. Simply make descriptive .rb
12
+ files in that directory (they can be named anything, but users.rb for the User
13
+ model seed data makes sense, etc.). These scripts will be run whenever the
14
+ db:seed rake task is called, and in order (you can use <tt>00_first.rb</tt>,
15
+ <tt>00_second.rb</tt>, etc). You can put arbitrary Ruby code in these files,
16
+ but remember that it will be executed every time the rake task is called, so
17
+ it needs to be runnable multiple times on the same database.
18
+
19
+ You can also have environment-specific seed data placed in
20
+ <tt>db/fixtures/ENVIRONMENT</tt> that will only be loaded if that is the current
21
+ environment.
22
+
23
+ Let's say we want to populate a few default users. In <tt>db/fixtures/users.rb</tt> we
24
+ write the following code:
25
+
26
+ User.seed(:login, :email) do |s|
27
+ s.login = "bob"
28
+ s.email = "bob@bobson.com"
29
+ s.first_name = "Bob"
30
+ s.last_name = "Bobson"
31
+ end
32
+
33
+ User.seed(:login, :email) do |s|
34
+ s.login = "bob"
35
+ s.email = "bob@stevenson.com"
36
+ s.first_name = "Bob"
37
+ s.last_name = "Stevenson"
38
+ end
39
+
40
+ That's all you have to do! You will now have two users created in the system
41
+ and you can change their first and last names in the users.rb file and it will
42
+ be updated as such.
43
+
44
+ The arguments passed to the <tt><ActiveRecord>.seed</tt> method are the constraining
45
+ attributes: these must remain unchanged between db:seed calls to avoid data
46
+ duplication. By default, seed data will constrain to the id like so:
47
+
48
+ Category.seed do |s|
49
+ s.id = 1
50
+ s.name = "Buttons"
51
+ end
52
+
53
+ Category.seed do |s|
54
+ s.id = 2
55
+ s.name = "Bobbins"
56
+ s.parent_id = 1
57
+ end
58
+
59
+ Note that any constraints that are passed in must be present in the subsequent
60
+ seed data setting.
61
+
62
+ == Seed-many Usage
63
+
64
+ The default <tt>.seed` syntax is very verbose. An alternative is the `.seed_many</tt> syntax.
65
+ Look at this refactoring of the first seed usage example above:
66
+
67
+ User.seed_many(:login, :email, [
68
+ { :login => "bob", :email => "bob@bobson.com", :first_name => "Bob", :last_name = "Bobson" },
69
+ { :login => "bob", :email => "bob@stevenson.com", :first_name => "Bob", :last_name = "Stevenson" }
70
+ ])
71
+
72
+ Not as pretty, but much more concise.
73
+
74
+ == Handling Large SeedFu Files
75
+
76
+ Seed files can be huge. To handle large files (over a million rows), try these tricks:
77
+
78
+ * Gzip your fixtures. Seed Fu will read .rb.gz files happily. <tt>gzip -9</tt> gives the
79
+ best compression, and with Seed Fu's repetitive syntax, a 160M file can shrink to 16M.
80
+ * Add lines reading <tt># BREAK EVAL</tt> in your big fixtures, and Seed Fu will avoid loading
81
+ the whole file into memory. If you use SeedFu::Writer, these breaks are built into
82
+ your generated fixtures.
83
+ * Load a single fixture with the <tt>SEED</tt> environment varialble:
84
+ <tt>SEED=cities,states rake db:seed > seed_log`. Each argument to `SEED</tt> is used as a
85
+ regex to filter fixtures by filename.
86
+
87
+ == Generating SeedFu Files
88
+
89
+ Say you have a CSV you need to massage and store as seed files. You can create
90
+ an import script using SeedFu::Writer.
91
+
92
+ #!/usr/bin/env ruby
93
+ #
94
+ # This is: script/generate_cities_seed_from_csv
95
+ #
96
+ require 'rubygems'
97
+ require 'fastercsv'
98
+ require File.join( File.dirname(__FILE__), '..', 'vendor/plugins/seed-fu/lib/seed-fu/writer' )
99
+
100
+ # Maybe SEEF_FILE could be $stdout, hm.
101
+ #
102
+ CITY_CSV = File.join( File.dirname(__FILE__), '..', 'city.csv' )
103
+ SEED_FILE = File.join( File.dirname(__FILE__), '..', 'db', 'fixtures', 'cities.rb' )
104
+
105
+ # Create a seed_writer, walk the CSV, add to the file.
106
+ #
107
+
108
+ seed_writer = SeedFu::Writer::SeedMany.new(
109
+ :seed_file => SEED_FILE,
110
+ :seed_model => 'City',
111
+ :seed_by => [ :city, :state ]
112
+ )
113
+
114
+ FasterCSV.foreach( CITY_CSV,
115
+ :return_headers => false,
116
+ :headers => :first_row
117
+ ) do |row|
118
+
119
+ # Skip all but the US
120
+ #
121
+ next unless row['country_code'] == 'US'
122
+
123
+ unless us_state
124
+ puts "No State Match for #{row['region_name']}"
125
+ next
126
+ end
127
+
128
+ # Write the seed
129
+ #
130
+ seed_writer.add_seed({
131
+ :zip => row['zipcode'],
132
+ :state => row['state'],
133
+ :city => row['city'],
134
+ :latitude => row['latitude'],
135
+ :longitude => row['longitude']
136
+ })
137
+
138
+ end
139
+
140
+ seed_writer.finish
141
+
142
+ There is also a SeedFu::Writer::Seed in case you prefere the seed()
143
+ syntax over the seen_many() syntax. Easy-peasy.
144
+
145
+ == Contributors
146
+
147
+ * Thanks to Matthew Beale for his great work in adding the writer, making it faster and better.
148
+
149
+ Copyright (c) 2008-2009 Michael Bleigh released under the MIT license
data/Rakefile CHANGED
@@ -2,5 +2,22 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "seed-fu"
9
+ gemspec.summary = "Allows easier database seeding of tables in Rails."
10
+ gemspec.email = "michael@intridea.com"
11
+ gemspec.homepage = "http://github.com/mblegih/seed-fu"
12
+ gemspec.description = "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."
13
+ gemspec.authors = ["Michael Bleigh"]
14
+ gemspec.add_dependency 'rails', '>= 2.1'
15
+ gemspec.files = FileList["[A-Z]*", "{lib,spec,rails}/**/*"] - FileList["**/*.log"]
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+
5
22
  desc 'Default: run specs.'
6
23
  task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.2.1
data/lib/seed-fu.rb CHANGED
@@ -25,24 +25,21 @@ module SeedFu
25
25
  end
26
26
  end
27
27
 
28
- def set_attribute(name, value)
29
- @data[name.to_sym] = value
30
- end
31
-
32
28
  def plant! insert_only=false
33
29
  record = get
34
30
  return if !record.new_record? and insert_only
35
31
  @data.each do |k, v|
36
32
  record.send("#{k}=", v)
37
33
  end
38
- record.save!
34
+ raise "Error Saving: #{record.inspect}" unless record.save(false)
39
35
  puts " - #{@model_class} #{condition_hash.inspect}"
40
36
  record
41
37
  end
42
38
 
43
39
  def method_missing(method_name, *args) #:nodoc:
44
- if (match = method_name.to_s.match(/(.*)=$/)) && args.size == 1
45
- set_attribute(match[1], args.first)
40
+ if args.size == 1 and (match = method_name.to_s.match(/(.*)=$/))
41
+ self.class.class_eval "def #{method_name} arg; @data[:#{match[1]}] = arg; end"
42
+ send(method_name, args[0])
46
43
  else
47
44
  super
48
45
  end
@@ -61,7 +58,7 @@ module SeedFu
61
58
  end
62
59
 
63
60
  def condition_hash
64
- @data.reject{|a,v| !@constraints.include?(a)}
61
+ @constraints.inject({}) {|a,c| a[c] = @data[c]; a }
65
62
  end
66
63
  end
67
64
  end
@@ -87,4 +84,4 @@ class ActiveRecord::Base
87
84
  end
88
85
  end
89
86
  end
90
- end
87
+ end
@@ -0,0 +1,3 @@
1
+ require File.join( File.dirname(__FILE__), 'writer', 'abstract' )
2
+ require File.join( File.dirname(__FILE__), 'writer', 'seed' )
3
+ require File.join( File.dirname(__FILE__), 'writer', 'seed_many' )
@@ -0,0 +1,58 @@
1
+ module SeedFu
2
+
3
+ module Writer
4
+
5
+ class Abstract
6
+ attr_accessor :seed_handle, :config, :number_of_seeds
7
+
8
+ def initialize(options={})
9
+ self.config = options
10
+ self.number_of_seeds = 0
11
+
12
+ self.seed_handle = File.new(self.config[:seed_file], 'w')
13
+
14
+ write_header
15
+ end
16
+
17
+ def header
18
+ <<-END
19
+ # DO NOT MODIFY THIS FILE, it was auto-generated.
20
+ #
21
+ # Date: #{DateTime.now}
22
+ # Using #{self.class} to seed #{config[:seed_model]}
23
+ # Written with the command:
24
+ #
25
+ # #{$0} #{$*.join}
26
+ #
27
+ END
28
+ end
29
+
30
+ def footer
31
+ <<-END
32
+ # End auto-generated file.
33
+ END
34
+ end
35
+
36
+ def add_seed(hash)
37
+ $stdout.puts "Added #{hash.inspect}" unless config[:quiet]
38
+ self.number_of_seeds += 1
39
+ end
40
+
41
+ def write_header
42
+ seed_handle.syswrite header
43
+ end
44
+
45
+ def write_footer
46
+ seed_handle.syswrite footer
47
+ end
48
+
49
+ def finish
50
+ write_footer
51
+ seed_handle.close
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,26 @@
1
+ module SeedFu
2
+
3
+ module Writer
4
+
5
+ class Seed < Abstract
6
+
7
+ # This method uses the :seed_by set earlier.
8
+ #
9
+ def add_seed(hash, seed_by=nil)
10
+ seed_by ||= config[:seed_by]
11
+ seed_handle.syswrite( <<-END
12
+ #{config[:seed_model]}.seed(#{seed_by.collect{|s| ":#{s}"}.join(',')}) { |s|
13
+ #{hash.collect{|k,v| " s.#{k} = '#{v.to_s.gsub("'", "\'")}'\n"}.join}}
14
+ END
15
+ )
16
+ super(hash)
17
+ if chunk_this_seed?
18
+ seed_handle.syswrite "# BREAK EVAL\n"
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,52 @@
1
+ module SeedFu
2
+
3
+ module Writer
4
+
5
+ class SeedMany < Abstract
6
+
7
+ def seed_many_header
8
+ "#{config[:seed_model]}.seed_many(#{config[:seed_by].collect{|s| ":#{s}"}.join(',')},["
9
+ end
10
+
11
+ def seed_many_footer
12
+ "\n])\n"
13
+ end
14
+
15
+ # Chunk in groups of 100 for performance
16
+ #
17
+ def chunk_this_seed?
18
+ 0 == (self.number_of_seeds % (config[:chunk_size] || 100))
19
+ end
20
+
21
+ def add_seed(hash)
22
+ seed_handle.syswrite( (<<-END
23
+ #{',' unless self.number_of_seeds == 0 or chunk_this_seed?}
24
+ { #{hash.collect{|k,v| ":#{k} => '#{v.to_s.gsub("'", "\'")}'"}.join(', ')} }
25
+ END
26
+ ).chomp )
27
+ super(hash)
28
+
29
+ if chunk_this_seed?
30
+ seed_handle.syswrite(
31
+ self.seed_many_footer +
32
+ "# BREAK EVAL\n" +
33
+ self.seed_many_header
34
+ )
35
+ end
36
+ end
37
+
38
+ def write_header
39
+ super
40
+ seed_handle.syswrite self.seed_many_header
41
+ end
42
+
43
+ def write_footer
44
+ seed_handle.syswrite self.seed_many_footer
45
+ super
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbleigh-seed-fu
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-16 00:00:00 -07:00
12
+ date: 2009-09-23 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -29,25 +29,26 @@ executables: []
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
- - README
32
+ - README.rdoc
33
33
  files:
34
- - README
34
+ - README.rdoc
35
35
  - Rakefile
36
- - init.rb
36
+ - VERSION
37
37
  - lib/seed-fu.rb
38
- - lib/autotest/discover.rb
38
+ - lib/seed-fu/writer.rb
39
+ - lib/seed-fu/writer/abstract.rb
40
+ - lib/seed-fu/writer/seed.rb
41
+ - lib/seed-fu/writer/seed_many.rb
39
42
  - rails/init.rb
40
- - seed-fu.gemspec
41
43
  - spec/schema.rb
42
44
  - spec/seed_fu_spec.rb
43
45
  - spec/spec_helper.rb
44
- - tasks/seed_fu_tasks.rake
45
- has_rdoc: true
46
- homepage: http://github.com/mbleigh/seed-fu
46
+ has_rdoc: false
47
+ homepage: http://github.com/mblegih/seed-fu
48
+ licenses:
47
49
  post_install_message:
48
50
  rdoc_options:
49
- - --main
50
- - README
51
+ - --charset=UTF-8
51
52
  require_paths:
52
53
  - lib
53
54
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -65,9 +66,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
66
  requirements: []
66
67
 
67
68
  rubyforge_project:
68
- rubygems_version: 1.2.0
69
+ rubygems_version: 1.3.5
69
70
  signing_key:
70
- specification_version: 2
71
- summary: Allows easier database seeding of tables.
72
- test_files: []
73
-
71
+ specification_version: 3
72
+ summary: Allows easier database seeding of tables in Rails.
73
+ test_files:
74
+ - spec/schema.rb
75
+ - spec/seed_fu_spec.rb
76
+ - spec/spec_helper.rb
data/README DELETED
@@ -1,66 +0,0 @@
1
- Seed Fu
2
- =======
3
-
4
- Seed Fu is an attempt to once and for all solve the problem of inserting and
5
- maintaining seed data in a database. It uses a variety of techniques gathered
6
- from various places around the web and combines them to create what is
7
- hopefully the most robust seed data system around.
8
-
9
- If you have suggestions or come across problems, please report them on
10
- the Lighthouse project for Seed Fu:
11
- http://mbleigh.lighthouseapp.com/projects/10223-seed-fu/overview
12
-
13
- Usage
14
- =======
15
-
16
- Seed data is taken from the db/fixtures directory. Simply make descriptive .rb
17
- files in that directory (they can be named anything, but users.rb for the User
18
- model seed data makes sense, etc.). These scripts will be run whenever the
19
- db:seed rake task is called. You can put arbitrary Ruby code in these files,
20
- but remember that it will be executed every time the rake task is called, so
21
- it needs to be runnable multiple times on the same database.
22
-
23
- You can also have environment-specific seed data placed in
24
- db/fixtures/ENVIRONMENT that will only be loaded if that is the current
25
- environment.
26
-
27
- Let's say we want to populate a few default users. In db/fixtures/users.rb we
28
- write the following code:
29
-
30
- User.seed(:login, :email) do |s|
31
- s.login = "bob"
32
- s.email = "bob@bobson.com"
33
- s.first_name = "Bob"
34
- s.last_name = "Bobson"
35
- end
36
-
37
- User.seed(:login, :email) do |s|
38
- s.login = "bob"
39
- s.email = "bob@stevenson.com"
40
- s.first_name = "Bob"
41
- s.last_name = "Stevenson"
42
- end
43
-
44
- That's all you have to do! You will now have two users created in the system
45
- and you can change their first and last names in the users.rb file and it will
46
- be updated as such.
47
-
48
- The arguments passed to the <ActiveRecord>.seed method are the constraining
49
- attributes: these must remain unchanged between db:seed calls to avoid data
50
- duplication. By default, seed data will constrain to the id like so:
51
-
52
- Category.seed do |s|
53
- s.id = 1
54
- s.name = "Buttons"
55
- end
56
-
57
- Category.seed do |s|
58
- s.id = 2
59
- s.name = "Bobbins"
60
- s.parent_id = 1
61
- end
62
-
63
- Note that any constraints that are passed in must be present in the subsequent
64
- seed data setting.
65
-
66
- Copyright (c) 2008 Michael Bleigh, released under the MIT license
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require File.dirname(__FILE__) + "/rails/init"
data/seed-fu.gemspec DELETED
@@ -1,31 +0,0 @@
1
- Gem::Specification.new do |s|
2
- s.name = 'seed-fu'
3
- s.version = '1.0.0'
4
- s.date = '2008-08-16'
5
-
6
- s.summary = "Allows easier database seeding of tables."
7
- s.description = "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."
8
-
9
- s.authors = ["Michael Bleigh"]
10
- s.email = "michael@intridea.com"
11
- s.homepage = 'http://github.com/mbleigh/seed-fu'
12
-
13
- s.has_rdoc = true
14
- s.rdoc_options = ["--main", "README"]
15
- s.extra_rdoc_files = ["README"]
16
-
17
- s.add_dependency 'rails', ['>= 2.1']
18
-
19
- s.files = ["README",
20
- "Rakefile",
21
- "init.rb",
22
- "lib/seed-fu.rb",
23
- "lib/autotest/discover.rb",
24
- "rails/init.rb",
25
- "seed-fu.gemspec",
26
- "spec/schema.rb",
27
- "spec/seed_fu_spec.rb",
28
- "spec/spec_helper.rb",
29
- "tasks/seed_fu_tasks.rake"]
30
-
31
- end
@@ -1,44 +0,0 @@
1
- namespace :db do
2
- desc <<-EOS
3
- Loads seed data for the current environment. It will look for
4
- ruby seed files in <RAILS_ROOT>/db/fixtures/ and
5
- <RAILS_ROOT>/db/fixtures/<RAILS_ENV>/.
6
-
7
- By default it will load any ruby files found. You can filter the files
8
- loaded by passing in the SEED environment variable with a comma-delimited
9
- list of patterns to include. Any files not matching the pattern will
10
- not be loaded.
11
-
12
- You can also change the directory where seed files are looked for
13
- with the FIXTURE_PATH environment variable.
14
-
15
- Examples:
16
- # default, to load all seed files for the current environment
17
- rake db:seed
18
-
19
- # to load seed files matching orders or customers
20
- rake db:seed SEED=orders,customers
21
-
22
- # to load files from RAILS_ROOT/features/fixtures
23
- rake db:seed FIXTURE_PATH=features/fixtures
24
- EOS
25
- task :seed => :environment do
26
- fixture_path = ENV["FIXTURE_PATH"] ? ENV["FIXTURE_PATH"] : "db/fixtures"
27
-
28
- global_seed_files = Dir[File.join(RAILS_ROOT, fixture_path, '*.rb')].sort
29
- env_specific_seed_files = Dir[File.join(RAILS_ROOT, fixture_path, RAILS_ENV, '*.rb')]
30
- potential_seed_files = (global_seed_files + env_specific_seed_files).uniq
31
-
32
- if ENV["SEED"]
33
- filter = ENV["SEED"].gsub(/,/, "|")
34
- potential_seed_files.reject!{ |file| true unless file =~ /#{filter}/ }
35
- puts "\n == Filtering seed files against regexp: #{filter}"
36
- end
37
-
38
- potential_seed_files.each do |file|
39
- pretty_name = file.sub("#{RAILS_ROOT}/", "")
40
- puts "\n== Seed from #{pretty_name} " + ("=" * (60 - (17 + File.split(file).last.length)))
41
- load file
42
- end
43
- end
44
- end