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 +1 -1
- data/lib/activerecord_save_many.rb +42 -16
- data/spec/activerecord_save_many_spec.rb +87 -2
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
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 -
|
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
|
-
|
73
|
-
|
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
|
-
|
91
|
-
|
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.
|
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-
|
12
|
+
date: 2010-02-18 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|