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 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