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 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
@@ -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
- # in a hash. Example...
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
- process = MassInsert::ProcessControl.new(values, options)
20
- process.execute
31
+ @mass_insert_process = MassInsert::ProcessControl.new(values, options)
32
+ @mass_insert_process.start
21
33
  end
22
34
 
23
- private
24
35
 
25
- def mass_insert_options args = {}
26
- # prepare default options that come in the class that invokes the
27
- # mass_insert function.
28
- args[:class_name] ||= self
29
- args[:table_name] ||= self.table_name
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
- # prepare attributes options that were configured by the user and
32
- # if the options weren't passed, they would be initialized with the
33
- # default values.
34
- args[:primary_key] ||= :id
35
- args[:primary_key_mode] ||= :auto
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
- # Returns the arguments but sanitized and ready to be used. This
38
- # args will be passed by params to ProcessControl class.
39
- args
40
- end
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
- def execute
14
- QueryExecution.new(query).execute
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 query
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
@@ -1,3 +1,3 @@
1
1
  module MassInsert
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -12,7 +12,7 @@ mysql2:
12
12
  adapter: mysql2
13
13
  database: mysql2_test
14
14
  user: root
15
- password: D3s4rr0ll0$.
15
+ password: D3s4rr0ll0
16
16
  pool: 5
17
17
  timeout: 5000
18
18
  encoding: utf8
@@ -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 1500 records" do
27
- 1500.times{ @values << @value_hash }
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(1500)
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(:execute => true)
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(:execute).exactly(1).times
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 "#execute" do
31
- it "should respond to execute method" do
32
- subject.respond_to?(:execute).should be_true
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
- it "should instance and call QueryExecution class" do
36
- subject.stub(:query).and_return("query")
37
- execution = MassInsert::QueryExecution.any_instance
38
- execution.stub(:execute).and_return("executed")
39
- execution.should_receive(:execute).exactly(1).times
40
- subject.execute
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 "#query" do
45
- it "should respond to query method" do
46
- subject.respond_to?(:query).should be_true
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.query.should eq("query")
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.1
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-19 00:00:00.000000000 Z
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: -4363220921701306027
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: -4363220921701306027
174
+ hash: -391143887748903532
175
175
  requirements: []
176
176
  rubyforge_project:
177
177
  rubygems_version: 1.8.25