activerecord_save_many 0.2.0 → 0.4.0
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/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
|