activerecord_save_many 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.4.0
@@ -3,8 +3,12 @@ require 'set'
3
3
  module ActiveRecord
4
4
  module SaveMany
5
5
  MAX_QUERY_SIZE = 1024 * 1024
6
+ # options for the create_many method
6
7
  OPTIONS_KEYS = [:columns, :max_rows, :async, :ignore, :update, :updates].to_set
7
8
 
9
+ # options for the create_many_sql method
10
+ SQL_OPTIONS_KEYS = [:async, :ignore, :update, :updates].to_set
11
+
8
12
  class << self
9
13
  attr_accessor :default_max_rows
10
14
  end
@@ -22,12 +26,12 @@ module ActiveRecord
22
26
  end
23
27
  module_function :disable_async?
24
28
 
25
- def check_options(options)
26
- unknown_keys = options.keys.to_set - OPTIONS_KEYS.to_set
29
+ def check_options(permitted, options)
30
+ unknown_keys = options.keys.to_set - permitted
27
31
  raise "unknown options: #{unknown_keys.to_a.join(", ")}" if !unknown_keys.empty?
28
32
  end
29
33
  module_function :check_options
30
-
34
+
31
35
  # slice an array into smaller arrays with maximum size max_size
32
36
  def slice_array(max_length, arr)
33
37
  slices = []
@@ -60,7 +64,7 @@ module ActiveRecord
60
64
  end
61
65
 
62
66
  def save_many(values, options={})
63
- Functions::check_options(options)
67
+ Functions::check_options(ActiveRecord::SaveMany::OPTIONS_KEYS , options)
64
68
  return if values.nil? || values.empty?
65
69
 
66
70
  columns, values = Functions::add_columns(self, values, options)
@@ -69,12 +73,8 @@ module ActiveRecord
69
73
  max_rows = options[:max_rows] || save_many_max_rows
70
74
  batches = Functions::slice_array(max_rows, values)
71
75
 
72
- column_list = columns.join(', ')
73
- do_updates = options[:update] || options[:updates]
74
- updates = options[:updates] || {}
75
-
76
- batches.each do |batch|
77
- batch = batch.map do |obj|
76
+ batches.each do |rows|
77
+ rows = rows.map do |obj|
78
78
  if obj.is_a? ActiveRecord::Base
79
79
  obj.send( :callback, :before_save )
80
80
  if obj.id
@@ -87,17 +87,29 @@ module ActiveRecord
87
87
  columns.map{|col| obj[col]}
88
88
  end
89
89
 
90
- insert_stmt = options[:async] && !disable_async? ? "insert delayed" : "insert"
91
- ignore_opt = options[:ignore] ? "ignore" : ""
90
+ sql = connection.save_many_sql(table_name,
91
+ columns,
92
+ rows,
93
+ { :ignore=>options[:ignore],
94
+ :async=>options[:async] && !Functions::disable_async?(),
95
+ :update=>options[:update] || options[:updates],
96
+ :updates=>options[:updates] || {} })
92
97
 
93
- sql = "#{insert_stmt} #{ignore_opt} into #{table_name} (#{column_list}) values " +
94
- batch.map{|vals| "(" + vals.map{|v| quote_value(v)}.join(", ") +")"}.join(", ") +
95
- (" on duplicate key update " + columns.map{|c| updates[c] || " #{c} = values(#{c}) "}.join(", ") if do_updates).to_s
96
-
97
98
  connection.execute_raw sql
98
99
  end
99
100
  end
100
101
  end
102
+
103
+ module MySQL
104
+ def save_many_sql(table_name, columns, rows, options)
105
+ Functions::check_options(SQL_OPTIONS_KEYS, options)
106
+
107
+ sql = ["insert", ("delayed" if options[:async]), ("ignore" if options[:ignore])].compact.join(' ') +
108
+ " into #{table_name} (#{columns.join(',')}) values " +
109
+ rows.map{|vals| "(" + vals.map{|v| quote_value(v)}.join(",") +")"}.join(",") +
110
+ (" on duplicate key update "+columns.map{|c| options[:updates][c] || "#{c}=values(#{c})"}.join(",") if options[:update]).to_s
111
+ end
112
+ end
101
113
  end
102
114
 
103
115
  class Base
@@ -106,3 +118,17 @@ module ActiveRecord
106
118
  end
107
119
  end
108
120
  end
121
+
122
+ module JdbcSpec
123
+ module MySQL
124
+ include ActiveRecord::SaveMany::MySQL
125
+ end
126
+ end
127
+
128
+ module ActiveRecord
129
+ module ConnectionAdapters
130
+ class MysqlAdapter
131
+ include ActiveRecord::SaveMany::MySQL
132
+ end
133
+ end
134
+ end
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  describe "check_options" do
38
38
  it "should raise if given unknown options" do
39
39
  lambda do
40
- SaveMany::Functions::check_options(:foo=>100)
40
+ SaveMany::Functions::check_options(ActiveRecord::SaveMany::OPTIONS_KEYS, :foo=>100)
41
41
  end.should raise_error(RuntimeError)
