populator 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *0.2.1* (August 30th, 2008)
2
+
3
+ * wrap sqlite inserts in transaction to improve performance
4
+
5
+ * default created_at/on and updated_at/on columns to current time
6
+
7
+ *0.2.0* (August 30th, 2008)
8
+
9
+ * adding :per_query option to limit how many inserts are made per query
10
+
11
+ * improving performance when nesting factories
12
+
13
+ * adding Populate.sentences to generate a lot of text
14
+
15
+ * adding Populate.words to fetch some random words
16
+
17
+ *0.1.0* (August 27th, 2008)
18
+
19
+ * initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Ryan Bates
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.
@@ -0,0 +1,26 @@
1
+ CHANGELOG
2
+ lib/populator/adapters/abstract.rb
3
+ lib/populator/adapters/sqlite.rb
4
+ lib/populator/factory.rb
5
+ lib/populator/model_additions.rb
6
+ lib/populator/random.rb
7
+ lib/populator/record.rb
8
+ lib/populator.rb
9
+ LICENSE
10
+ Manifest
11
+ populator.gemspec
12
+ README
13
+ spec/database.yml
14
+ spec/example_database.yml
15
+ spec/models/category.rb
16
+ spec/models/product.rb
17
+ spec/populator/factory_spec.rb
18
+ spec/populator/model_additions_spec.rb
19
+ spec/populator/random_spec.rb
20
+ spec/populator/record_spec.rb
21
+ spec/README
22
+ spec/spec.opts
23
+ spec/spec_helper.rb
24
+ tasks/deployment.rake
25
+ tasks/spec.rake
26
+ TODO
data/README ADDED
@@ -0,0 +1,86 @@
1
+ = Populator
2
+
3
+ Populate an Active Record database with mass insert.
4
+
5
+ Special thanks to Zach Dennis for his ar-extensions gem which some of
6
+ this code is loosely based on.
7
+
8
+
9
+ == Install
10
+
11
+ Install the gem:
12
+
13
+ gem install ryanb-populator --source http://gems.github.com
14
+
15
+ And then load it in your project:
16
+
17
+ require 'populator'
18
+
19
+
20
+ == Usage
21
+
22
+ This gem adds a "populate" method to all Active Record models. Pass the
23
+ number of records you want to create along with a block. In the block
24
+ you can set the column values for each record.
25
+
26
+ Person.populate(3000) do |person|
27
+ person.first_name = "John"
28
+ person.last_name = "Smith"
29
+ end
30
+
31
+ This will do a mass insert into the database so it is very fast.
32
+ The person object contains the "id" so you can set up associations.
33
+
34
+ Person.populate(3000) do |person|
35
+ person.first_name = "John"
36
+ person.last_name = "Smith"
37
+ Project.populate(30) do |project|
38
+ project.person_id = person.id
39
+ end
40
+ end
41
+
42
+ That will create 30 projects for each person.
43
+
44
+ Passing an range or array of values will randomly select one.
45
+
46
+ Person.populate(1000..5000) do |person|
47
+ person.gender = ['male', 'female']
48
+ person.annual_income = 10000..200000
49
+ end
50
+
51
+ This will create 1000 to 5000 men or women with the annual income
52
+ between 10,000 and 200,000.
53
+
54
+ You can pass a :per_query option to limit how many records are saved
55
+ per query. This defaults to 1000.
56
+
57
+ Person.populate(2000, :per_query => 100)
58
+
59
+ If you need to generate fake data, there are a few methods to do this.
60
+
61
+ Populator.words(3) # generates 3 random words separated by spaces
62
+ Populator.words(10..20) # generates between 10 and 20 random words
63
+ Populator.sentences(5) # generates 5 sentences
64
+
65
+ For fancier data generation, try the Faker gem.
66
+
67
+ http://faker.rubyforge.org
68
+
69
+
70
+ == Important
71
+
72
+ For performance reasons, this gem does not use actual instances of the
73
+ model. This means validations and callbacks are bypassed. It is up to
74
+ you to ensure you're adding valid data.
75
+
76
+
77
+ == Development
78
+
79
+ See spec/README for instructions on running specs.
80
+
81
+ This project can be found on github at the following URL.
82
+
83
+ http://github.com/ryanb/populator/
84
+
85
+ If you would like to contribute to this project, please fork the
86
+ repository and send me a pull request.
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('populator', '0.2.1') do |p|
6
+ p.summary = "Mass populate an Active Record database."
7
+ p.description = "Mass populate an Active Record database."
8
+ p.url = "http://github.com/ryanb/populator"
9
+ p.author = 'Ryan Bates'
10
+ p.email = "ryan (at) railscasts (dot) com"
11
+ p.ignore_pattern = ["script/*", "**/*.sqlite3"]
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/TODO ADDED
@@ -0,0 +1,22 @@
1
+ Features
2
+ - get single table inheritance working.
3
+
4
+ Possible
5
+ - iterate through array instead of selecting at random to ensure all values are chosen
6
+ - if hash is passed, apply given value that number of times: { 'foo' => 3, 'bar' => 2 }
7
+ - randomly fill every column if no block is passed to populate
8
+ - add random_foo method to record for randomly generating content
9
+
10
+ Performance
11
+ - ruby-prof to see where the bottlenecks are
12
+ - benchmark to find faster solutions
13
+ - ensure instantiating the model is really that much of a performance hit.
14
+
15
+ Support Environments
16
+ - sqlite 2
17
+ - old rails
18
+ - edge rails
19
+
20
+ Tests
21
+ - automatically detect schema changes and rebuild database
22
+ - add db:reset, db:drop and db:create rake tasks
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ require 'populator/model_additions'
3
+ require 'populator/factory'
4
+ require 'populator/record'
5
+ require 'populator/random'
6
+
7
+ require 'populator/adapters/abstract'
8
+ require 'populator/adapters/sqlite'
@@ -0,0 +1,18 @@
1
+ module Populator
2
+ module Adapters
3
+ module Abstract
4
+ # Executes multiple SQL statements in one query when joined with ";"
5
+ def execute_batch(sql, name = nil)
6
+ raise NotImplementedError, "execute_batch is an abstract method"
7
+ end
8
+
9
+ def populate(table, columns, rows, name = nil)
10
+ execute("INSERT INTO #{table} #{columns} VALUES #{rows.join(', ')}", name)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class ActiveRecord::ConnectionAdapters::AbstractAdapter
17
+ include Populator::Adapters::Abstract
18
+ end
@@ -0,0 +1,26 @@
1
+ module Populator
2
+ module Adapters
3
+ module Sqlite
4
+ # Executes multiple SQL statements in one query when joined with ";"
5
+ def execute_batch(sql, name = nil)
6
+ catch_schema_changes do
7
+ log(sql, name) do
8
+ @connection.transaction { |db| db.execute_batch(sql) }
9
+ end
10
+ end
11
+ end
12
+
13
+ def populate(table, columns, rows, name = nil)
14
+ queries = []
15
+ rows.each do |row|
16
+ queries << "INSERT INTO #{table} #{columns} VALUES #{row}"
17
+ end
18
+ execute_batch(queries.join(';'), name)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class ActiveRecord::ConnectionAdapters::SQLiteAdapter < ActiveRecord::ConnectionAdapters::AbstractAdapter
25
+ include Populator::Adapters::Sqlite
26
+ end
@@ -0,0 +1,77 @@
1
+ module Populator
2
+ class Factory
3
+ DEFAULT_RECORDS_PER_QUERY = 1000
4
+
5
+ @factories = {}
6
+ @depth = 0
7
+
8
+ def self.for_model(model_class)
9
+ @factories[model_class] ||= new(model_class)
10
+ end
11
+
12
+ def self.save_remaining_records
13
+ @factories.values.each do |factory|
14
+ factory.save_records
15
+ end
16
+ @factories = {}
17
+ end
18
+
19
+ def self.remember_depth
20
+ @depth += 1
21
+ yield
22
+ @depth -= 1
23
+ save_remaining_records if @depth.zero?
24
+ end
25
+
26
+ def initialize(model_class)
27
+ @model_class = model_class
28
+ @records = []
29
+ end
30
+
31
+ def populate(amount, options = {}, &block)
32
+ self.class.remember_depth do
33
+ build_records(Populator.interpret_value(amount), options[:per_query] || DEFAULT_RECORDS_PER_QUERY, &block)
34
+ end
35
+ end
36
+
37
+ def build_records(amount, per_query, &block)
38
+ amount.times do
39
+ record = Record.new(@model_class, last_id_in_database + @records.size + 1)
40
+ @records << record
41
+ block.call(record) if block
42
+ save_records if @records.size >= per_query
43
+ end
44
+ end
45
+
46
+ def save_records
47
+ unless @records.empty?
48
+ @model_class.connection.populate(@model_class.quoted_table_name, columns_sql, rows_sql_arr, "#{@model_class.name} Populate")
49
+ @last_id_in_database = @records.last.id
50
+ @records.clear
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def quoted_column_names
57
+ @model_class.column_names.map do |column_name|
58
+ @model_class.connection.quote_column_name(column_name)
59
+ end
60
+ end
61
+
62
+ def last_id_in_database
63
+ @last_id_in_database ||= @model_class.connection.select_value("SELECT id FROM #{@model_class.quoted_table_name} ORDER BY id DESC", "#{@model_class.name} Last ID").to_i
64
+ end
65
+
66
+ def columns_sql
67
+ "(#{quoted_column_names.join(', ')})"
68
+ end
69
+
70
+ def rows_sql_arr
71
+ @records.map do |record|
72
+ quoted_attributes = record.attribute_values.map { |v| @model_class.sanitize(v) }
73
+ "(#{quoted_attributes.join(', ')})"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,11 @@
1
+ module Populator
2
+ module ModelAdditions
3
+ def populate(amount, options = {}, &block)
4
+ Factory.for_model(self).populate(amount, options, &block)
5
+ end
6
+ end
7
+ end
8
+
9
+ class ActiveRecord::Base
10
+ extend Populator::ModelAdditions
11
+ end
@@ -0,0 +1,52 @@
1
+ module Populator
2
+ module Random
3
+ WORDS = %w(alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo aspernatur aut odit aut fugit sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt neque dolorem ipsum quia dolor sit amet consectetur adipisci velit sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem ut enim ad minima veniam quis nostrum exercitationem ullam corporis nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam nisi ut aliquid ex ea commodi consequatur quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi id est laborum et dolorum fuga et harum quidem rerum facilis est et expedita distinctio nam libero tempore cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est qui minus id quod maxime placeat facere possimus omnis voluptas assumenda est omnis dolor repellendus temporibus autem quibusdam et aut consequatur vel illum qui dolorem eum fugiat quo voluptas nulla pariatur at vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae itaque earum rerum hic tenetur a sapiente delectus ut aut reiciendis voluptatibus maiores doloribus asperiores repellat)
4
+
5
+ def value_in_range(range)
6
+ case range.first
7
+ when Integer then number_in_range(range)
8
+ when Time then time_in_range(range)
9
+ when Date then date_in_range(range)
10
+ else range.to_a.rand
11
+ end
12
+ end
13
+
14
+ def words(total)
15
+ (1..interpret_value(total)).map { WORDS.rand }.join(' ')
16
+ end
17
+
18
+ def sentences(total)
19
+ (1..interpret_value(total)).map do
20
+ words(5..20).capitalize
21
+ end.join('. ')
22
+ end
23
+
24
+ def interpret_value(value)
25
+ case value
26
+ when Array then value.rand
27
+ when Range then value_in_range(value)
28
+ else value
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def time_in_range(range)
35
+ Time.at number_in_range(Range.new(range.first.to_i, range.last.to_i, range.exclude_end?))
36
+ end
37
+
38
+ def date_in_range(range)
39
+ Date.jd number_in_range(Range.new(range.first.jd, range.last.jd, range.exclude_end?))
40
+ end
41
+
42
+ def number_in_range(range)
43
+ if range.exclude_end?
44
+ rand(range.last - range.first) + range.first
45
+ else
46
+ rand((range.last+1) - range.first) + range.first
47
+ end
48
+ end
49
+ end
50
+
51
+ extend Random # load it into the populator module directly so we can call the methods
52
+ end
@@ -0,0 +1,33 @@
1
+ module Populator
2
+ class Record
3
+ attr_accessor :attributes
4
+
5
+ def initialize(model_class, id)
6
+ @attributes = { :id => id }
7
+ @columns = model_class.column_names
8
+ @columns.each do |column|
9
+ if column == 'created_at' || column == 'updated_at'
10
+ @attributes[column.to_sym] = Time.now
11
+ end
12
+ if column == 'created_on' || column == 'updated_on'
13
+ @attributes[column.to_sym] = Date.today
14
+ end
15
+ self.instance_eval <<-EOS
16
+ def #{column}=(value)
17
+ @attributes[:#{column}] = Populator.interpret_value(value)
18
+ end
19
+
20
+ def #{column}
21
+ @attributes[:#{column}]
22
+ end
23
+ EOS
24
+ end
25
+ end
26
+
27
+ def attribute_values
28
+ @columns.map do |column|
29
+ @attributes[column.to_sym]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,106 @@
1
+
2
+ # Gem::Specification for Populator-0.2.1
3
+ # Originally generated by Echoe
4
+
5
+ --- !ruby/object:Gem::Specification
6
+ name: populator
7
+ version: !ruby/object:Gem::Version
8
+ version: 0.2.1
9
+ platform: ruby
10
+ authors:
11
+ - Ryan Bates
12
+ autorequire:
13
+ bindir: bin
14
+
15
+ date: 2008-09-01 00:00:00 -07:00
16
+ default_executable:
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: echoe
20
+ type: :development
21
+ version_requirement:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: "0"
27
+ version:
28
+ description: Mass populate an Active Record database.
29
+ email: ryan (at) railscasts (dot) com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files:
35
+ - CHANGELOG
36
+ - lib/populator/adapters/abstract.rb
37
+ - lib/populator/adapters/sqlite.rb
38
+ - lib/populator/factory.rb
39
+ - lib/populator/model_additions.rb
40
+ - lib/populator/random.rb
41
+ - lib/populator/record.rb
42
+ - lib/populator.rb
43
+ - LICENSE
44
+ - README
45
+ - tasks/deployment.rake
46
+ - tasks/spec.rake
47
+ - TODO
48
+ files:
49
+ - CHANGELOG
50
+ - lib/populator/adapters/abstract.rb
51
+ - lib/populator/adapters/sqlite.rb
52
+ - lib/populator/factory.rb
53
+ - lib/populator/model_additions.rb
54
+ - lib/populator/random.rb
55
+ - lib/populator/record.rb
56
+ - lib/populator.rb
57
+ - LICENSE
58
+ - Manifest
59
+ - populator.gemspec
60
+ - README
61
+ - spec/database.yml
62
+ - spec/example_database.yml
63
+ - spec/models/category.rb
64
+ - spec/models/product.rb
65
+ - spec/populator/factory_spec.rb
66
+ - spec/populator/model_additions_spec.rb
67
+ - spec/populator/random_spec.rb
68
+ - spec/populator/record_spec.rb
69
+ - spec/README
70
+ - spec/spec.opts
71
+ - spec/spec_helper.rb
72
+ - tasks/deployment.rake
73
+ - tasks/spec.rake
74
+ - TODO
75
+ - Rakefile
76
+ has_rdoc: true
77
+ homepage: http://github.com/ryanb/populator
78
+ post_install_message:
79
+ rdoc_options:
80
+ - --line-numbers
81
+ - --inline-source
82
+ - --title
83
+ - Populator
84
+ - --main
85
+ - README
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "="
97
+ - !ruby/object:Gem::Version
98
+ version: "1.2"
99
+ version:
100
+ requirements: []
101
+
102
+ rubyforge_project: populator
103
+ rubygems_version: 1.2.0
104
+ specification_version: 2
105
+ summary: Mass populate an Active Record database.
106
+ test_files: []
@@ -0,0 +1,23 @@
1
+ Running Specs
2
+ -------------
3
+
4
+ To prepare the specs, run this command.
5
+
6
+ script/setup
7
+
8
+ This will generate the spec/database.yml file. Configure this to work
9
+ with your databases. You can add and remove entries here as well to
10
+ test different databases. A rake task is available to run the specs for
11
+ each database.
12
+
13
+ rake spec # Run specs under all databases
14
+ rake spec:mysql # Run specs under mysql
15
+ rake spec:sqlite3 # Run specs under sqlite3
16
+ rake spec:postgresql # Run specs under postgresql
17
+ ...
18
+
19
+ Don't forget to create the user and database as necessary. You can do
20
+ so under MySQL with these commands.
21
+
22
+ CREATE DATABASE populator_test;
23
+ GRANT ALL ON populator_test.* TO populator@localhost;
@@ -0,0 +1,13 @@
1
+ # you can add and remove entries to support different databases in tests.
2
+
3
+ sqlite3:
4
+ adapter: sqlite3
5
+ database: spec/test.sqlite3
6
+ timeout: 5000
7
+
8
+ mysql:
9
+ adapter: mysql
10
+ database: populator_test
11
+ username: populator
12
+ password:
13
+ host: localhost
@@ -0,0 +1,20 @@
1
+ # you can add and remove entries to support different databases in tests.
2
+
3
+ sqlite3:
4
+ adapter: sqlite3
5
+ database: spec/test.sqlite3
6
+ timeout: 5000
7
+
8
+ mysql:
9
+ adapter: mysql
10
+ database: populator_test
11
+ username: populator
12
+ password:
13
+ host: localhost
14
+
15
+ postgresql:
16
+ adapter: postgresql
17
+ database: populator_test
18
+ username: populator
19
+ password: populator
20
+ host: localhost
@@ -0,0 +1,15 @@
1
+ class Category < ActiveRecord::Base
2
+ has_many :products
3
+ end
4
+
5
+ class CreateCategories < ActiveRecord::Migration
6
+ def self.up
7
+ create_table :categories do |t|
8
+ t.string :name
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :categories
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ class Product < ActiveRecord::Base
2
+ belongs_to :category
3
+ end
4
+
5
+ class CreateProducts < ActiveRecord::Migration
6
+ def self.up
7
+ create_table :products do |t|
8
+ t.string :name
9
+ t.text :description
10
+ t.integer :stock
11
+ t.float :weight
12
+ t.decimal :price
13
+ t.datetime :released_at
14
+ t.boolean :hidden
15
+ t.integer :category_id
16
+ end
17
+ end
18
+
19
+ def self.down
20
+ drop_table :products
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Populator::Factory do
4
+ describe "for products" do
5
+ before(:each) do
6
+ @factory = Populator::Factory.for_model(Product)
7
+ end
8
+
9
+ it "should only use one query when inserting records" do
10
+ $queries_executed = []
11
+ @factory.populate(5)
12
+ $queries_executed.grep(/^insert/i).should have(1).record
13
+ end
14
+
15
+ it "should start id at 1 and increment when table is empty" do
16
+ Product.delete_all
17
+ expected_id = 1
18
+ @factory.populate(5) do |product|
19
+ product.id.should == expected_id
20
+ expected_id += 1
21
+ end
22
+ end
23
+
24
+ it "should start id at last id and increment" do
25
+ Product.delete_all
26
+ product = Product.create
27
+ expected_id = product.id+1
28
+ @factory.populate(5) do |product|
29
+ product.id.should == expected_id
30
+ expected_id += 1
31
+ end
32
+ end
33
+
34
+ it "should generate within range" do
35
+ Product.delete_all
36
+ @factory.populate(2..4)
37
+ Product.count.should >= 2
38
+ Product.count.should <= 4
39
+ end
40
+
41
+ it "should limit number of records per query" do
42
+ $queries_executed = []
43
+ @factory.populate(5, :per_query => 2)
44
+ $queries_executed.grep(/^insert/i).should have(3).records
45
+ end
46
+ end
47
+
48
+ it "should only use two queries when nesting factories (one for each class)" do
49
+ $queries_executed = []
50
+ Populator::Factory.for_model(Category).populate(3) do |category|
51
+ Populator::Factory.for_model(Product).populate(3) do |product|
52
+ product.category_id = category.id
53
+ end
54
+ end
55
+ $queries_executed.grep(/^insert/i).should have(2).records
56
+ end
57
+
58
+ it "should only use one query when nesting factories of the same type" do
59
+ $queries_executed = []
60
+ Populator::Factory.for_model(Product).populate(3) do |product|
61
+ Populator::Factory.for_model(Product).populate(3)
62
+ end
63
+ $queries_executed.grep(/^insert/i).should have(1).record
64
+ end
65
+
66
+ it "should default to 1000 records per query" do
67
+ Populator::Factory::DEFAULT_RECORDS_PER_QUERY.should == 1000
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Populator::ModelAdditions do
4
+ it "should add populate method to active record class" do
5
+ Product.should respond_to(:populate)
6
+ end
7
+
8
+ it "should add 10 records to database" do
9
+ Product.delete_all
10
+ Product.populate(10)
11
+ Product.count.should == 10
12
+ end
13
+
14
+ it "should set attribute columns" do
15
+ Product.populate(1) do |product|
16
+ product.name = "foo"
17
+ end
18
+ Product.last.name.should == "foo"
19
+ end
20
+
21
+ it "should not pass in an instance of Active Record for performance reasons" do
22
+ Product.populate(1) do |product|
23
+ product.should_not be_kind_of(ActiveRecord::Base)
24
+ end
25
+ end
26
+
27
+ it "should not pass options hash" do
28
+ $queries_executed = []
29
+ Product.populate(5, :per_query => 2) do |product|
30
+ product.should_not be_kind_of(ActiveRecord::Base)
31
+ end
32
+ $queries_executed.grep(/^insert/i).should have(3).records
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Populator::Random do
4
+ it "should pick a random number in range excluding last value" do
5
+ Populator.expects(:rand).with(5).returns(3)
6
+ Populator.value_in_range(10...15).should == 13
7
+ end
8
+
9
+ it "should pick a random number in range including last value" do
10
+ Populator.expects(:rand).with(5).returns(3)
11
+ Populator.value_in_range(10..14).should == 13
12
+ end
13
+
14
+ it "should pick a random time in range" do
15
+ start_time = 2.days.ago
16
+ end_time = Time.now
17
+ Populator.expects(:rand).with(end_time.to_i-start_time.to_i).returns(1)
18
+ Populator.value_in_range(start_time...end_time).should == Time.at(start_time.to_i + 1)
19
+ end
20
+
21
+ it "should pick a random date in range" do
22
+ start_date = 2.years.ago.to_date
23
+ end_date = Date.today
24
+ Populator.expects(:rand).with(end_date.jd-start_date.jd).returns(1)
25
+ Populator.value_in_range(start_date...end_date).should == Date.jd(start_date.jd + 1)
26
+ end
27
+
28
+ it "should pick a random string by converting to array" do
29
+ Kernel.expects(:rand).with(5).returns(2)
30
+ Populator.value_in_range('a'..'e').should == 'c'
31
+ end
32
+
33
+ it "should pick 3 random words" do
34
+ Populator.words(3).split.should have(3).records
35
+ end
36
+
37
+ it "should pick a random number of random words" do
38
+ Populator.expects(:rand).with(5).returns(3)
39
+ Populator.words(10...15).split.should have(13).records
40
+ end
41
+
42
+ it "should generate 3 random sentences" do
43
+ Populator.sentences(3).split(/\. [A-Z]/).should have(3).records
44
+ end
45
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Populator::Record do
4
+ it "should have a writer and reader methods for each column" do
5
+ record = Populator::Record.new(Product, 1)
6
+ Product.column_names.each do |column|
7
+ record.send("#{column}=", "foo")
8
+ record.send(column).should == "foo"
9
+ end
10
+ end
11
+
12
+ it "should return attribute values in same order as columns" do
13
+ record = Populator::Record.new(Product, nil)
14
+ record.name = "foo"
15
+ expected = Product.column_names.map { |c| "foo" if c == 'name' }
16
+ record.attribute_values.should == expected
17
+ end
18
+
19
+ it "should assign second parameter to id" do
20
+ Populator::Record.new(Product, 2).id.should == 2
21
+ end
22
+
23
+ it "should pick random number from range" do
24
+ record = Populator::Record.new(Product, 1)
25
+ record.stock = 2..5
26
+ record.stock.should >= 2
27
+ record.stock.should <= 5
28
+ end
29
+
30
+ it "should pick random value from array" do
31
+ record = Populator::Record.new(Product, 1)
32
+ record.name = %w[foo bar]
33
+ %w[foo bar].should include(record.name)
34
+ end
35
+
36
+ it "should automatically set created/updated columns" do
37
+ Product.stubs(:column_names).returns(%w[id created_at updated_at created_on updated_on])
38
+ record = Populator::Record.new(Product, 1)
39
+ record.created_at.to_date.should == Date.today
40
+ record.updated_at.to_date.should == Date.today
41
+ record.created_on.should == Date.today
42
+ record.updated_on.should == Date.today
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'active_support'
4
+ require 'active_record'
5
+ require File.dirname(__FILE__) + '/../lib/populator.rb'
6
+
7
+ adapter = ENV['POPULATOR_ADAPTER'] || 'sqlite3'
8
+ puts "Running specs on #{adapter}"
9
+
10
+ # setup database adapter
11
+ ActiveRecord::Base.establish_connection(
12
+ YAML.load(File.read(File.dirname(__FILE__) + "/database.yml"))[adapter]
13
+ )
14
+
15
+ # keep track of which queries have been executed
16
+ unless ActiveRecord::Base.connection.respond_to? :record_query
17
+ ActiveRecord::Base.connection.class.class_eval do
18
+ IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^begin /i, /^commit /i]
19
+
20
+ def record_query(sql)
21
+ $queries_executed ||= []
22
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
23
+ end
24
+
25
+ def execute_with_query_record(*args, &block)
26
+ record_query(args.first)
27
+ execute_without_query_record(*args, &block)
28
+ end
29
+ alias_method_chain :execute, :query_record
30
+
31
+ def execute_batch_with_query_record(*args, &block)
32
+ record_query(args.first)
33
+ execute_batch_without_query_record(*args, &block)
34
+ end
35
+ alias_method_chain :execute_batch, :query_record
36
+ end
37
+ end
38
+
39
+ # load models
40
+ # there's probably a better way to handle this
41
+ require File.dirname(__FILE__) + '/models/category.rb'
42
+ require File.dirname(__FILE__) + '/models/product.rb'
43
+ CreateCategories.migrate(:up) unless Category.table_exists?
44
+ CreateProducts.migrate(:up) unless Product.table_exists?
45
+
46
+ Spec::Runner.configure do |config|
47
+ config.mock_with :mocha
48
+ end
@@ -0,0 +1,2 @@
1
+ desc "Build the manifest and gemspec files."
2
+ task :build => [:build_manifest, :build_gemspec]
@@ -0,0 +1,22 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ ADAPTERS = YAML.load(File.read(File.dirname(__FILE__) + "/../spec/database.yml")).keys
4
+
5
+ desc "Run specs under all supported databases"
6
+ task :spec => ADAPTERS.map { |a| "spec:#{a}" }
7
+
8
+ namespace :spec do
9
+ ADAPTERS.each do |adapter|
10
+ namespace :prepare do
11
+ task adapter do
12
+ ENV["POPULATOR_ADAPTER"] = adapter
13
+ end
14
+ end
15
+
16
+ desc "Run specs under #{adapter}"
17
+ Spec::Rake::SpecTask.new(adapter => "spec:prepare:#{adapter}") do |t|
18
+ t.spec_files = Rake::FileList["spec/**/*_spec.rb"]
19
+ t.spec_opts = ["-c"]
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: populator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Bates
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: echoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Mass populate an Active Record database.
26
+ email: ryan (at) railscasts (dot) com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - CHANGELOG
33
+ - lib/populator/adapters/abstract.rb
34
+ - lib/populator/adapters/sqlite.rb
35
+ - lib/populator/factory.rb
36
+ - lib/populator/model_additions.rb
37
+ - lib/populator/random.rb
38
+ - lib/populator/record.rb
39
+ - lib/populator.rb
40
+ - LICENSE
41
+ - README
42
+ - tasks/deployment.rake
43
+ - tasks/spec.rake
44
+ - TODO
45
+ files:
46
+ - CHANGELOG
47
+ - lib/populator/adapters/abstract.rb
48
+ - lib/populator/adapters/sqlite.rb
49
+ - lib/populator/factory.rb
50
+ - lib/populator/model_additions.rb
51
+ - lib/populator/random.rb
52
+ - lib/populator/record.rb
53
+ - lib/populator.rb
54
+ - LICENSE
55
+ - Manifest
56
+ - populator.gemspec
57
+ - README
58
+ - spec/database.yml
59
+ - spec/example_database.yml
60
+ - spec/models/category.rb
61
+ - spec/models/product.rb
62
+ - spec/populator/factory_spec.rb
63
+ - spec/populator/model_additions_spec.rb
64
+ - spec/populator/random_spec.rb
65
+ - spec/populator/record_spec.rb
66
+ - spec/README
67
+ - spec/spec.opts
68
+ - spec/spec_helper.rb
69
+ - tasks/deployment.rake
70
+ - tasks/spec.rake
71
+ - TODO
72
+ - Rakefile
73
+ has_rdoc: true
74
+ homepage: http://github.com/ryanb/populator
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --line-numbers
78
+ - --inline-source
79
+ - --title
80
+ - Populator
81
+ - --main
82
+ - README
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "="
94
+ - !ruby/object:Gem::Version
95
+ version: "1.2"
96
+ version:
97
+ requirements: []
98
+
99
+ rubyforge_project: populator
100
+ rubygems_version: 1.2.0
101
+ signing_key:
102
+ specification_version: 2
103
+ summary: Mass populate an Active Record database.
104
+ test_files: []
105
+