sp-squealer 1.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/.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
|
+
|