42
42
  end
43
43
  end
@@ -99,6 +99,91 @@ module ActiveRecord
99
99
  k2.save_many_max_rows.should == 100
100
100
  end
101
101
 
102
-
102
+ # argh. am i really testing the correct sql is generated ?
103
+ describe "save_many" do
104
+ def new_ar_stub(classname, column_names, tablename, match_sql)
105
+ k=new_ar_class(classname)
106
+ stub(k).table_name{tablename}
107
+ cns = column_names.map{|cn| col=Object.new ; stub(col).name{cn} ; col}
108
+ stub(k).columns{cns}
109
+ connection = ActiveRecord::ConnectionAdapters::MysqlAdapter.new
110
+ stub(connection).quote_value{|v| "'#{v}'"}
111
+ stub(connection).execute_raw{|sql|
112
+ sql.should == match_sql
113
+ }
114
+ stub(k).connection{connection}
115
+ k
116
+ end
117
+
118
+ def new_ar_inst(klass, id, valid, field_hash)
119
+ kinst = klass.new
120
+ mock(kinst).id(){id}
121
+ mock(kinst).callback(:before_save)
122
+ if id
123
+ mock(kinst).callback(:before_update)
124
+ else
125
+ mock(kinst).callback(:before_create)
126
+ end
127
+ mock(kinst).valid?(){valid}
128
+
129
+ field_hash.keys.each{ |key|
130
+ mock(kinst).[](key){field_hash[key]}
131
+ }
132
+ kinst
133
+ end
134
+
135
+ it "should generate extended insert sql for all model columns with new objects" do
136
+ k=new_ar_stub("Foo", [:foo, :bar], "foos", "insert into foos (foo,bar) values ('foofoo','barbar')")
137
+ kinst = new_ar_inst(k, nil, true, {:foo=>"foofoo", :bar=>"barbar"})
138
+ k.save_many([kinst])
139
+ end
140
+
141
+ it "should generate extended insert sql for all model columns with existing objects" do
142
+ k=new_ar_stub("Foo", [:id, :foo, :bar], "foos", "insert into foos (id,foo,bar) values ('100','foofoo','barbar')")
143
+ kinst = new_ar_inst(k, '100', true, {:foo=>"foofoo", :bar=>"barbar", :id=>'100'})
144
+ k.save_many([kinst])
145
+ end
146
+
147
+ it "should generate extended insert sql for all model columns for multiple model instances" do
148
+ k=new_ar_stub("Foo", [:foo, :bar], "foos",
149
+ "insert into foos (foo,bar) values ('foofoo','barbar'),('foofoofoo','barbarbar')")
150
+ kinst = new_ar_inst(k, nil, true, {:foo=>"foofoo", :bar=>"barbar"})
151
+ kinst2 = new_ar_inst(k, nil, true, {:foo=>"foofoofoo", :bar=>"barbarbar"})
152
+ k.save_many([kinst,kinst2])
153
+ end
154
+
155
+ it "should generate simple extended insert sql for specified columns" do
156
+ k=new_ar_stub("Foo", [:foo, :bar], "foos", "insert into foos (foo) values ('foofoo')")
157
+ kinst = new_ar_inst(k, nil, true, {:foo=>"foofoo"})
158
+ k.save_many([kinst], :columns=>[:foo])
159
+ end
160
+
161
+ it "should generate simple extended insert sql for specified string-name columns" do
162
+ k=new_ar_stub("Foo", ["foo", "bar"], "foos", "insert into foos (foo) values ('foofoo')")
163
+ kinst = new_ar_inst(k, nil, true, {"foo"=>"foofoo"})
164
+ k.save_many([kinst], :columns=>["foo"])
165
+ end
166
+
167
+ it "should generate insert delayed sql if :async param give " do
168
+ k=new_ar_stub("Foo", [:foo], "foos", "insert delayed into foos (foo) values ('foofoo')")
169
+ kinst = new_ar_inst(k, nil, true, {"foo"=>"foofoo"})
170
+ k.save_many([kinst], :columns=>["foo"], :async=>true)
171
+ end
172
+
173
+ it "should generate insert ignore sql if :ignore param given" do
174
+ k=new_ar_stub("Foo", [:foo], "foos", "insert ignore into foos (foo) values ('foofoo')")
175
+ kinst = new_ar_inst(k, nil, true, {"foo"=>"foofoo"})
176
+ k.save_many([kinst], :columns=>["foo"], :ignore=>true)
177
+ end
178
+
179
+ it "should generate update sql with all columns if :update given" do
180
+ k=new_ar_stub("Foo", [:foo], "foos",
181
+ "insert into foos (foo) values ('foofoo') on duplicate key update foo=values(foo)")
182
+ kinst = new_ar_inst(k, nil, true, {"foo"=>"foofoo"})
183
+ k.save_many([kinst], :columns=>["foo"], :update=>true)
184
+ end
185
+
186
+ end
187
+
103
188
  end
104
189
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_save_many
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mccraigmccraig
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-17 00:00:00 +00:00
12
+ date: 2010-02-18 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency