mass_insert 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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