mass_insert 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +52 -1
- data/Rakefile +0 -2
- data/lib/mass_insert/adapters/mysql2_adapter.rb +1 -1
- data/lib/mass_insert/base.rb +42 -19
- data/lib/mass_insert/process_control.rb +29 -5
- data/lib/mass_insert/version.rb +1 -1
- data/spec/active_record_dummy/config/database.yml +1 -1
- data/spec/active_record_models/model_spec.rb +3 -3
- data/spec/mass_insert/base_spec.rb +18 -2
- data/spec/mass_insert/process_control_spec.rb +89 -13
- metadata +4 -4
data/README.md
CHANGED
@@ -41,10 +41,61 @@ The array of values:
|
|
41
41
|
}
|
42
42
|
]
|
43
43
|
|
44
|
-
And call mass_insert method:
|
44
|
+
And call mass_insert method from your model:
|
45
45
|
|
46
46
|
User.mass_insert(values)
|
47
47
|
|
48
|
+
|
49
|
+
## Results
|
50
|
+
|
51
|
+
Sometimes after MassInsert process you need to see some necessary information about the process. MassInsert gem provides a simple way to do it. Only call the next methods from your model after MassInsert execution.
|
52
|
+
|
53
|
+
User.mass_insert_results.records # => 120000
|
54
|
+
|
55
|
+
Some result options are...
|
56
|
+
|
57
|
+
1. `records` : Returns the amount of records that were persisted.
|
58
|
+
2. `time` : Returns the time that took to do all the MassInsert process.
|
59
|
+
3. `build_time` : Returns the time that took to create the query string that was persisted.
|
60
|
+
4. `execute_time` : Returns the time that took to execute the query string that was persisted.
|
61
|
+
|
62
|
+
|
63
|
+
## Options
|
64
|
+
|
65
|
+
MassInsert accepts options hash by second param when you call `mass_insert` from your model. This options allow you to configure the way that the records will be created. Example...
|
66
|
+
|
67
|
+
options = {
|
68
|
+
:primary_key => :user_id,
|
69
|
+
:primary_key_mode => :manual
|
70
|
+
}
|
71
|
+
|
72
|
+
User.mass_insert(values, options)
|
73
|
+
|
74
|
+
OR directly
|
75
|
+
|
76
|
+
User.mass_insert(values, :primary_key => :user_id, :primary_key_mode => :manual)
|
77
|
+
|
78
|
+
Some options that you can include are...
|
79
|
+
|
80
|
+
1. `:table_name` : Default value is the table name to your model. This options rarely needs to change but you can do it if you pass a string with the table name. Example...
|
81
|
+
|
82
|
+
options = {:table_name => "users"}
|
83
|
+
|
84
|
+
2. `:primary_key` : Default value is `:id`. You can change the name of primary key column send it a symbol with the column name.
|
85
|
+
|
86
|
+
options = {:primary_key => :post_id}
|
87
|
+
|
88
|
+
3. `:primary_key_mode` : Default value is `:auto`. When is `:auto` MassInsert knows that the database will generate the value of the primary key column automatically. If you pass `:manual` as primary key mode you need to create your value hashes with the key and value of the primary key column.
|
89
|
+
|
90
|
+
options = {:primary_key_mode => :manual}
|
91
|
+
|
92
|
+
|
93
|
+
## Advantages
|
94
|
+
|
95
|
+
Faster. It's depending of the computer but these are some results...
|
96
|
+
|
97
|
+
* PostgreSQL - Saving 10,000 records in 0.84s
|
98
|
+
|
48
99
|
## Attention
|
49
100
|
|
50
101
|
Since this is a single database insertion your model validation will be ignored, then if you use this gem you need to be sure that information is OK to be persisted.
|
data/Rakefile
CHANGED
@@ -4,8 +4,6 @@ namespace :spec do
|
|
4
4
|
desc "Prepares the environment to use the gem"
|
5
5
|
task :prepare do
|
6
6
|
system("
|
7
|
-
bundle install
|
8
|
-
cd spec/active_record_dummy/
|
9
7
|
bundle install
|
10
8
|
rake db:drop db:create db:migrate RAILS_ENV=mysql2
|
11
9
|
rake db:drop db:create db:migrate RAILS_ENV=postgresql
|
@@ -5,7 +5,7 @@ module MassInsert
|
|
5
5
|
# This method is overwrite because the timestamp format to this
|
6
6
|
# database engine does not need precision in nanoseconds.
|
7
7
|
def timestamp_format
|
8
|
-
"%Y-%m-%d %H:%M:%S"
|
8
|
+
@timestamp_format ||= "%Y-%m-%d %H:%M:%S"
|
9
9
|
end
|
10
10
|
|
11
11
|
# This functions calls the necessary functions to create a complete
|
data/lib/mass_insert/base.rb
CHANGED
@@ -5,8 +5,8 @@ module MassInsert
|
|
5
5
|
#
|
6
6
|
# User.mass_insert(values)
|
7
7
|
#
|
8
|
-
# The values should be an array with the object values
|
9
|
-
#
|
8
|
+
# The values should be an array with the object values in a hash.
|
9
|
+
# Example...
|
10
10
|
#
|
11
11
|
# values = [
|
12
12
|
# {:name => "user name", :email => "user email"},
|
@@ -14,30 +14,53 @@ module MassInsert
|
|
14
14
|
# {:name => "user name", :email => "user email"}
|
15
15
|
# ]
|
16
16
|
#
|
17
|
+
# When a class that inherit from ActiveRecord::Base calls this method
|
18
|
+
# is going to extend the methods in ClassMethods module. This module
|
19
|
+
# contains some methods that provides some necessary functionality.
|
20
|
+
#
|
21
|
+
# After extends the class with methods in ClassMethods module. The
|
22
|
+
# options that were passed by params are sanitized in the method
|
23
|
+
# called mass_insert_options_sanitized to be passed by params to
|
24
|
+
# do ProcessControl instance. The values are passed too.
|
17
25
|
def mass_insert values, args = {}
|
26
|
+
class_eval do
|
27
|
+
extend ClassMethods
|
28
|
+
end
|
29
|
+
|
18
30
|
options = mass_insert_options(args)
|
19
|
-
|
20
|
-
|
31
|
+
@mass_insert_process = MassInsert::ProcessControl.new(values, options)
|
32
|
+
@mass_insert_process.start
|
21
33
|
end
|
22
34
|
|
23
|
-
private
|
24
35
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
36
|
+
module ClassMethods
|
37
|
+
# Returns an OpenStruc instance where is possible to see the
|
38
|
+
# results of MassInsert process. This method calls results method
|
39
|
+
# in ProcessControl class. Returns nil if there is not a instance
|
40
|
+
# variable with the MassInset process.
|
41
|
+
def mass_insert_results
|
42
|
+
@mass_insert_process.results if @mass_insert_process
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
47
|
+
def mass_insert_options args = {}
|
48
|
+
# prepare default options that come in the class that invokes the
|
49
|
+
# mass_insert function.
|
50
|
+
args[:class_name] ||= self
|
51
|
+
args[:table_name] ||= self.table_name
|
36
52
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
# prepare attributes options that were configured by the user and
|
54
|
+
# if the options weren't passed, they would be initialized with the
|
55
|
+
# default values.
|
56
|
+
args[:primary_key] ||= :id
|
57
|
+
args[:primary_key_mode] ||= :auto
|
41
58
|
|
59
|
+
# Returns the arguments but sanitized and ready to be used. This
|
60
|
+
# args will be passed by params to ProcessControl class.
|
61
|
+
args
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
42
65
|
end
|
43
66
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'ostruct'
|
3
|
+
|
1
4
|
module MassInsert
|
2
5
|
class ProcessControl
|
3
6
|
|
@@ -9,15 +12,36 @@ module MassInsert
|
|
9
12
|
end
|
10
13
|
|
11
14
|
# This method is responsible to call all the necessary process to
|
12
|
-
# complete the mass insertion process
|
13
|
-
|
14
|
-
|
15
|
+
# complete the mass insertion process and save the time each method
|
16
|
+
# takes being executed.
|
17
|
+
def start
|
18
|
+
# Calls the method that build the query.
|
19
|
+
@build_time = Benchmark.measure{ build_query }
|
20
|
+
# Calls the method that execute the query.
|
21
|
+
@execute_time = Benchmark.measure{ execute_query }
|
15
22
|
end
|
16
23
|
|
17
24
|
# Returns the correct query string according to database adapter
|
18
25
|
# previosly configured usually in database.yml in Rails project.
|
19
|
-
def
|
20
|
-
QueryBuilder.new(values, options).build
|
26
|
+
def build_query
|
27
|
+
@query = QueryBuilder.new(values, options).build
|
28
|
+
end
|
29
|
+
|
30
|
+
# This method does a QueryExecution instance where the query will be
|
31
|
+
# execute. The query string is the instance variable @query.
|
32
|
+
def execute_query
|
33
|
+
QueryExecution.new(@query).execute if @query
|
34
|
+
end
|
35
|
+
|
36
|
+
# Provides an OpenStruc instance to see the process results. This
|
37
|
+
# method is usually called from mass_insert_results in Base module.
|
38
|
+
def results
|
39
|
+
result = OpenStruct.new
|
40
|
+
result.time = @build_time.total + @execute_time.total
|
41
|
+
result.records = values.count
|
42
|
+
result.build_time = @build_time.total
|
43
|
+
result.execute_time = @execute_time.total
|
44
|
+
result
|
21
45
|
end
|
22
46
|
|
23
47
|
end
|
data/lib/mass_insert/version.rb
CHANGED
@@ -23,10 +23,10 @@ describe "Model" do
|
|
23
23
|
User.count.should eq(5)
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should save if values cointains
|
27
|
-
|
26
|
+
it "should save if values cointains 1200 records" do
|
27
|
+
1200.times{ @values << @value_hash }
|
28
28
|
User.mass_insert(@values, @options)
|
29
|
-
User.count.should eq(
|
29
|
+
User.count.should eq(1200)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -6,7 +6,7 @@ describe MassInsert::Base do
|
|
6
6
|
describe "class methods" do
|
7
7
|
describe ".mass_insert" do
|
8
8
|
before :each do
|
9
|
-
MassInsert::ProcessControl.any_instance.stub(:
|
9
|
+
MassInsert::ProcessControl.any_instance.stub(:start => true)
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should respond to mass_insert class method" do
|
@@ -30,7 +30,7 @@ describe MassInsert::Base do
|
|
30
30
|
|
31
31
|
it "should call execute ProcessControl method" do
|
32
32
|
process = MassInsert::ProcessControl.any_instance
|
33
|
-
process.should_receive(:
|
33
|
+
process.should_receive(:start).exactly(1).times
|
34
34
|
Test.mass_insert([], {})
|
35
35
|
end
|
36
36
|
|
@@ -41,6 +41,22 @@ describe MassInsert::Base do
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
describe ".mass_insert_results" do
|
45
|
+
it "should respond to mass_insert_results class method" do
|
46
|
+
Test.respond_to?(:mass_insert_results).should be_true
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when mass_insert_process instance variable exists" do
|
50
|
+
it "should call results method in ProcessControl class" do
|
51
|
+
process = MassInsert::ProcessControl
|
52
|
+
process.any_instance.stub(:results).and_return(true)
|
53
|
+
process.any_instance.should_receive(:results).exactly(1).times
|
54
|
+
Test.mass_insert([], {})
|
55
|
+
Test.mass_insert_results
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
44
60
|
describe ".mass_insert_options" do
|
45
61
|
describe "class_name" do
|
46
62
|
it "returns class name that call if that option doesn't exist" do
|
@@ -27,30 +27,106 @@ describe MassInsert::ProcessControl do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
describe "#
|
31
|
-
|
32
|
-
subject.
|
30
|
+
describe "#start" do
|
31
|
+
before :each do
|
32
|
+
subject.stub(:build_query).and_return(true)
|
33
|
+
subject.stub(:execute_query).and_return(true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should respond to start method" do
|
37
|
+
subject.respond_to?(:start).should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should call build_query" do
|
41
|
+
subject.should_receive(:build_query).exactly(1).times
|
42
|
+
subject.start
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should define instance variable @build_time" do
|
46
|
+
subject.start
|
47
|
+
subject.instance_variables.include?(:@build_time).should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should call execute_query" do
|
51
|
+
subject.should_receive(:execute_query).exactly(1).times
|
52
|
+
subject.start
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should define instance variable @execute_time" do
|
56
|
+
subject.start
|
57
|
+
subject.instance_variables.include?(:@execute_time).should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#execute_query" do
|
62
|
+
it "should respond to execute_query method" do
|
63
|
+
subject.respond_to?(:execute_query).should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when query instance variable exists" do
|
67
|
+
it "should instance and call QueryExecution class" do
|
68
|
+
subject.instance_variable_set(:@query, "query")
|
69
|
+
execution = MassInsert::QueryExecution.any_instance
|
70
|
+
execution.stub(:execute).and_return("executed")
|
71
|
+
execution.should_receive(:execute).exactly(1).times
|
72
|
+
subject.execute_query
|
73
|
+
end
|
33
74
|
end
|
34
75
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
76
|
+
context "when query instance variable does not exists" do
|
77
|
+
it "should instance and call QueryExecution class" do
|
78
|
+
subject.instance_variable_set(:@query, nil)
|
79
|
+
execution = MassInsert::QueryExecution.any_instance
|
80
|
+
execution.stub(:execute).and_return("executed")
|
81
|
+
execution.should_not_receive(:execute)
|
82
|
+
subject.execute_query
|
83
|
+
end
|
41
84
|
end
|
42
85
|
end
|
43
86
|
|
44
|
-
describe "#
|
45
|
-
it "should respond to
|
46
|
-
subject.respond_to?(:
|
87
|
+
describe "#build_query" do
|
88
|
+
it "should respond to build_query method" do
|
89
|
+
subject.respond_to?(:build_query).should be_true
|
47
90
|
end
|
48
91
|
|
49
92
|
it "should instance and call QueryBuilder class" do
|
50
93
|
builder = MassInsert::QueryBuilder.any_instance
|
51
94
|
builder.stub(:build).and_return("query")
|
52
|
-
subject.
|
95
|
+
subject.build_query.should eq("query")
|
53
96
|
end
|
97
|
+
|
98
|
+
it "should set query instance variable" do
|
99
|
+
builder = MassInsert::QueryBuilder.any_instance
|
100
|
+
builder.stub(:build).and_return("query")
|
101
|
+
subject.build_query
|
102
|
+
subject.instance_variables.include?(:@query).should be_true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#results" do
|
108
|
+
before :each do
|
109
|
+
subject.stub(:values).and_return([{}, {}])
|
110
|
+
a = Benchmark.measure{}
|
111
|
+
a.stub(:total).and_return(10.0)
|
112
|
+
subject.instance_variable_set(:@build_time, a)
|
113
|
+
subject.instance_variable_set(:@execute_time, a)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should return total time in results" do
|
117
|
+
subject.results.time.should eql(20.0)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should return records persisted in results" do
|
121
|
+
subject.results.records.should eql(2)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return build time in results" do
|
125
|
+
subject.results.build_time.should eql(10.0)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should return execute time in results" do
|
129
|
+
subject.results.execute_time.should eql(10.0)
|
54
130
|
end
|
55
131
|
end
|
56
132
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mass_insert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -162,7 +162,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
162
|
version: '0'
|
163
163
|
segments:
|
164
164
|
- 0
|
165
|
-
hash: -
|
165
|
+
hash: -391143887748903532
|
166
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
167
|
none: false
|
168
168
|
requirements:
|
@@ -171,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
171
|
version: '0'
|
172
172
|
segments:
|
173
173
|
- 0
|
174
|
-
hash: -
|
174
|
+
hash: -391143887748903532
|
175
175
|
requirements: []
|
176
176
|
rubyforge_project:
|
177
177
|
rubygems_version: 1.8.25
|