sp-squealer 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rvmrc +6 -0
- data/.watchr +27 -0
- data/LICENSE +20 -0
- data/README.md +83 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/bin/skewer +161 -0
- data/lib/example_squeal.rb +59 -0
- data/lib/squealer.rb +6 -0
- data/lib/squealer/database.rb +87 -0
- data/lib/squealer/hash.rb +7 -0
- data/lib/squealer/object.rb +34 -0
- data/lib/squealer/progress_bar.rb +122 -0
- data/lib/squealer/target.rb +177 -0
- data/lib/tasks/jeweler.rake +15 -0
- data/spec/integration/export_a_record_spec.rb +136 -0
- data/spec/integration/imports_from_mongodb.rb +166 -0
- data/spec/integration/spec_helper_dbms.rb +111 -0
- data/spec/integration/spec_helper_dbms_mysql.rb +50 -0
- data/spec/integration/spec_helper_dbms_postgres.rb +49 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/squealer/database_spec.rb +4 -0
- data/spec/squealer/hash_spec.rb +28 -0
- data/spec/squealer/object_spec.rb +119 -0
- data/spec/squealer/progress_bar_spec.rb +254 -0
- data/spec/squealer/target_spec.rb +288 -0
- data/squealer.gemspec +99 -0
- metadata +201 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Squealer::ProgressBar do
|
4
|
+
let(:clock) { Time.new }
|
5
|
+
let(:total) { 200 }
|
6
|
+
let(:progress_bar) do
|
7
|
+
testable_progress_bar = Class.new(Squealer::ProgressBar) do
|
8
|
+
attr_reader :emitter
|
9
|
+
attr_accessor :clock
|
10
|
+
public :total, :ticks, :percentage, :progress_markers, :emit,
|
11
|
+
:duration, :start_time, :end_time, :progress_bar_width
|
12
|
+
|
13
|
+
def console
|
14
|
+
@console ||= StringIO.new
|
15
|
+
end
|
16
|
+
|
17
|
+
alias real_start_emitter start_emitter
|
18
|
+
def start_emitter; end
|
19
|
+
public :real_start_emitter
|
20
|
+
|
21
|
+
end
|
22
|
+
bar = testable_progress_bar.new(total)
|
23
|
+
bar.clock = clock
|
24
|
+
bar.start
|
25
|
+
end
|
26
|
+
let(:console) { progress_bar.console }
|
27
|
+
let(:progress_bar_width) { progress_bar.progress_bar_width }
|
28
|
+
|
29
|
+
before { progress_bar.start }
|
30
|
+
after { progress_bar.finish }
|
31
|
+
|
32
|
+
it "allows only one progress bar at a time" do
|
33
|
+
Squealer::ProgressBar.new(0).should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "records the starting time" do
|
37
|
+
progress_bar.start_time.should be_an_instance_of(Time)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "records the starting time" do
|
41
|
+
progress_bar.start_time.should be_an_instance_of(Time)
|
42
|
+
end
|
43
|
+
|
44
|
+
context "threaded" do
|
45
|
+
before { progress_bar.stub(:emitter).and_return(progress_bar.real_start_emitter) }
|
46
|
+
after { progress_bar.emitter.kill }
|
47
|
+
|
48
|
+
it "has an emitter" do
|
49
|
+
progress_bar.tick
|
50
|
+
progress_bar.emitter.should_not be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "emits at least once" do
|
54
|
+
progress_bar.tick
|
55
|
+
progress_bar.emitter.wakeup
|
56
|
+
sleep 0.1
|
57
|
+
console.string.split("\r").length.should > 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "no items completed" do
|
62
|
+
it "emits the total number provided" do
|
63
|
+
progress_bar.total.should == total
|
64
|
+
end
|
65
|
+
|
66
|
+
it "emits the number of ticks" do
|
67
|
+
progress_bar.ticks.should == 0
|
68
|
+
end
|
69
|
+
|
70
|
+
it "displays an empty bar" do
|
71
|
+
progress_bar.progress_markers.size.should == 0
|
72
|
+
end
|
73
|
+
|
74
|
+
it "prints a progress bar to the console" do
|
75
|
+
progress_bar.emit
|
76
|
+
console.string.should == "\r[#{' ' * progress_bar_width}] #{0}/#{total} (0%) [0/s]"
|
77
|
+
end
|
78
|
+
|
79
|
+
context "0 ticks per second" do
|
80
|
+
it "shows 0 documents per second" do
|
81
|
+
progress_bar.clock = progress_bar.clock + 60
|
82
|
+
progress_bar.emit
|
83
|
+
console.string.should =~ %r{ \[0/s\]$}
|
84
|
+
|
85
|
+
progress_bar.clock = progress_bar.clock + 60
|
86
|
+
progress_bar.emit
|
87
|
+
console.string.should =~ %r{ \[0/s\]$}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "1 ticks per second" do
|
92
|
+
it "shows 1 document per second" do
|
93
|
+
progress_bar.clock = progress_bar.clock + 60
|
94
|
+
progress_bar.tick
|
95
|
+
progress_bar.emit
|
96
|
+
console.string.should =~ %r{ \[1/s\]$}
|
97
|
+
|
98
|
+
progress_bar.clock = progress_bar.clock + 60
|
99
|
+
progress_bar.tick
|
100
|
+
progress_bar.emit
|
101
|
+
console.string.should =~ %r{ \[1/s\]$}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "2 ticks per second" do
|
106
|
+
it "shows 2 documents per second" do
|
107
|
+
progress_bar.clock = progress_bar.clock + 60
|
108
|
+
progress_bar.tick
|
109
|
+
progress_bar.tick
|
110
|
+
progress_bar.emit
|
111
|
+
console.string.should =~ %r{ \[2/s\]$}
|
112
|
+
|
113
|
+
progress_bar.clock = progress_bar.clock + 60
|
114
|
+
progress_bar.tick
|
115
|
+
progress_bar.tick
|
116
|
+
progress_bar.emit
|
117
|
+
console.string.should =~ %r{ \[2/s\]$}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "averaging 2 ticks per second" do
|
122
|
+
it "shows 2 documents per second" do
|
123
|
+
progress_bar.clock = progress_bar.clock + 60
|
124
|
+
progress_bar.tick
|
125
|
+
progress_bar.emit
|
126
|
+
|
127
|
+
progress_bar.clock = progress_bar.clock + 60
|
128
|
+
progress_bar.tick
|
129
|
+
progress_bar.emit
|
130
|
+
|
131
|
+
progress_bar.clock = progress_bar.clock + 60
|
132
|
+
4.times { progress_bar.tick }
|
133
|
+
progress_bar.emit
|
134
|
+
console.string.should =~ %r{ \[2/s\]$}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "one item completed" do
|
140
|
+
before { progress_bar.tick }
|
141
|
+
it "emits the number of ticks" do
|
142
|
+
progress_bar.ticks.should == 1
|
143
|
+
end
|
144
|
+
|
145
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
146
|
+
progress_bar.percentage.should == 0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "1/nth complete (where n is the width of the progress bar)" do
|
151
|
+
let(:ticks) { (total / progress_bar_width) }
|
152
|
+
before { ticks.times { progress_bar.tick } }
|
153
|
+
it "emits the number of ticks" do
|
154
|
+
progress_bar.ticks.should == ticks
|
155
|
+
end
|
156
|
+
|
157
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
158
|
+
progress_bar.percentage.should == (ticks.to_f * 100 / total).floor
|
159
|
+
end
|
160
|
+
|
161
|
+
it "displays the first progress marker" do
|
162
|
+
progress_bar.progress_markers.size.should == 1
|
163
|
+
end
|
164
|
+
|
165
|
+
it "prints a progress bar to the console" do
|
166
|
+
progress_bar.emit
|
167
|
+
percentage = (ticks.to_f * 100 / total).floor
|
168
|
+
console.string.should == "\r[=#{' ' * (progress_bar_width - 1)}] #{ticks}/#{total} (#{percentage}%) [0/s]"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "all but one item completed" do
|
173
|
+
let(:ticks) { total - 1 }
|
174
|
+
before { ticks.times { progress_bar.tick } }
|
175
|
+
|
176
|
+
it "emits the number of ticks" do
|
177
|
+
progress_bar.ticks.should == ticks
|
178
|
+
end
|
179
|
+
|
180
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
181
|
+
progress_bar.percentage.should == 99
|
182
|
+
end
|
183
|
+
|
184
|
+
it "has not yet displayed the final progress marker" do
|
185
|
+
progress_bar.progress_markers.size.should == (progress_bar_width - 1)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "all items completed" do
|
190
|
+
let(:ticks) { total }
|
191
|
+
before { ticks.times { progress_bar.tick } }
|
192
|
+
|
193
|
+
it "emits the number of ticks" do
|
194
|
+
progress_bar.ticks.should == ticks
|
195
|
+
end
|
196
|
+
|
197
|
+
it "emits 100%" do
|
198
|
+
progress_bar.percentage.should == 100
|
199
|
+
end
|
200
|
+
|
201
|
+
it "fills the progress bar with progress markers" do
|
202
|
+
progress_bar.progress_markers.size.should == progress_bar_width
|
203
|
+
end
|
204
|
+
|
205
|
+
it "records the ending time when finished" do
|
206
|
+
progress_bar.finish
|
207
|
+
progress_bar.end_time.should be_an_instance_of(Time)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "prints a progress bar to the console" do
|
211
|
+
progress_bar.finish
|
212
|
+
progress_bar.emit
|
213
|
+
console.string.split("\n").first.should == "\r[#{'=' * progress_bar_width}] #{ticks}/#{total} (100%) [0/s]"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "multiple emits" do
|
218
|
+
let(:ticks) { total }
|
219
|
+
subject { console.string }
|
220
|
+
before do
|
221
|
+
progress_bar.emit
|
222
|
+
end
|
223
|
+
|
224
|
+
context "not done" do
|
225
|
+
it "emitted two lines with no final newline" do
|
226
|
+
progress_bar.emit
|
227
|
+
|
228
|
+
subject.split("\r").size.should == 3
|
229
|
+
subject[-1, 1].should_not == "\n"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "done" do
|
234
|
+
it "emitted two lines with a final newline" do
|
235
|
+
ticks.times { progress_bar.tick }
|
236
|
+
progress_bar.finish
|
237
|
+
progress_bar.emit
|
238
|
+
|
239
|
+
subject.split("\r").size.should == 3
|
240
|
+
subject[-1, 1].should == "\n"
|
241
|
+
end
|
242
|
+
|
243
|
+
it "emitted final timings" do
|
244
|
+
ticks.times { progress_bar.tick }
|
245
|
+
progress_bar.finish
|
246
|
+
progress_bar.emit
|
247
|
+
|
248
|
+
subject.should include("Start: #{progress_bar.start_time}\n")
|
249
|
+
subject.should include("End: #{progress_bar.end_time}\n")
|
250
|
+
subject.should include("Duration: #{progress_bar.duration}\n")
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Squealer::Target do
|
4
|
+
let(:table_name) { :test_table }
|
5
|
+
let(:test_table) { {'_id' => 0} }
|
6
|
+
|
7
|
+
let(:upsertable?) { Squealer::Database.instance.upsertable? }
|
8
|
+
|
9
|
+
after(:each) { Squealer::Target::Queue.instance.clear }
|
10
|
+
|
11
|
+
context "targeting" do
|
12
|
+
describe "initialize" do
|
13
|
+
let(:faqs) { [{'_id' => '123'}] }
|
14
|
+
let(:naqs) { [{:_id => 'abc'}] }
|
15
|
+
|
16
|
+
context "without a target row id" do
|
17
|
+
context "with the inferred variable in scope" do
|
18
|
+
it "infers the value from the _id field in the hashmap referenced by the variable" do
|
19
|
+
faqs.each do |faq|
|
20
|
+
Squealer::TestTarget.new(nil, :faq) do |target|
|
21
|
+
target.instance_variable_get('@row_id').should == faq['_id']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
it "infers the value from the :_id field in the hashmap referenced by the variable" do
|
26
|
+
naqs.each do |naq|
|
27
|
+
Squealer::TestTarget.new(nil, :naq) do |target|
|
28
|
+
target.instance_variable_get('@row_id').should == naq[:_id]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "but it doesn't have an _id key" do
|
34
|
+
it "throws an argument error" do
|
35
|
+
hash_with_no_id = {}
|
36
|
+
lambda do
|
37
|
+
Squealer::TestTarget.new(nil, :hash_with_no_id) {}
|
38
|
+
end.should raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "but it isn't a hashmap" do
|
43
|
+
it "throws an argument error" do
|
44
|
+
not_a_hash = nil
|
45
|
+
lambda do
|
46
|
+
Squealer::TestTarget.new(nil, :not_a_hash) {}
|
47
|
+
end.should raise_error(ArgumentError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "without the inferred variable in scope" do
|
53
|
+
it "throws a name error" do
|
54
|
+
lambda do
|
55
|
+
Squealer::TestTarget.new(nil, :missing_variable) {}
|
56
|
+
end.should raise_error(NameError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
context "nesting" do
|
65
|
+
it "pushes itself onto the targets stack when starting" do
|
66
|
+
@target1 = @target2 = nil
|
67
|
+
|
68
|
+
target1 = Squealer::TestTarget.new(nil, table_name) do
|
69
|
+
@target1 = Squealer::Target.current
|
70
|
+
test_table_2 = test_table
|
71
|
+
Squealer::TestTarget.new(nil, "#{table_name}_2") do
|
72
|
+
@target2 = Squealer::Target.current
|
73
|
+
@target2.should_not == @target1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
target1.should === @target1
|
77
|
+
end
|
78
|
+
|
79
|
+
it "pops itself off the targets stack when finished" do
|
80
|
+
Squealer::TestTarget.new(nil, table_name) { nil }
|
81
|
+
Squealer::Target.current.should be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
context "yielding" do
|
87
|
+
it "yields" do
|
88
|
+
block_done = false
|
89
|
+
target = Squealer::TestTarget.new(nil, table_name) { block_done = true }
|
90
|
+
block_done.should be_true
|
91
|
+
end
|
92
|
+
|
93
|
+
it "yields inner blocks before executing its own SQL" do
|
94
|
+
blocks_done = []
|
95
|
+
Squealer::TestTarget.new(nil, table_name) do |target_1|
|
96
|
+
blocks_done << target_1
|
97
|
+
blocks_done.first.sql.should be_empty
|
98
|
+
Squealer::TestTarget.new(nil, table_name) do |target_2|
|
99
|
+
blocks_done << target_2
|
100
|
+
blocks_done.first.sql.should be_empty
|
101
|
+
blocks_done.last.sql.should be_empty
|
102
|
+
end
|
103
|
+
blocks_done.first.sql.should be_empty
|
104
|
+
blocks_done.last.sql.should_not be_empty
|
105
|
+
end
|
106
|
+
blocks_done.first.sql.should_not be_empty
|
107
|
+
blocks_done.last.sql.should_not be_empty
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
context "assigning" do
|
113
|
+
describe "#assign" do
|
114
|
+
let(:col1) { :meaning }
|
115
|
+
let(:value1) { 42 }
|
116
|
+
let(:faqs) { [{'_id' => nil, col1.to_s => value1}] }
|
117
|
+
let(:askers) { [{'_id' => 2001, 'name' => 'Zarathustra'}] }
|
118
|
+
|
119
|
+
context "with a block" do
|
120
|
+
it "uses the value from the block" do
|
121
|
+
faqs.each do |faq|
|
122
|
+
Squealer::TestTarget.new(nil, :faq) do
|
123
|
+
assign(col1) { value1 }
|
124
|
+
Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
|
125
|
+
Squealer::Target.current.instance_variable_get('@column_values').should == [value1]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
it "uses the value from the block even if it is nil" do
|
130
|
+
faqs.each do |faq|
|
131
|
+
Squealer::TestTarget.new(nil, :faq) do
|
132
|
+
assign(col1) { nil }
|
133
|
+
Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
|
134
|
+
Squealer::Target.current.instance_variable_get('@column_values').should == [nil]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "without a block" do
|
141
|
+
context "with the inferred variable in scope" do
|
142
|
+
it "infers source from target name" do
|
143
|
+
faqs.each do |faq|
|
144
|
+
Squealer::TestTarget.new(nil, :faq) do
|
145
|
+
assign(col1)
|
146
|
+
Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
|
147
|
+
Squealer::Target.current.instance_variable_get('@column_values').should == [value1]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
it "infers related source from target name" do
|
152
|
+
askers.each do |asker|
|
153
|
+
faqs.each do |faq|
|
154
|
+
Squealer::TestTarget.new(nil, :faq) do
|
155
|
+
assign(:asker_id)
|
156
|
+
Squealer::Target.current.instance_variable_get('@column_names').should == [:asker_id]
|
157
|
+
Squealer::Target.current.instance_variable_get('@column_values').should == [2001]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context "with an empty block" do
|
166
|
+
it "assumes nil" do
|
167
|
+
faqs.each do |faq|
|
168
|
+
Squealer::TestTarget.new(nil, :faq) do
|
169
|
+
assign(col1) {}
|
170
|
+
Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
|
171
|
+
Squealer::Target.current.instance_variable_get('@column_values').should == [nil]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
context "exporting" do
|
181
|
+
it "sends the sql to the export database" do
|
182
|
+
Squealer::TestTarget.new(nil, table_name) { nil }
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#target" do
|
186
|
+
describe "#typecast_values" do
|
187
|
+
subject { target.send(:typecast_values) }
|
188
|
+
let(:target) { Squealer::TestTarget.new(nil, table_name) {} }
|
189
|
+
|
190
|
+
it "casts array to comma-separated string" do
|
191
|
+
target.assign(:colA) { ['1', '2'] }
|
192
|
+
subject.should == ['1,2']
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "generates SQL command strings" do
|
197
|
+
let(:target) { Squealer::TestTarget.new(nil, table_name) { nil } }
|
198
|
+
|
199
|
+
context "insert" do
|
200
|
+
it "targets the table" do
|
201
|
+
target.sql.should =~ /^INSERT INTO "#{table_name}" / if upsertable?
|
202
|
+
target.sql.should =~ /; INSERT INTO "#{table_name}" / unless upsertable?
|
203
|
+
end
|
204
|
+
|
205
|
+
it "includes the primary key name in the INSERT" do
|
206
|
+
target.sql.should =~ / \(id\) VALUES/
|
207
|
+
end
|
208
|
+
|
209
|
+
it "includes the primary key value in the INSERT" do
|
210
|
+
target.sql.should =~ / VALUES \('0'\)/
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "upsert" do
|
215
|
+
before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
|
216
|
+
it "uses an INSERT ... ON DUPLICATE KEY UPDATE statement" do
|
217
|
+
target.sql.should =~ /^INSERT INTO .* ON DUPLICATE KEY UPDATE /
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context "with 2 columns" do
|
223
|
+
let(:value_1) { 42 }
|
224
|
+
let(:target) do
|
225
|
+
Squealer::TestTarget.new(nil, table_name) { Squealer::Target.current.assign(:colA) { value_1 } }
|
226
|
+
end
|
227
|
+
|
228
|
+
context "insert" do
|
229
|
+
it "includes the column name in the INSERT" do
|
230
|
+
target.sql.should =~ /\(id,"colA"\) VALUES/
|
231
|
+
end
|
232
|
+
|
233
|
+
it "includes the column value in the INSERT" do
|
234
|
+
target.sql.should =~ /VALUES \('0',\?\)/
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "upsert" do
|
239
|
+
before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
|
240
|
+
it "includes the column name and value in the UPDATE" do
|
241
|
+
target.sql.should =~ /UPDATE "colA"=\?/
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "with 3 columns" do
|
247
|
+
let(:value_1) { 42 }
|
248
|
+
let(:value_2) { 'foobar' }
|
249
|
+
let(:target) do
|
250
|
+
Squealer::TestTarget.new(nil, table_name) do |target|
|
251
|
+
Squealer::Target.current.assign(:colA) { value_1 }
|
252
|
+
Squealer::Target.current.assign(:colB) { value_2 }
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "insert" do
|
257
|
+
it "includes the column names in the INSERT" do
|
258
|
+
target.sql.should =~ /\(id,"colA","colB"\) VALUES/
|
259
|
+
end
|
260
|
+
|
261
|
+
it "includes the column values in the INSERT" do
|
262
|
+
target.sql.should =~ /VALUES \('0',\?,\?\)/
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "upsert" do
|
267
|
+
before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
|
268
|
+
it "includes the column names and values in the UPDATE" do
|
269
|
+
target.sql.should =~ /UPDATE "colA"=\?,"colB"=\?/
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Yes, David Chelimsky, an any_instance stub would be handy right about now :-P
|
278
|
+
module Squealer
|
279
|
+
class TestTarget < Target
|
280
|
+
def execute_sql(sql, values)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
module BSON
|
286
|
+
class ObjectID; end
|
287
|
+
end
|
288
|
+
|