sequel_core 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/CHANGELOG +1003 -0
- data/COPYING +18 -0
- data/README +81 -0
- data/Rakefile +176 -0
- data/bin/sequel +41 -0
- data/lib/sequel_core.rb +59 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
- data/lib/sequel_core/adapters/ado.rb +100 -0
- data/lib/sequel_core/adapters/db2.rb +158 -0
- data/lib/sequel_core/adapters/dbi.rb +126 -0
- data/lib/sequel_core/adapters/informix.rb +87 -0
- data/lib/sequel_core/adapters/jdbc.rb +108 -0
- data/lib/sequel_core/adapters/mysql.rb +269 -0
- data/lib/sequel_core/adapters/odbc.rb +145 -0
- data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
- data/lib/sequel_core/adapters/openbase.rb +90 -0
- data/lib/sequel_core/adapters/oracle.rb +99 -0
- data/lib/sequel_core/adapters/postgres.rb +519 -0
- data/lib/sequel_core/adapters/sqlite.rb +192 -0
- data/lib/sequel_core/array_keys.rb +296 -0
- data/lib/sequel_core/connection_pool.rb +152 -0
- data/lib/sequel_core/core_ext.rb +59 -0
- data/lib/sequel_core/core_sql.rb +191 -0
- data/lib/sequel_core/database.rb +433 -0
- data/lib/sequel_core/dataset.rb +409 -0
- data/lib/sequel_core/dataset/convenience.rb +321 -0
- data/lib/sequel_core/dataset/sequelizer.rb +354 -0
- data/lib/sequel_core/dataset/sql.rb +586 -0
- data/lib/sequel_core/exceptions.rb +45 -0
- data/lib/sequel_core/migration.rb +191 -0
- data/lib/sequel_core/model.rb +8 -0
- data/lib/sequel_core/pretty_table.rb +73 -0
- data/lib/sequel_core/schema.rb +8 -0
- data/lib/sequel_core/schema/schema_generator.rb +131 -0
- data/lib/sequel_core/schema/schema_sql.rb +131 -0
- data/lib/sequel_core/worker.rb +58 -0
- data/spec/adapters/informix_spec.rb +139 -0
- data/spec/adapters/mysql_spec.rb +330 -0
- data/spec/adapters/oracle_spec.rb +130 -0
- data/spec/adapters/postgres_spec.rb +189 -0
- data/spec/adapters/sqlite_spec.rb +345 -0
- data/spec/array_keys_spec.rb +679 -0
- data/spec/connection_pool_spec.rb +356 -0
- data/spec/core_ext_spec.rb +67 -0
- data/spec/core_sql_spec.rb +301 -0
- data/spec/database_spec.rb +812 -0
- data/spec/dataset_spec.rb +2381 -0
- data/spec/migration_spec.rb +261 -0
- data/spec/pretty_table_spec.rb +66 -0
- data/spec/rcov.opts +4 -0
- data/spec/schema_generator_spec.rb +86 -0
- data/spec/schema_spec.rb +230 -0
- data/spec/sequelizer_spec.rb +448 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/worker_spec.rb +96 -0
- metadata +162 -0
@@ -0,0 +1,2381 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
context "Dataset" do
|
4
|
+
setup do
|
5
|
+
@dataset = Sequel::Dataset.new("db")
|
6
|
+
end
|
7
|
+
|
8
|
+
specify "should accept database and opts in initialize" do
|
9
|
+
db = "db"
|
10
|
+
opts = {:from => :test}
|
11
|
+
d = Sequel::Dataset.new(db, opts)
|
12
|
+
d.db.should be(db)
|
13
|
+
d.opts.should be(opts)
|
14
|
+
|
15
|
+
d = Sequel::Dataset.new(db)
|
16
|
+
d.db.should be(db)
|
17
|
+
d.opts.should be_a_kind_of(Hash)
|
18
|
+
d.opts.should == {}
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "should provide clone_merge for chainability." do
|
22
|
+
d1 = @dataset.clone_merge(:from => :test)
|
23
|
+
d1.class.should == @dataset.class
|
24
|
+
d1.should_not == @dataset
|
25
|
+
d1.db.should be(@dataset.db)
|
26
|
+
d1.opts[:from].should == :test
|
27
|
+
@dataset.opts[:from].should be_nil
|
28
|
+
|
29
|
+
d2 = d1.clone_merge(:order => :name)
|
30
|
+
d2.class.should == @dataset.class
|
31
|
+
d2.should_not == d1
|
32
|
+
d2.should_not == @dataset
|
33
|
+
d2.db.should be(@dataset.db)
|
34
|
+
d2.opts[:from].should == :test
|
35
|
+
d2.opts[:order].should == :name
|
36
|
+
d1.opts[:order].should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should include Enumerable" do
|
40
|
+
Sequel::Dataset.included_modules.should include(Enumerable)
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should raise ImplementedError for the dataset interface methods" do
|
44
|
+
proc {@dataset.fetch_rows('abc')}.should raise_error(NotImplementedError)
|
45
|
+
proc {@dataset.insert(1, 2, 3)}.should raise_error(NotImplementedError)
|
46
|
+
proc {@dataset.update(:name => 'abc')}.should raise_error(NotImplementedError)
|
47
|
+
proc {@dataset.delete}.should raise_error(NotImplementedError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "Dataset#clone_merge" do
|
52
|
+
setup do
|
53
|
+
@dataset = Sequel::Dataset.new(nil).from(:items)
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should return a clone self" do
|
57
|
+
clone = @dataset.clone_merge({})
|
58
|
+
clone.class.should == @dataset.class
|
59
|
+
clone.db.should == @dataset.db
|
60
|
+
clone.opts.should == @dataset.opts
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "should merge the specified options" do
|
64
|
+
clone = @dataset.clone_merge(1 => 2)
|
65
|
+
clone.opts.should == {1 => 2, :from => [:items]}
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "should overwrite existing options" do
|
69
|
+
clone = @dataset.clone_merge(:from => [:other])
|
70
|
+
clone.opts.should == {:from => [:other]}
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "should create a clone with a deep copy of options" do
|
74
|
+
clone = @dataset.clone_merge(:from => [:other])
|
75
|
+
@dataset.opts[:from].should == [:items]
|
76
|
+
clone.opts[:from].should == [:other]
|
77
|
+
end
|
78
|
+
|
79
|
+
specify "should return an object with the same modules included" do
|
80
|
+
m = Module.new do
|
81
|
+
def __xyz__; "xyz"; end
|
82
|
+
end
|
83
|
+
@dataset.extend(m)
|
84
|
+
@dataset.clone_merge({}).should respond_to(:__xyz__)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "A simple dataset" do
|
89
|
+
setup do
|
90
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should format a select statement" do
|
94
|
+
@dataset.select_sql.should == 'SELECT * FROM test'
|
95
|
+
end
|
96
|
+
|
97
|
+
specify "should format a delete statement" do
|
98
|
+
@dataset.delete_sql.should == 'DELETE FROM test'
|
99
|
+
end
|
100
|
+
|
101
|
+
specify "should format an insert statement with default values" do
|
102
|
+
@dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
|
103
|
+
end
|
104
|
+
|
105
|
+
specify "should format an insert statement with hash" do
|
106
|
+
@dataset.insert_sql(:name => 'wxyz', :price => 342).
|
107
|
+
should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
|
108
|
+
|
109
|
+
@dataset.insert_sql({}).should == "INSERT INTO test DEFAULT VALUES"
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "should format an insert statement with array with keys" do
|
113
|
+
v = [1, 2, 3]
|
114
|
+
v.keys = [:a, :b, :c]
|
115
|
+
@dataset.insert_sql(v).should == "INSERT INTO test (a, b, c) VALUES (1, 2, 3)"
|
116
|
+
|
117
|
+
v = []
|
118
|
+
v.keys = [:a, :b]
|
119
|
+
@dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
|
120
|
+
end
|
121
|
+
|
122
|
+
specify "should format an insert statement with string keys" do
|
123
|
+
@dataset.insert_sql('name' => 'wxyz', 'price' => 342).
|
124
|
+
should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
|
125
|
+
end
|
126
|
+
|
127
|
+
specify "should format an insert statement with a model instance" do
|
128
|
+
dbb = Sequel::Database.new
|
129
|
+
|
130
|
+
@c = Class.new(Sequel::Model) do
|
131
|
+
attr_accessor :values
|
132
|
+
end
|
133
|
+
|
134
|
+
v = @c.new; v.values = {:a => 1}
|
135
|
+
|
136
|
+
@dataset.insert_sql(v).should == "INSERT INTO test (a) VALUES (1)"
|
137
|
+
|
138
|
+
v = @c.new; v.values = {}
|
139
|
+
@dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "should format an insert statement with an arbitrary value" do
|
143
|
+
@dataset.insert_sql(123).should == "INSERT INTO test VALUES (123)"
|
144
|
+
end
|
145
|
+
|
146
|
+
specify "should format an insert statement with sub-query" do
|
147
|
+
@sub = Sequel::Dataset.new(nil).from(:something).filter(:x => 2)
|
148
|
+
@dataset.insert_sql(@sub).should == \
|
149
|
+
"INSERT INTO test (SELECT * FROM something WHERE (x = 2))"
|
150
|
+
end
|
151
|
+
|
152
|
+
specify "should format an insert statement with array" do
|
153
|
+
@dataset.insert_sql('a', 2, 6.5).should ==
|
154
|
+
"INSERT INTO test VALUES ('a', 2, 6.5)"
|
155
|
+
end
|
156
|
+
|
157
|
+
specify "should format an update statement" do
|
158
|
+
@dataset.update_sql(:name => 'abc').should ==
|
159
|
+
"UPDATE test SET name = 'abc'"
|
160
|
+
|
161
|
+
@dataset.update_sql {:x << :y}.should ==
|
162
|
+
"UPDATE test SET x = y"
|
163
|
+
end
|
164
|
+
|
165
|
+
specify "should format an update statement with array with keys" do
|
166
|
+
v = ['abc']
|
167
|
+
v.keys = [:name]
|
168
|
+
|
169
|
+
@dataset.update_sql(v).should == "UPDATE test SET name = 'abc'"
|
170
|
+
end
|
171
|
+
|
172
|
+
specify "should be able to return rows for arbitrary SQL" do
|
173
|
+
@dataset.select_sql(:sql => 'xxx yyy zzz').should ==
|
174
|
+
"xxx yyy zzz"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "A dataset with multiple tables in its FROM clause" do
|
179
|
+
setup do
|
180
|
+
@dataset = Sequel::Dataset.new(nil).from(:t1, :t2)
|
181
|
+
end
|
182
|
+
|
183
|
+
specify "should raise on #update_sql" do
|
184
|
+
proc {@dataset.update_sql(:a=>1)}.should raise_error
|
185
|
+
end
|
186
|
+
|
187
|
+
specify "should raise on #delete_sql" do
|
188
|
+
proc {@dataset.delete_sql}.should raise_error
|
189
|
+
end
|
190
|
+
|
191
|
+
specify "should generate a select query FROM all specified tables" do
|
192
|
+
@dataset.select_sql.should == "SELECT * FROM t1, t2"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context "Dataset#where" do
|
197
|
+
setup do
|
198
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
199
|
+
@d1 = @dataset.where(:region => 'Asia')
|
200
|
+
@d2 = @dataset.where('(region = ?)', 'Asia')
|
201
|
+
@d3 = @dataset.where("(a = 1)")
|
202
|
+
end
|
203
|
+
|
204
|
+
specify "should work with hashes" do
|
205
|
+
@dataset.where(:name => 'xyz', :price => 342).select_sql.
|
206
|
+
should match(/WHERE \(name = 'xyz'\) AND \(price = 342\)|WHERE \(price = 342\) AND \(name = 'xyz'\)/)
|
207
|
+
end
|
208
|
+
|
209
|
+
specify "should work with arrays (ala ActiveRecord)" do
|
210
|
+
@dataset.where('price < ? AND id in (?)', 100, [1, 2, 3]).select_sql.should ==
|
211
|
+
"SELECT * FROM test WHERE price < 100 AND id in (1, 2, 3)"
|
212
|
+
end
|
213
|
+
|
214
|
+
specify "should work with strings (custom SQL expressions)" do
|
215
|
+
@dataset.where('(a = 1 AND b = 2)').select_sql.should ==
|
216
|
+
"SELECT * FROM test WHERE (a = 1 AND b = 2)"
|
217
|
+
end
|
218
|
+
|
219
|
+
specify "should affect select, delete and update statements" do
|
220
|
+
@d1.select_sql.should == "SELECT * FROM test WHERE (region = 'Asia')"
|
221
|
+
@d1.delete_sql.should == "DELETE FROM test WHERE (region = 'Asia')"
|
222
|
+
@d1.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
|
223
|
+
|
224
|
+
@d2.select_sql.should == "SELECT * FROM test WHERE (region = 'Asia')"
|
225
|
+
@d2.delete_sql.should == "DELETE FROM test WHERE (region = 'Asia')"
|
226
|
+
@d2.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
|
227
|
+
|
228
|
+
@d3.select_sql.should == "SELECT * FROM test WHERE (a = 1)"
|
229
|
+
@d3.delete_sql.should == "DELETE FROM test WHERE (a = 1)"
|
230
|
+
@d3.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (a = 1)"
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
specify "should be composable using AND operator (for scoping)" do
|
235
|
+
# hashes are merged, no problem
|
236
|
+
@d1.where(:size => 'big').select_sql.should ==
|
237
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (size = 'big')"
|
238
|
+
|
239
|
+
# hash and string
|
240
|
+
@d1.where('population > 1000').select_sql.should ==
|
241
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (population > 1000)"
|
242
|
+
@d1.where('(a > 1) OR (b < 2)').select_sql.should ==
|
243
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND ((a > 1) OR (b < 2))"
|
244
|
+
|
245
|
+
# hash and array
|
246
|
+
@d1.where('(GDP > ?)', 1000).select_sql.should ==
|
247
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (GDP > 1000)"
|
248
|
+
|
249
|
+
# array and array
|
250
|
+
@d2.where('(GDP > ?)', 1000).select_sql.should ==
|
251
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (GDP > 1000)"
|
252
|
+
|
253
|
+
# array and hash
|
254
|
+
@d2.where(:name => ['Japan', 'China']).select_sql.should ==
|
255
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (name IN ('Japan', 'China'))"
|
256
|
+
|
257
|
+
# array and string
|
258
|
+
@d2.where('GDP > ?').select_sql.should ==
|
259
|
+
"SELECT * FROM test WHERE (region = 'Asia') AND (GDP > ?)"
|
260
|
+
|
261
|
+
# string and string
|
262
|
+
@d3.where('b = 2').select_sql.should ==
|
263
|
+
"SELECT * FROM test WHERE (a = 1) AND (b = 2)"
|
264
|
+
|
265
|
+
# string and hash
|
266
|
+
@d3.where(:c => 3).select_sql.should ==
|
267
|
+
"SELECT * FROM test WHERE (a = 1) AND (c = 3)"
|
268
|
+
|
269
|
+
# string and array
|
270
|
+
@d3.where('(d = ?)', 4).select_sql.should ==
|
271
|
+
"SELECT * FROM test WHERE (a = 1) AND (d = 4)"
|
272
|
+
|
273
|
+
# string and proc expr
|
274
|
+
@d3.where {:e < 5}.select_sql.should ==
|
275
|
+
"SELECT * FROM test WHERE (a = 1) AND (e < 5)"
|
276
|
+
end
|
277
|
+
|
278
|
+
specify "should raise if the dataset is grouped" do
|
279
|
+
proc {@dataset.group(:t).where(:a => 1)}.should raise_error
|
280
|
+
end
|
281
|
+
|
282
|
+
specify "should accept ranges" do
|
283
|
+
@dataset.filter(:id => 4..7).sql.should ==
|
284
|
+
'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
|
285
|
+
@dataset.filter(:id => 4...7).sql.should ==
|
286
|
+
'SELECT * FROM test WHERE (id >= 4 AND id < 7)'
|
287
|
+
|
288
|
+
@dataset.filter {:id == (4..7)}.sql.should ==
|
289
|
+
'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
|
290
|
+
|
291
|
+
@dataset.filter {:id.in?(4..7)}.sql.should ==
|
292
|
+
'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
|
293
|
+
|
294
|
+
@dataset.filter(:table__id => 4..7).sql.should ==
|
295
|
+
'SELECT * FROM test WHERE (table.id >= 4 AND table.id <= 7)'
|
296
|
+
@dataset.filter(:table__id => 4...7).sql.should ==
|
297
|
+
'SELECT * FROM test WHERE (table.id >= 4 AND table.id < 7)'
|
298
|
+
|
299
|
+
@dataset.filter {:table__id == (4..7)}.sql.should ==
|
300
|
+
'SELECT * FROM test WHERE (table.id >= 4 AND table.id <= 7)'
|
301
|
+
@dataset.filter {:table__id.in?(4..7)}.sql.should ==
|
302
|
+
'SELECT * FROM test WHERE (table.id >= 4 AND table.id <= 7)'
|
303
|
+
end
|
304
|
+
|
305
|
+
specify "should accept nil" do
|
306
|
+
@dataset.filter(:owner_id => nil).sql.should ==
|
307
|
+
'SELECT * FROM test WHERE (owner_id IS NULL)'
|
308
|
+
|
309
|
+
@dataset.filter{:owner_id.nil?}.sql.should ==
|
310
|
+
'SELECT * FROM test WHERE (owner_id IS NULL)'
|
311
|
+
end
|
312
|
+
|
313
|
+
specify "should accept a subquery" do
|
314
|
+
# select all countries that have GDP greater than the average for Asia
|
315
|
+
@dataset.filter('gdp > ?', @d1.select(:gdp.AVG)).sql.should ==
|
316
|
+
"SELECT * FROM test WHERE gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia'))"
|
317
|
+
|
318
|
+
@dataset.filter(:id => @d1.select(:id)).sql.should ==
|
319
|
+
"SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
|
320
|
+
end
|
321
|
+
|
322
|
+
specify "should accept a subquery for an EXISTS clause" do
|
323
|
+
a = @dataset.filter {:price < 100}
|
324
|
+
@dataset.filter(a.exists).sql.should ==
|
325
|
+
'SELECT * FROM test WHERE EXISTS (SELECT 1 FROM test WHERE (price < 100))'
|
326
|
+
end
|
327
|
+
|
328
|
+
specify "should accept proc expressions" do
|
329
|
+
d = @d1.select(:gdp.AVG)
|
330
|
+
@dataset.filter {:gdp > d}.sql.should ==
|
331
|
+
"SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
|
332
|
+
|
333
|
+
@dataset.filter {:id.in(4..7)}.sql.should ==
|
334
|
+
'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
|
335
|
+
|
336
|
+
@dataset.filter {:c == 3}.sql.should ==
|
337
|
+
'SELECT * FROM test WHERE (c = 3)'
|
338
|
+
|
339
|
+
@dataset.filter {:id == :items__id}.sql.should ==
|
340
|
+
'SELECT * FROM test WHERE (id = items.id)'
|
341
|
+
|
342
|
+
@dataset.filter {:a < 1}.sql.should ==
|
343
|
+
'SELECT * FROM test WHERE (a < 1)'
|
344
|
+
|
345
|
+
@dataset.filter {:a != 1}.sql.should ==
|
346
|
+
'SELECT * FROM test WHERE (NOT (a = 1))'
|
347
|
+
|
348
|
+
@dataset.filter {:a >= 1 && :b <= 2}.sql.should ==
|
349
|
+
'SELECT * FROM test WHERE ((a >= 1) AND (b <= 2))'
|
350
|
+
|
351
|
+
@dataset.filter {:c.like 'ABC%'}.sql.should ==
|
352
|
+
"SELECT * FROM test WHERE (c LIKE 'ABC%')"
|
353
|
+
|
354
|
+
@dataset.filter {:c.like? 'ABC%'}.sql.should ==
|
355
|
+
"SELECT * FROM test WHERE (c LIKE 'ABC%')"
|
356
|
+
end
|
357
|
+
|
358
|
+
specify "should raise if receiving a single boolean value" do
|
359
|
+
# the result of erroneous use of comparison not in a block
|
360
|
+
# so instead of filter{:x == y} someone writes filter(:x == y)
|
361
|
+
|
362
|
+
proc {@dataset.filter(:a == 1)}.should raise_error(Sequel::Error::InvalidFilter)
|
363
|
+
proc {@dataset.filter(:a != 1)}.should raise_error(Sequel::Error::InvalidFilter)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
context "Dataset#or" do
|
368
|
+
setup do
|
369
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
370
|
+
@d1 = @dataset.where(:x => 1)
|
371
|
+
end
|
372
|
+
|
373
|
+
specify "should raise if no filter exists" do
|
374
|
+
proc {@dataset.or(:a => 1)}.should raise_error(Sequel::Error)
|
375
|
+
end
|
376
|
+
|
377
|
+
specify "should add an alternative expression to the where clause" do
|
378
|
+
@d1.or(:y => 2).sql.should ==
|
379
|
+
'SELECT * FROM test WHERE (x = 1) OR (y = 2)'
|
380
|
+
end
|
381
|
+
|
382
|
+
specify "should accept all forms of filters" do
|
383
|
+
# probably not exhaustive, but good enough
|
384
|
+
@d1.or('(y > ?)', 2).sql.should ==
|
385
|
+
'SELECT * FROM test WHERE (x = 1) OR (y > 2)'
|
386
|
+
|
387
|
+
(@d1.or {:yy > 3}).sql.should ==
|
388
|
+
'SELECT * FROM test WHERE (x = 1) OR (yy > 3)'
|
389
|
+
end
|
390
|
+
|
391
|
+
specify "should correctly add parens to give predictable results" do
|
392
|
+
@d1.filter(:y => 2).or(:z => 3).sql.should ==
|
393
|
+
'SELECT * FROM test WHERE ((x = 1) AND (y = 2)) OR (z = 3)'
|
394
|
+
|
395
|
+
@d1.or(:y => 2).filter(:z => 3).sql.should ==
|
396
|
+
'SELECT * FROM test WHERE ((x = 1) OR (y = 2)) AND (z = 3)'
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "Dataset#and" do
|
401
|
+
setup do
|
402
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
403
|
+
@d1 = @dataset.where(:x => 1)
|
404
|
+
end
|
405
|
+
|
406
|
+
specify "should raise if no filter exists" do
|
407
|
+
proc {@dataset.and(:a => 1)}.should raise_error(Sequel::Error)
|
408
|
+
end
|
409
|
+
|
410
|
+
specify "should add an alternative expression to the where clause" do
|
411
|
+
@d1.and(:y => 2).sql.should ==
|
412
|
+
'SELECT * FROM test WHERE (x = 1) AND (y = 2)'
|
413
|
+
end
|
414
|
+
|
415
|
+
specify "should accept all forms of filters" do
|
416
|
+
# probably not exhaustive, but good enough
|
417
|
+
@d1.and('(y > ?)', 2).sql.should ==
|
418
|
+
'SELECT * FROM test WHERE (x = 1) AND (y > 2)'
|
419
|
+
|
420
|
+
(@d1.and {:yy > 3}).sql.should ==
|
421
|
+
'SELECT * FROM test WHERE (x = 1) AND (yy > 3)'
|
422
|
+
end
|
423
|
+
|
424
|
+
specify "should correctly add parens to give predictable results" do
|
425
|
+
@d1.or(:y => 2).and(:z => 3).sql.should ==
|
426
|
+
'SELECT * FROM test WHERE ((x = 1) OR (y = 2)) AND (z = 3)'
|
427
|
+
|
428
|
+
@d1.and(:y => 2).or(:z => 3).sql.should ==
|
429
|
+
'SELECT * FROM test WHERE ((x = 1) AND (y = 2)) OR (z = 3)'
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
context "Dataset#exclude" do
|
434
|
+
setup do
|
435
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
436
|
+
end
|
437
|
+
|
438
|
+
specify "should correctly include the NOT operator when one condition is given" do
|
439
|
+
@dataset.exclude(:region=>'Asia').select_sql.should ==
|
440
|
+
"SELECT * FROM test WHERE (NOT (region = 'Asia'))"
|
441
|
+
end
|
442
|
+
|
443
|
+
specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
|
444
|
+
@dataset.exclude(:region => 'Asia', :name => 'Japan').select_sql.
|
445
|
+
should match(Regexp.union(/WHERE \(NOT \(\(region = 'Asia'\) AND \(name = 'Japan'\)\)\)/,
|
446
|
+
/WHERE \(NOT \(\(name = 'Japan'\) AND \(region = 'Asia'\)\)\)/))
|
447
|
+
end
|
448
|
+
|
449
|
+
specify "should parenthesize a single string condition correctly" do
|
450
|
+
@dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should ==
|
451
|
+
"SELECT * FROM test WHERE (NOT (region = 'Asia' AND name = 'Japan'))"
|
452
|
+
end
|
453
|
+
|
454
|
+
specify "should parenthesize an array condition correctly" do
|
455
|
+
@dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should ==
|
456
|
+
"SELECT * FROM test WHERE (NOT (region = 'Asia' AND name = 'Japan'))"
|
457
|
+
end
|
458
|
+
|
459
|
+
specify "should correctly parenthesize when it is used twice" do
|
460
|
+
@dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should ==
|
461
|
+
"SELECT * FROM test WHERE (NOT (region = 'Asia')) AND (NOT (name = 'Japan'))"
|
462
|
+
end
|
463
|
+
|
464
|
+
specify "should support proc expressions" do
|
465
|
+
@dataset.exclude {:id == (6...12)}.sql.should ==
|
466
|
+
'SELECT * FROM test WHERE (NOT ((id >= 6 AND id < 12)))'
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context "Dataset#having" do
|
471
|
+
setup do
|
472
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
473
|
+
@grouped = @dataset.group(:region).select(:region, :population.SUM, :gdp.AVG)
|
474
|
+
@d1 = @grouped.having('sum(population) > 10')
|
475
|
+
@d2 = @grouped.having(:region => 'Asia')
|
476
|
+
@columns = "region, sum(population), avg(gdp)"
|
477
|
+
end
|
478
|
+
|
479
|
+
specify "should raise if the dataset is not grouped" do
|
480
|
+
proc {@dataset.having('avg(gdp) > 10')}.should raise_error
|
481
|
+
end
|
482
|
+
|
483
|
+
specify "should affect select statements" do
|
484
|
+
@d1.select_sql.should ==
|
485
|
+
"SELECT #{@columns} FROM test GROUP BY region HAVING sum(population) > 10"
|
486
|
+
end
|
487
|
+
|
488
|
+
specify "should support proc expressions" do
|
489
|
+
@grouped.having {:sum[:population] > 10}.sql.should ==
|
490
|
+
"SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
context "a grouped dataset" do
|
495
|
+
setup do
|
496
|
+
@dataset = Sequel::Dataset.new(nil).from(:test).group(:type_id)
|
497
|
+
end
|
498
|
+
|
499
|
+
specify "should raise when trying to generate an update statement" do
|
500
|
+
proc {@dataset.update_sql(:id => 0)}.should raise_error
|
501
|
+
end
|
502
|
+
|
503
|
+
specify "should raise when trying to generate a delete statement" do
|
504
|
+
proc {@dataset.delete_sql}.should raise_error
|
505
|
+
end
|
506
|
+
|
507
|
+
specify "should specify the grouping in generated select statement" do
|
508
|
+
@dataset.select_sql.should ==
|
509
|
+
"SELECT * FROM test GROUP BY type_id"
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
context "Dataset#group_by" do
|
514
|
+
setup do
|
515
|
+
@dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
|
516
|
+
end
|
517
|
+
|
518
|
+
specify "should raise when trying to generate an update statement" do
|
519
|
+
proc {@dataset.update_sql(:id => 0)}.should raise_error
|
520
|
+
end
|
521
|
+
|
522
|
+
specify "should raise when trying to generate a delete statement" do
|
523
|
+
proc {@dataset.delete_sql}.should raise_error
|
524
|
+
end
|
525
|
+
|
526
|
+
specify "should specify the grouping in generated select statement" do
|
527
|
+
@dataset.select_sql.should ==
|
528
|
+
"SELECT * FROM test GROUP BY type_id"
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
context "Dataset#literal" do
|
533
|
+
setup do
|
534
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
535
|
+
end
|
536
|
+
|
537
|
+
specify "should escape strings properly" do
|
538
|
+
@dataset.literal('abc').should == "'abc'"
|
539
|
+
@dataset.literal('a"x"bc').should == "'a\"x\"bc'"
|
540
|
+
@dataset.literal("a'bc").should == "'a''bc'"
|
541
|
+
@dataset.literal("a''bc").should == "'a''''bc'"
|
542
|
+
end
|
543
|
+
|
544
|
+
specify "should literalize numbers properly" do
|
545
|
+
@dataset.literal(1).should == "1"
|
546
|
+
@dataset.literal(1.5).should == "1.5"
|
547
|
+
end
|
548
|
+
|
549
|
+
specify "should literalize nil as NULL" do
|
550
|
+
@dataset.literal(nil).should == "NULL"
|
551
|
+
end
|
552
|
+
|
553
|
+
specify "should literalize an array properly" do
|
554
|
+
@dataset.literal([]).should == "NULL"
|
555
|
+
@dataset.literal([1, 'abc', 3]).should == "1, 'abc', 3"
|
556
|
+
@dataset.literal([1, "a'b''c", 3]).should == "1, 'a''b''''c', 3"
|
557
|
+
end
|
558
|
+
|
559
|
+
specify "should literalize symbols as column references" do
|
560
|
+
@dataset.literal(:name).should == "name"
|
561
|
+
@dataset.literal(:items__name).should == "items.name"
|
562
|
+
end
|
563
|
+
|
564
|
+
specify "should raise an error for unsupported types" do
|
565
|
+
proc {@dataset.literal({})}.should raise_error
|
566
|
+
end
|
567
|
+
|
568
|
+
specify "should literalize datasets as subqueries" do
|
569
|
+
d = @dataset.from(:test)
|
570
|
+
d.literal(d).should == "(#{d.sql})"
|
571
|
+
end
|
572
|
+
|
573
|
+
specify "should literalize Time properly" do
|
574
|
+
t = Time.now
|
575
|
+
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
|
576
|
+
@dataset.literal(t).should == s
|
577
|
+
end
|
578
|
+
|
579
|
+
specify "should literalize Date properly" do
|
580
|
+
d = Date.today
|
581
|
+
s = d.strftime("DATE '%Y-%m-%d'")
|
582
|
+
@dataset.literal(d).should == s
|
583
|
+
end
|
584
|
+
|
585
|
+
specify "should not literalize expression strings" do
|
586
|
+
@dataset.literal('col1 + 2'.expr).should == 'col1 + 2'
|
587
|
+
|
588
|
+
@dataset.update_sql(:a => 'a + 2'.expr).should ==
|
589
|
+
'UPDATE test SET a = a + 2'
|
590
|
+
end
|
591
|
+
|
592
|
+
specify "should literalize BigDecimal instances correctly" do
|
593
|
+
@dataset.literal(BigDecimal.new("80")).should == "80.0"
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
context "Dataset#from" do
|
598
|
+
setup do
|
599
|
+
@dataset = Sequel::Dataset.new(nil)
|
600
|
+
end
|
601
|
+
|
602
|
+
specify "should accept a Dataset" do
|
603
|
+
proc {@dataset.from(@dataset)}.should_not raise_error
|
604
|
+
end
|
605
|
+
|
606
|
+
specify "should format a Dataset as a subquery if it has had options set" do
|
607
|
+
@dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should ==
|
608
|
+
"SELECT * FROM (SELECT * FROM a WHERE (a = 1)) t1"
|
609
|
+
end
|
610
|
+
|
611
|
+
specify "should automatically alias sub-queries" do
|
612
|
+
@dataset.from(@dataset.from(:a).group(:b)).select_sql.should ==
|
613
|
+
"SELECT * FROM (SELECT * FROM a GROUP BY b) t1"
|
614
|
+
|
615
|
+
d1 = @dataset.from(:a).group(:b)
|
616
|
+
d2 = @dataset.from(:c).group(:d)
|
617
|
+
|
618
|
+
@dataset.from(d1, d2).sql.should ==
|
619
|
+
"SELECT * FROM (SELECT * FROM a GROUP BY b) t1, (SELECT * FROM c GROUP BY d) t2"
|
620
|
+
end
|
621
|
+
|
622
|
+
specify "should accept a hash for aliasing" do
|
623
|
+
@dataset.from(:a => :b).sql.should ==
|
624
|
+
"SELECT * FROM a b"
|
625
|
+
|
626
|
+
@dataset.from(@dataset.from(:a).group(:b) => :c).sql.should ==
|
627
|
+
"SELECT * FROM (SELECT * FROM a GROUP BY b) c"
|
628
|
+
end
|
629
|
+
|
630
|
+
specify "should use the relevant table name if given a simple dataset" do
|
631
|
+
@dataset.from(@dataset.from(:a)).select_sql.should ==
|
632
|
+
"SELECT * FROM a"
|
633
|
+
end
|
634
|
+
|
635
|
+
specify "should raise if no source is given" do
|
636
|
+
proc {@dataset.from(@dataset.from).select_sql}.should raise_error(Sequel::Error)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
context "Dataset#select" do
|
641
|
+
setup do
|
642
|
+
@d = Sequel::Dataset.new(nil).from(:test)
|
643
|
+
end
|
644
|
+
|
645
|
+
specify "should accept variable arity" do
|
646
|
+
@d.select(:name).sql.should == 'SELECT name FROM test'
|
647
|
+
@d.select(:a, :b, :test__c).sql.should == 'SELECT a, b, test.c FROM test'
|
648
|
+
end
|
649
|
+
|
650
|
+
specify "should accept symbols and literal strings" do
|
651
|
+
@d.select('aaa'.lit).sql.should == 'SELECT aaa FROM test'
|
652
|
+
@d.select(:a, 'b'.lit).sql.should == 'SELECT a, b FROM test'
|
653
|
+
@d.select(:test__cc, 'test.d AS e'.lit).sql.should ==
|
654
|
+
'SELECT test.cc, test.d AS e FROM test'
|
655
|
+
@d.select('test.d AS e'.lit, :test__cc).sql.should ==
|
656
|
+
'SELECT test.d AS e, test.cc FROM test'
|
657
|
+
|
658
|
+
# symbol helpers
|
659
|
+
@d.select(:test.ALL).sql.should ==
|
660
|
+
'SELECT test.* FROM test'
|
661
|
+
@d.select(:test__name.AS(:n)).sql.should ==
|
662
|
+
'SELECT test.name AS n FROM test'
|
663
|
+
@d.select(:test__name___n).sql.should ==
|
664
|
+
'SELECT test.name AS n FROM test'
|
665
|
+
end
|
666
|
+
|
667
|
+
specify "should use the wildcard if no arguments are given" do
|
668
|
+
@d.select.sql.should == 'SELECT * FROM test'
|
669
|
+
end
|
670
|
+
|
671
|
+
specify "should accept a hash for AS values" do
|
672
|
+
@d.select(:name => 'n', :__ggh => 'age').sql.should =~
|
673
|
+
/SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
|
674
|
+
end
|
675
|
+
|
676
|
+
specify "should overrun the previous select option" do
|
677
|
+
@d.select(:a, :b, :c).select.sql.should == 'SELECT * FROM test'
|
678
|
+
@d.select(:price).select(:name).sql.should == 'SELECT name FROM test'
|
679
|
+
end
|
680
|
+
|
681
|
+
specify "should accept arbitrary objects and literalize them correctly" do
|
682
|
+
@d.select(1, :a, 't').sql.should == "SELECT 1, a, 't' FROM test"
|
683
|
+
|
684
|
+
@d.select(nil, :sum[:t], :x___y).sql.should == "SELECT NULL, sum(t), x AS y FROM test"
|
685
|
+
|
686
|
+
@d.select(nil, 1, :x => :y).sql.should == "SELECT NULL, 1, x AS y FROM test"
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
context "Dataset#order" do
|
691
|
+
setup do
|
692
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
693
|
+
end
|
694
|
+
|
695
|
+
specify "should include an ORDER BY clause in the select statement" do
|
696
|
+
@dataset.order(:name).sql.should ==
|
697
|
+
'SELECT * FROM test ORDER BY name'
|
698
|
+
end
|
699
|
+
|
700
|
+
specify "should accept multiple arguments" do
|
701
|
+
@dataset.order(:name, :price.DESC).sql.should ==
|
702
|
+
'SELECT * FROM test ORDER BY name, price DESC'
|
703
|
+
end
|
704
|
+
|
705
|
+
specify "should overrun a previous ordering" do
|
706
|
+
@dataset.order(:name).order(:stamp).sql.should ==
|
707
|
+
'SELECT * FROM test ORDER BY stamp'
|
708
|
+
end
|
709
|
+
|
710
|
+
specify "should accept a string" do
|
711
|
+
@dataset.order('dada ASC'.lit).sql.should ==
|
712
|
+
'SELECT * FROM test ORDER BY dada ASC'
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
context "Dataset#order_by" do
|
717
|
+
setup do
|
718
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
719
|
+
end
|
720
|
+
|
721
|
+
specify "should include an ORDER BY clause in the select statement" do
|
722
|
+
@dataset.order_by(:name).sql.should ==
|
723
|
+
'SELECT * FROM test ORDER BY name'
|
724
|
+
end
|
725
|
+
|
726
|
+
specify "should accept multiple arguments" do
|
727
|
+
@dataset.order_by(:name, :price.DESC).sql.should ==
|
728
|
+
'SELECT * FROM test ORDER BY name, price DESC'
|
729
|
+
end
|
730
|
+
|
731
|
+
specify "should overrun a previous ordering" do
|
732
|
+
@dataset.order_by(:name).order(:stamp).sql.should ==
|
733
|
+
'SELECT * FROM test ORDER BY stamp'
|
734
|
+
end
|
735
|
+
|
736
|
+
specify "should accept a string" do
|
737
|
+
@dataset.order_by('dada ASC'.lit).sql.should ==
|
738
|
+
'SELECT * FROM test ORDER BY dada ASC'
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
context "Dataset#reverse_order" do
|
743
|
+
setup do
|
744
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
745
|
+
end
|
746
|
+
|
747
|
+
specify "should use DESC as default order" do
|
748
|
+
@dataset.reverse_order(:name).sql.should ==
|
749
|
+
'SELECT * FROM test ORDER BY name DESC'
|
750
|
+
end
|
751
|
+
|
752
|
+
specify "should invert the order given" do
|
753
|
+
@dataset.reverse_order(:name.DESC).sql.should ==
|
754
|
+
'SELECT * FROM test ORDER BY name'
|
755
|
+
end
|
756
|
+
|
757
|
+
specify "should accept multiple arguments" do
|
758
|
+
@dataset.reverse_order(:name, :price.DESC).sql.should ==
|
759
|
+
'SELECT * FROM test ORDER BY name DESC, price'
|
760
|
+
end
|
761
|
+
|
762
|
+
specify "should reverse a previous ordering if no arguments are given" do
|
763
|
+
@dataset.order(:name).reverse_order.sql.should ==
|
764
|
+
'SELECT * FROM test ORDER BY name DESC'
|
765
|
+
@dataset.order(:clumsy.DESC, :fool).reverse_order.sql.should ==
|
766
|
+
'SELECT * FROM test ORDER BY clumsy, fool DESC'
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
context "Dataset#limit" do
|
771
|
+
setup do
|
772
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
773
|
+
end
|
774
|
+
|
775
|
+
specify "should include a LIMIT clause in the select statement" do
|
776
|
+
@dataset.limit(10).sql.should ==
|
777
|
+
'SELECT * FROM test LIMIT 10'
|
778
|
+
end
|
779
|
+
|
780
|
+
specify "should accept ranges" do
|
781
|
+
@dataset.limit(3..7).sql.should ==
|
782
|
+
'SELECT * FROM test LIMIT 5 OFFSET 3'
|
783
|
+
|
784
|
+
@dataset.limit(3...7).sql.should ==
|
785
|
+
'SELECT * FROM test LIMIT 4 OFFSET 3'
|
786
|
+
end
|
787
|
+
|
788
|
+
specify "should include an offset if a second argument is given" do
|
789
|
+
@dataset.limit(6, 10).sql.should ==
|
790
|
+
'SELECT * FROM test LIMIT 6 OFFSET 10'
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
context "Dataset#naked" do
|
795
|
+
setup do
|
796
|
+
@d1 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4})
|
797
|
+
@d2 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4}).set_model(Object)
|
798
|
+
end
|
799
|
+
|
800
|
+
specify "should return a clone with :naked option set" do
|
801
|
+
naked = @d1.naked
|
802
|
+
naked.opts[:naked].should be_true
|
803
|
+
end
|
804
|
+
|
805
|
+
specify "should remove any existing reference to a model class" do
|
806
|
+
naked = @d2.naked
|
807
|
+
naked.opts[:models].should be_nil
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
context "Dataset#qualified_column_name" do
|
812
|
+
setup do
|
813
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
814
|
+
end
|
815
|
+
|
816
|
+
specify "should return the same if already qualified" do
|
817
|
+
@dataset.qualified_column_name('test.a'.lit, :items).should == 'test.a'
|
818
|
+
@dataset.qualified_column_name(:ccc__b, :items).should == :ccc__b
|
819
|
+
end
|
820
|
+
|
821
|
+
specify "should qualify the column with the supplied table name" do
|
822
|
+
@dataset.qualified_column_name('a'.lit, :items).to_s(@dataset).should == 'items.a'
|
823
|
+
@dataset.qualified_column_name(:b1, :items).to_s(@dataset).should == 'items.b1'
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
class DummyDataset < Sequel::Dataset
|
828
|
+
VALUES = [
|
829
|
+
{:a => 1, :b => 2},
|
830
|
+
{:a => 3, :b => 4},
|
831
|
+
{:a => 5, :b => 6}
|
832
|
+
]
|
833
|
+
def fetch_rows(sql, &block)
|
834
|
+
VALUES.each(&block)
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
context "Dataset#map" do
|
839
|
+
setup do
|
840
|
+
@d = DummyDataset.new(nil).from(:items)
|
841
|
+
end
|
842
|
+
|
843
|
+
specify "should provide the usual functionality if no argument is given" do
|
844
|
+
@d.map {|n| n[:a] + n[:b]}.should == [3, 7, 11]
|
845
|
+
end
|
846
|
+
|
847
|
+
specify "should map using #[column name] if column name is given" do
|
848
|
+
@d.map(:a).should == [1, 3, 5]
|
849
|
+
end
|
850
|
+
|
851
|
+
specify "should return the complete dataset values if nothing is given" do
|
852
|
+
@d.map.should == DummyDataset::VALUES
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
context "Dataset#to_hash" do
|
857
|
+
setup do
|
858
|
+
@d = DummyDataset.new(nil).from(:items)
|
859
|
+
end
|
860
|
+
|
861
|
+
specify "should provide a hash with the first column as key and the second as value" do
|
862
|
+
@d.to_hash(:a, :b).should == {1 => 2, 3 => 4, 5 => 6}
|
863
|
+
@d.to_hash(:b, :a).should == {2 => 1, 4 => 3, 6 => 5}
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
context "Dataset#uniq" do
|
868
|
+
setup do
|
869
|
+
@dataset = Sequel::Dataset.new(nil).from(:test).select(:name)
|
870
|
+
end
|
871
|
+
|
872
|
+
specify "should include DISTINCT clause in statement" do
|
873
|
+
@dataset.uniq.sql.should == 'SELECT DISTINCT name FROM test'
|
874
|
+
end
|
875
|
+
|
876
|
+
specify "should be aliased by Dataset#distinct" do
|
877
|
+
@dataset.distinct.sql.should == 'SELECT DISTINCT name FROM test'
|
878
|
+
end
|
879
|
+
|
880
|
+
specify "should accept an expression list" do
|
881
|
+
@dataset.uniq(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
|
882
|
+
|
883
|
+
@dataset.uniq(:stamp.cast_as(:integer), :node_id).sql.should == 'SELECT DISTINCT ON (cast(stamp AS integer), node_id) name FROM test'
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|
887
|
+
context "Dataset#count" do
|
888
|
+
setup do
|
889
|
+
@c = Class.new(Sequel::Dataset) do
|
890
|
+
def self.sql
|
891
|
+
@@sql
|
892
|
+
end
|
893
|
+
|
894
|
+
def fetch_rows(sql)
|
895
|
+
@@sql = sql
|
896
|
+
yield({1 => 1})
|
897
|
+
end
|
898
|
+
end
|
899
|
+
@dataset = @c.new(nil).from(:test)
|
900
|
+
end
|
901
|
+
|
902
|
+
specify "should format SQL properly" do
|
903
|
+
@dataset.count.should == 1
|
904
|
+
@c.sql.should == 'SELECT COUNT(*) FROM test'
|
905
|
+
end
|
906
|
+
|
907
|
+
specify "should be aliased by #size" do
|
908
|
+
@dataset.size.should == 1
|
909
|
+
end
|
910
|
+
|
911
|
+
specify "should include the where clause if it's there" do
|
912
|
+
@dataset.filter {:abc < 30}.count.should == 1
|
913
|
+
@c.sql.should == 'SELECT COUNT(*) FROM test WHERE (abc < 30)'
|
914
|
+
end
|
915
|
+
|
916
|
+
specify "should count properly for datasets with fixed sql" do
|
917
|
+
@dataset.opts[:sql] = "select abc from xyz"
|
918
|
+
@dataset.count.should == 1
|
919
|
+
@c.sql.should == "SELECT COUNT(*) FROM (select abc from xyz) AS c"
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
|
924
|
+
context "Dataset#group_and_count" do
|
925
|
+
setup do
|
926
|
+
@c = Class.new(Sequel::Dataset) do
|
927
|
+
def self.sql
|
928
|
+
@@sql
|
929
|
+
end
|
930
|
+
|
931
|
+
def fetch_rows(sql)
|
932
|
+
@@sql = sql
|
933
|
+
yield({1 => 1})
|
934
|
+
end
|
935
|
+
end
|
936
|
+
@ds = @c.new(nil).from(:test)
|
937
|
+
end
|
938
|
+
|
939
|
+
specify "should format SQL properly" do
|
940
|
+
@ds.group_and_count(:name).sql.should == "SELECT name, count(name) AS count FROM test GROUP BY name ORDER BY count"
|
941
|
+
end
|
942
|
+
end
|
943
|
+
context "Dataset#empty?" do
|
944
|
+
specify "should return true if #count == 0" do
|
945
|
+
@c = Class.new(Sequel::Dataset) do
|
946
|
+
def count
|
947
|
+
0
|
948
|
+
end
|
949
|
+
end
|
950
|
+
@dataset = @c.new(nil).from(:test)
|
951
|
+
@dataset.empty?.should be_true
|
952
|
+
|
953
|
+
@c = Class.new(Sequel::Dataset) do
|
954
|
+
def count
|
955
|
+
1
|
956
|
+
end
|
957
|
+
end
|
958
|
+
@dataset = @c.new(nil).from(:test)
|
959
|
+
@dataset.empty?.should be_false
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
context "Dataset#join_table" do
|
964
|
+
setup do
|
965
|
+
@d = Sequel::Dataset.new(nil).from(:items)
|
966
|
+
end
|
967
|
+
|
968
|
+
specify "should format the JOIN clause properly" do
|
969
|
+
@d.join_table(:left_outer, :categories, :category_id => :id).sql.should ==
|
970
|
+
'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
|
971
|
+
end
|
972
|
+
|
973
|
+
specify "should include WHERE clause if applicable" do
|
974
|
+
@d.filter {:price < 100}.join_table(:right_outer, :categories, :category_id => :id).sql.should ==
|
975
|
+
'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id) WHERE (price < 100)'
|
976
|
+
end
|
977
|
+
|
978
|
+
specify "should include ORDER BY clause if applicable" do
|
979
|
+
@d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should ==
|
980
|
+
'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id) ORDER BY stamp'
|
981
|
+
end
|
982
|
+
|
983
|
+
specify "should support multiple joins" do
|
984
|
+
@d.join_table(:inner, :b, :items_id).join_table(:left_outer, :c, :b_id => :b__id).sql.should ==
|
985
|
+
'SELECT * FROM items INNER JOIN b ON (b.items_id = items.id) LEFT OUTER JOIN c ON (c.b_id = b.id)'
|
986
|
+
end
|
987
|
+
|
988
|
+
specify "should use id as implicit relation primary key if omitted" do
|
989
|
+
@d.join_table(:left_outer, :categories, :category_id).sql.should ==
|
990
|
+
@d.join_table(:left_outer, :categories, :category_id => :id).sql
|
991
|
+
|
992
|
+
# when doing multiple joins, id should be qualified using the last joined table
|
993
|
+
@d.join_table(:right_outer, :b, :items_id).join_table(:full_outer, :c, :b_id).sql.should ==
|
994
|
+
'SELECT * FROM items RIGHT OUTER JOIN b ON (b.items_id = items.id) FULL OUTER JOIN c ON (c.b_id = b.id)'
|
995
|
+
end
|
996
|
+
|
997
|
+
specify "should support left outer joins" do
|
998
|
+
@d.join_table(:left_outer, :categories, :category_id).sql.should ==
|
999
|
+
'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
|
1000
|
+
|
1001
|
+
@d.left_outer_join(:categories, :category_id).sql.should ==
|
1002
|
+
'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
specify "should support right outer joins" do
|
1006
|
+
@d.join_table(:right_outer, :categories, :category_id).sql.should ==
|
1007
|
+
'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id)'
|
1008
|
+
|
1009
|
+
@d.right_outer_join(:categories, :category_id).sql.should ==
|
1010
|
+
'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id)'
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
specify "should support full outer joins" do
|
1014
|
+
@d.join_table(:full_outer, :categories, :category_id).sql.should ==
|
1015
|
+
'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id)'
|
1016
|
+
|
1017
|
+
@d.full_outer_join(:categories, :category_id).sql.should ==
|
1018
|
+
'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id)'
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
specify "should support inner joins" do
|
1022
|
+
@d.join_table(:inner, :categories, :category_id).sql.should ==
|
1023
|
+
'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
|
1024
|
+
|
1025
|
+
@d.inner_join(:categories, :category_id).sql.should ==
|
1026
|
+
'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
specify "should default to an inner join" do
|
1030
|
+
@d.join_table(nil, :categories, :category_id).sql.should ==
|
1031
|
+
'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
|
1032
|
+
|
1033
|
+
@d.join(:categories, :category_id).sql.should ==
|
1034
|
+
'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
specify "should raise if an invalid join type is specified" do
|
1038
|
+
proc {@d.join_table(:invalid, :a, :b)}.should raise_error(Sequel::Error)
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
specify "should treat aliased tables correctly" do
|
1042
|
+
@d.from('stats s').join('players p', :id => :player_id).sql.should ==
|
1043
|
+
'SELECT * FROM stats s INNER JOIN players p ON (p.id = s.player_id)'
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
specify "should allow for arbitrary conditions in the JOIN clause" do
|
1047
|
+
@d.join_table(:left_outer, :categories, :status => 0).sql.should ==
|
1048
|
+
'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status = 0)'
|
1049
|
+
@d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
|
1050
|
+
"SELECT * FROM items LEFT OUTER JOIN categories ON (categories.categorizable_type = 'Post')"
|
1051
|
+
@d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
|
1052
|
+
"SELECT * FROM items LEFT OUTER JOIN categories ON (categories.timestamp = CURRENT_TIMESTAMP)"
|
1053
|
+
@d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
|
1054
|
+
"SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status IN (1, 2, 3))"
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
context "Dataset#[]=" do
|
1059
|
+
setup do
|
1060
|
+
c = Class.new(Sequel::Dataset) do
|
1061
|
+
def last_sql
|
1062
|
+
@@last_sql
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def update(*args)
|
1066
|
+
@@last_sql = update_sql(*args)
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
@d = c.new(nil).from(:items)
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
specify "should perform an update on the specified filter" do
|
1074
|
+
@d[:a => 1] = {:x => 3}
|
1075
|
+
@d.last_sql.should == 'UPDATE items SET x = 3 WHERE (a = 1)'
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
context "Dataset#set" do
|
1080
|
+
setup do
|
1081
|
+
c = Class.new(Sequel::Dataset) do
|
1082
|
+
def last_sql
|
1083
|
+
@@last_sql
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def update(*args, &block)
|
1087
|
+
@@last_sql = update_sql(*args, &block)
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
@d = c.new(nil).from(:items)
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
specify "should act as alias to #update" do
|
1095
|
+
@d.set({:x => 3})
|
1096
|
+
@d.last_sql.should == 'UPDATE items SET x = 3'
|
1097
|
+
|
1098
|
+
@d.set {:x << :x + 1}
|
1099
|
+
@d.last_sql.should == 'UPDATE items SET x = (x + 1)'
|
1100
|
+
|
1101
|
+
@d.set {(:x|1) << (:x|2) + 1}
|
1102
|
+
@d.last_sql.should == 'UPDATE items SET x[1] = (x[2] + 1)'
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
|
1107
|
+
context "Dataset#insert_multiple" do
|
1108
|
+
setup do
|
1109
|
+
c = Class.new(Sequel::Dataset) do
|
1110
|
+
attr_reader :inserts
|
1111
|
+
def insert(arg)
|
1112
|
+
@inserts ||= []
|
1113
|
+
@inserts << arg
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
@d = c.new(nil)
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
specify "should insert all items in the supplied array" do
|
1121
|
+
@d.insert_multiple [:aa, 5, 3, {1 => 2}]
|
1122
|
+
@d.inserts.should == [:aa, 5, 3, {1 => 2}]
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
specify "should pass array items through the supplied block if given" do
|
1126
|
+
a = ["inevitable", "hello", "the ticking clock"]
|
1127
|
+
@d.insert_multiple(a) {|i| i.gsub('l', 'r')}
|
1128
|
+
@d.inserts.should == ["inevitabre", "herro", "the ticking crock"]
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
context "Dataset aggregate methods" do
|
1133
|
+
setup do
|
1134
|
+
c = Class.new(Sequel::Dataset) do
|
1135
|
+
def fetch_rows(sql)
|
1136
|
+
yield({1 => sql})
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
@d = c.new(nil).from(:test)
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
specify "should include min" do
|
1143
|
+
@d.min(:a).should == 'SELECT min(a) AS v FROM test'
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
specify "should include max" do
|
1147
|
+
@d.max(:b).should == 'SELECT max(b) AS v FROM test'
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
specify "should include sum" do
|
1151
|
+
@d.sum(:c).should == 'SELECT sum(c) AS v FROM test'
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
specify "should include avg" do
|
1155
|
+
@d.avg(:d).should == 'SELECT avg(d) AS v FROM test'
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
specify "should accept qualified columns" do
|
1159
|
+
@d.avg(:test__bc).should == 'SELECT avg(test.bc) AS v FROM test'
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
context "Dataset#range" do
|
1164
|
+
setup do
|
1165
|
+
c = Class.new(Sequel::Dataset) do
|
1166
|
+
@@sql = nil
|
1167
|
+
|
1168
|
+
def last_sql; @@sql; end
|
1169
|
+
|
1170
|
+
def fetch_rows(sql)
|
1171
|
+
@@sql = sql
|
1172
|
+
yield(:v1 => 1, :v2 => 10)
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
@d = c.new(nil).from(:test)
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
specify "should generate a correct SQL statement" do
|
1179
|
+
@d.range(:stamp)
|
1180
|
+
@d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"
|
1181
|
+
|
1182
|
+
@d.filter {:price > 100}.range(:stamp)
|
1183
|
+
@d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
specify "should return a range object" do
|
1187
|
+
@d.range(:tryme).should == (1..10)
|
1188
|
+
@d.last_sql.should == "SELECT min(tryme) AS v1, max(tryme) AS v2 FROM test LIMIT 1"
|
1189
|
+
end
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
context "Dataset#range" do
|
1193
|
+
setup do
|
1194
|
+
c = Class.new(Sequel::Dataset) do
|
1195
|
+
@@sql = nil
|
1196
|
+
|
1197
|
+
def last_sql; @@sql; end
|
1198
|
+
|
1199
|
+
def fetch_rows(sql)
|
1200
|
+
@@sql = sql
|
1201
|
+
yield(:v => 1234)
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
@d = c.new(nil).from(:test)
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
specify "should generate a correct SQL statement" do
|
1208
|
+
@d.interval(:stamp)
|
1209
|
+
@d.last_sql.should == "SELECT (max(stamp) - min(stamp)) AS v FROM test LIMIT 1"
|
1210
|
+
|
1211
|
+
@d.filter {:price > 100}.interval(:stamp)
|
1212
|
+
@d.last_sql.should == "SELECT (max(stamp) - min(stamp)) AS v FROM test WHERE (price > 100) LIMIT 1"
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
specify "should return a range object" do
|
1216
|
+
@d.interval(:tryme).should == 1234
|
1217
|
+
@d.last_sql.should == "SELECT (max(tryme) - min(tryme)) AS v FROM test LIMIT 1"
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
context "Dataset#first" do
|
1222
|
+
setup do
|
1223
|
+
@c = Class.new(Sequel::Dataset) do
|
1224
|
+
@@last_dataset = nil
|
1225
|
+
@@last_opts = nil
|
1226
|
+
|
1227
|
+
def self.last_dataset
|
1228
|
+
@@last_dataset
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def self.last_opts
|
1232
|
+
@@last_opts
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
def single_record(opts = nil)
|
1236
|
+
@@last_opts = @opts.merge(opts || {})
|
1237
|
+
{:a => 1, :b => 2}
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
def all
|
1241
|
+
@@last_dataset = self
|
1242
|
+
[{:a => 1, :b => 2}] * @opts[:limit]
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
@d = @c.new(nil).from(:test)
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
specify "should return the first matching record if a hash is specified" do
|
1249
|
+
@d.first(:z => 26).should == {:a => 1, :b => 2}
|
1250
|
+
@c.last_opts[:where].should == ('(z = 26)')
|
1251
|
+
|
1252
|
+
@d.first('z = ?', 15)
|
1253
|
+
@c.last_opts[:where].should == ('z = 15')
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
specify "should return the first matching record if a block is given" do
|
1257
|
+
@d.first {:z > 26}.should == {:a => 1, :b => 2}
|
1258
|
+
@c.last_opts[:where].should == ('(z > 26)')
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
specify "should return a single record if no argument is given" do
|
1262
|
+
@d.first.should == {:a => 1, :b => 2}
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
specify "should set the limit according to the given number" do
|
1266
|
+
@d.first
|
1267
|
+
@c.last_opts[:limit].should == 1
|
1268
|
+
|
1269
|
+
i = rand(10) + 10
|
1270
|
+
@d.first(i)
|
1271
|
+
@c.last_dataset.opts[:limit].should == i
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
specify "should return an array with the records if argument is greater than 1" do
|
1275
|
+
i = rand(10) + 10
|
1276
|
+
r = @d.first(i)
|
1277
|
+
r.should be_a_kind_of(Array)
|
1278
|
+
r.size.should == i
|
1279
|
+
r.each {|row| row.should == {:a => 1, :b => 2}}
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
context "Dataset#last" do
|
1284
|
+
setup do
|
1285
|
+
@c = Class.new(Sequel::Dataset) do
|
1286
|
+
@@last_dataset = nil
|
1287
|
+
|
1288
|
+
def self.last_dataset
|
1289
|
+
@@last_dataset
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
def single_record(opts = nil)
|
1293
|
+
@@last_dataset = clone_merge(opts) if opts
|
1294
|
+
{:a => 1, :b => 2}
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def all
|
1298
|
+
@@last_dataset = self
|
1299
|
+
[{:a => 1, :b => 2}] * @opts[:limit]
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
@d = @c.new(nil).from(:test)
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
specify "should raise if no order is given" do
|
1306
|
+
proc {@d.last}.should raise_error(Sequel::Error)
|
1307
|
+
proc {@d.last(2)}.should raise_error(Sequel::Error)
|
1308
|
+
proc {@d.order(:a).last}.should_not raise_error
|
1309
|
+
proc {@d.order(:a).last(2)}.should_not raise_error
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
specify "should invert the order" do
|
1313
|
+
@d.order(:a).last
|
1314
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:a.DESC])
|
1315
|
+
|
1316
|
+
@d.order(:b.DESC).last
|
1317
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal(:b)
|
1318
|
+
|
1319
|
+
@d.order(:c, :d).last
|
1320
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:c.DESC, :d.DESC])
|
1321
|
+
|
1322
|
+
@d.order(:e.DESC, :f).last
|
1323
|
+
@d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:e, :f.DESC])
|
1324
|
+
end
|
1325
|
+
|
1326
|
+
specify "should return the first matching record if a hash is specified" do
|
1327
|
+
@d.order(:a).last(:z => 26).should == {:a => 1, :b => 2}
|
1328
|
+
@c.last_dataset.opts[:where].should == ('(z = 26)')
|
1329
|
+
|
1330
|
+
@d.order(:a).last('z = ?', 15)
|
1331
|
+
@c.last_dataset.opts[:where].should == ('z = 15')
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
specify "should return a single record if no argument is given" do
|
1335
|
+
@d.order(:a).last.should == {:a => 1, :b => 2}
|
1336
|
+
end
|
1337
|
+
|
1338
|
+
specify "should set the limit according to the given number" do
|
1339
|
+
i = rand(10) + 10
|
1340
|
+
r = @d.order(:a).last(i)
|
1341
|
+
@c.last_dataset.opts[:limit].should == i
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
specify "should return an array with the records if argument is greater than 1" do
|
1345
|
+
i = rand(10) + 10
|
1346
|
+
r = @d.order(:a).last(i)
|
1347
|
+
r.should be_a_kind_of(Array)
|
1348
|
+
r.size.should == i
|
1349
|
+
r.each {|row| row.should == {:a => 1, :b => 2}}
|
1350
|
+
end
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
context "Dataset set operations" do
|
1354
|
+
setup do
|
1355
|
+
@a = Sequel::Dataset.new(nil).from(:a).filter(:z => 1)
|
1356
|
+
@b = Sequel::Dataset.new(nil).from(:b).filter(:z => 2)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
specify "should support UNION and UNION ALL" do
|
1360
|
+
@a.union(@b).sql.should == \
|
1361
|
+
"SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)"
|
1362
|
+
@b.union(@a, true).sql.should == \
|
1363
|
+
"SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
specify "should support INTERSECT and INTERSECT ALL" do
|
1367
|
+
@a.intersect(@b).sql.should == \
|
1368
|
+
"SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)"
|
1369
|
+
@b.intersect(@a, true).sql.should == \
|
1370
|
+
"SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
specify "should support EXCEPT and EXCEPT ALL" do
|
1374
|
+
@a.except(@b).sql.should == \
|
1375
|
+
"SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)"
|
1376
|
+
@b.except(@a, true).sql.should == \
|
1377
|
+
"SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
context "Dataset#[]" do
|
1382
|
+
setup do
|
1383
|
+
@c = Class.new(Sequel::Dataset) do
|
1384
|
+
@@last_dataset = nil
|
1385
|
+
|
1386
|
+
def self.last_dataset
|
1387
|
+
@@last_dataset
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def single_record(opts = nil)
|
1391
|
+
@@last_dataset = opts ? clone_merge(opts) : self
|
1392
|
+
{1 => 2, 3 => 4}
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
@d = @c.new(nil).from(:test)
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
specify "should return a single record filtered according to the given conditions" do
|
1399
|
+
@d[:name => 'didi'].should == {1 => 2, 3 => 4}
|
1400
|
+
@c.last_dataset.opts[:where].should == "(name = 'didi')"
|
1401
|
+
|
1402
|
+
@d[:id => 5..45].should == {1 => 2, 3 => 4}
|
1403
|
+
@c.last_dataset.opts[:where].should == "(id >= 5 AND id <= 45)"
|
1404
|
+
end
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
context "Dataset#single_record" do
|
1408
|
+
setup do
|
1409
|
+
@c = Class.new(Sequel::Dataset) do
|
1410
|
+
def fetch_rows(sql)
|
1411
|
+
yield sql
|
1412
|
+
end
|
1413
|
+
end
|
1414
|
+
@cc = Class.new(@c) do
|
1415
|
+
def fetch_rows(sql); end
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
@d = @c.new(nil).from(:test)
|
1419
|
+
@e = @cc.new(nil).from(:test)
|
1420
|
+
end
|
1421
|
+
|
1422
|
+
specify "should call each and return the first record" do
|
1423
|
+
@d.single_record.should == 'SELECT * FROM test'
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
specify "should pass opts to each" do
|
1427
|
+
@d.single_record(:limit => 3).should == 'SELECT * FROM test LIMIT 3'
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
specify "should return nil if no record is present" do
|
1431
|
+
@e.single_record.should be_nil
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
context "Dataset#single_value" do
|
1436
|
+
setup do
|
1437
|
+
@c = Class.new(Sequel::Dataset) do
|
1438
|
+
def fetch_rows(sql)
|
1439
|
+
yield({1 => sql})
|
1440
|
+
end
|
1441
|
+
end
|
1442
|
+
@cc = Class.new(@c) do
|
1443
|
+
def fetch_rows(sql); end
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
@d = @c.new(nil).from(:test)
|
1447
|
+
@e = @cc.new(nil).from(:test)
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
specify "should call each and return the first value of the first record" do
|
1451
|
+
@d.single_value.should == 'SELECT * FROM test'
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
specify "should pass opts to each" do
|
1455
|
+
@d.single_value(:limit => 3).should == 'SELECT * FROM test LIMIT 3'
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
specify "should return nil" do
|
1459
|
+
@e.single_value.should be_nil
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
context "Dataset#set_row_proc" do
|
1465
|
+
setup do
|
1466
|
+
@c = Class.new(Sequel::Dataset) do
|
1467
|
+
def fetch_rows(sql, &block)
|
1468
|
+
# yield a hash with kind as the 1 bit of a number
|
1469
|
+
(1..10).each {|i| block.call({:kind => i[0]})}
|
1470
|
+
end
|
1471
|
+
end
|
1472
|
+
@dataset = @c.new(nil).from(:items)
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
specify "should cause dataset to pass all rows through the filter" do
|
1476
|
+
@dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
|
1477
|
+
|
1478
|
+
rows = @dataset.all
|
1479
|
+
rows.size.should == 10
|
1480
|
+
|
1481
|
+
rows.each {|r| r[:der].should == (r[:kind] + 2)}
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
specify "should be copied over when dataset is cloned" do
|
1485
|
+
@dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
|
1486
|
+
|
1487
|
+
@dataset.filter(:a => 1).first.should == {:kind => 1, :der => 3}
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
context "Dataset#set_model" do
|
1492
|
+
setup do
|
1493
|
+
@c = Class.new(Sequel::Dataset) do
|
1494
|
+
def fetch_rows(sql, &block)
|
1495
|
+
# yield a hash with kind as the 1 bit of a number
|
1496
|
+
(1..10).each {|i| block.call({:kind => i[0]})}
|
1497
|
+
end
|
1498
|
+
end
|
1499
|
+
@dataset = @c.new(nil).from(:items)
|
1500
|
+
@m = Class.new do
|
1501
|
+
attr_accessor :c, :args
|
1502
|
+
def initialize(c, *args); @c = c; @args = args; end
|
1503
|
+
def ==(o); (@c == o.c) && (@args = o.args); end
|
1504
|
+
end
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
specify "should clear the models hash and restore the stock #each if nil is specified" do
|
1508
|
+
@dataset.set_model(@m)
|
1509
|
+
@dataset.set_model(nil)
|
1510
|
+
@dataset.first.should == {:kind => 1}
|
1511
|
+
@dataset.model_classes.should be_nil
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
specify "should clear the models hash and restore the stock #each if nothing is specified" do
|
1515
|
+
@dataset.set_model(@m)
|
1516
|
+
@dataset.set_model(nil)
|
1517
|
+
@dataset.first.should == {:kind => 1}
|
1518
|
+
@dataset.model_classes.should be_nil
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
specify "should alter #each to provide model instances" do
|
1522
|
+
@dataset.first.should == {:kind => 1}
|
1523
|
+
@dataset.set_model(@m)
|
1524
|
+
@dataset.first.should == @m.new({:kind => 1})
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
specify "should extend the dataset with a #destroy method" do
|
1528
|
+
@dataset.should_not respond_to(:destroy)
|
1529
|
+
@dataset.set_model(@m)
|
1530
|
+
@dataset.should respond_to(:destroy)
|
1531
|
+
end
|
1532
|
+
|
1533
|
+
specify "should set opts[:naked] to nil" do
|
1534
|
+
@dataset.opts[:naked] = true
|
1535
|
+
@dataset.set_model(@m)
|
1536
|
+
@dataset.opts[:naked].should be_nil
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
specify "should send additional arguments to the models' initialize method" do
|
1540
|
+
@dataset.set_model(@m, 7, 6, 5)
|
1541
|
+
@dataset.first.should == @m.new({:kind => 1}, 7, 6, 5)
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
specify "should provide support for polymorphic model instantiation" do
|
1545
|
+
@m1 = Class.new(@m)
|
1546
|
+
@m2 = Class.new(@m)
|
1547
|
+
@dataset.set_model(:kind, 0 => @m1, 1 => @m2)
|
1548
|
+
@dataset.opts[:polymorphic_key].should == :kind
|
1549
|
+
all = @dataset.all
|
1550
|
+
all[0].class.should == @m2
|
1551
|
+
all[1].class.should == @m1
|
1552
|
+
all[2].class.should == @m2
|
1553
|
+
all[3].class.should == @m1
|
1554
|
+
#...
|
1555
|
+
|
1556
|
+
# denude model
|
1557
|
+
@dataset.set_model(nil)
|
1558
|
+
@dataset.first.should == {:kind => 1}
|
1559
|
+
end
|
1560
|
+
|
1561
|
+
specify "should send additional arguments for polymorphic models as well" do
|
1562
|
+
@m1 = Class.new(@m)
|
1563
|
+
@m2 = Class.new(@m)
|
1564
|
+
@dataset.set_model(:kind, {0 => @m1, 1 => @m2}, :hey => :wow)
|
1565
|
+
all = @dataset.all
|
1566
|
+
all[0].class.should == @m2; all[0].args.should == [{:hey => :wow}]
|
1567
|
+
all[1].class.should == @m1; all[1].args.should == [{:hey => :wow}]
|
1568
|
+
all[2].class.should == @m2; all[2].args.should == [{:hey => :wow}]
|
1569
|
+
all[3].class.should == @m1; all[3].args.should == [{:hey => :wow}]
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
specify "should raise for invalid parameters" do
|
1573
|
+
proc {@dataset.set_model('kind')}.should raise_error(ArgumentError)
|
1574
|
+
proc {@dataset.set_model(0)}.should raise_error(ArgumentError)
|
1575
|
+
proc {@dataset.set_model(:kind)}.should raise_error(ArgumentError) # no hash given
|
1576
|
+
end
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
context "Dataset#model_classes" do
|
1580
|
+
setup do
|
1581
|
+
@c = Class.new(Sequel::Dataset) do
|
1582
|
+
# # We don't need that for now
|
1583
|
+
# def fetch_rows(sql, &block)
|
1584
|
+
# (1..10).each(&block)
|
1585
|
+
# end
|
1586
|
+
end
|
1587
|
+
@dataset = @c.new(nil).from(:items)
|
1588
|
+
@m = Class.new do
|
1589
|
+
attr_accessor :c
|
1590
|
+
def initialize(c); @c = c; end
|
1591
|
+
def ==(o); @c == o.c; end
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
specify "should return nil for a naked dataset" do
|
1596
|
+
@dataset.model_classes.should == nil
|
1597
|
+
end
|
1598
|
+
|
1599
|
+
specify "should return a {nil => model_class} hash for a model dataset" do
|
1600
|
+
@dataset.set_model(@m)
|
1601
|
+
@dataset.model_classes.should == {nil => @m}
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
specify "should return the polymorphic hash for a polymorphic model dataset" do
|
1605
|
+
@m1 = Class.new(@m)
|
1606
|
+
@m2 = Class.new(@m)
|
1607
|
+
@dataset.set_model(:key, 0 => @m1, 1 => @m2)
|
1608
|
+
@dataset.model_classes.should == {0 => @m1, 1 => @m2}
|
1609
|
+
end
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
context "Dataset#polymorphic_key" do
|
1613
|
+
setup do
|
1614
|
+
@c = Class.new(Sequel::Dataset) do
|
1615
|
+
# # We don't need this for now
|
1616
|
+
# def fetch_rows(sql, &block)
|
1617
|
+
# (1..10).each(&block)
|
1618
|
+
# end
|
1619
|
+
end
|
1620
|
+
@dataset = @c.new(nil).from(:items)
|
1621
|
+
@m = Class.new do
|
1622
|
+
attr_accessor :c
|
1623
|
+
def initialize(c); @c = c; end
|
1624
|
+
def ==(o); @c == o.c; end
|
1625
|
+
end
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
specify "should return nil for a naked dataset" do
|
1629
|
+
@dataset.polymorphic_key.should be_nil
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
specify "should return the polymorphic key" do
|
1633
|
+
@dataset.set_model(:id, nil => @m)
|
1634
|
+
@dataset.polymorphic_key.should == :id
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
context "A model dataset" do
|
1639
|
+
setup do
|
1640
|
+
@c = Class.new(Sequel::Dataset) do
|
1641
|
+
def fetch_rows(sql, &block)
|
1642
|
+
(1..10).each(&block)
|
1643
|
+
end
|
1644
|
+
end
|
1645
|
+
@dataset = @c.new(nil).from(:items)
|
1646
|
+
@m = Class.new do
|
1647
|
+
attr_accessor :c
|
1648
|
+
def initialize(c); @c = c; end
|
1649
|
+
def ==(o); @c == o.c; end
|
1650
|
+
end
|
1651
|
+
@dataset.set_model(@m)
|
1652
|
+
end
|
1653
|
+
|
1654
|
+
specify "should supply naked records if the naked option is specified" do
|
1655
|
+
@dataset.each {|r| r.class.should == @m}
|
1656
|
+
@dataset.each(:naked => true) {|r| r.class.should == Fixnum}
|
1657
|
+
end
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
context "A polymorphic model dataset" do
|
1661
|
+
setup do
|
1662
|
+
@c = Class.new(Sequel::Dataset) do
|
1663
|
+
def fetch_rows(sql, &block)
|
1664
|
+
(1..10).each {|i| block.call(:bit => i[0])}
|
1665
|
+
end
|
1666
|
+
end
|
1667
|
+
@dataset = @c.new(nil).from(:items)
|
1668
|
+
@m = Class.new do
|
1669
|
+
attr_accessor :c
|
1670
|
+
def initialize(c); @c = c; end
|
1671
|
+
def ==(o); @c == o.c; end
|
1672
|
+
end
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
specify "should use a nil key in the polymorphic hash to specify the default model class" do
|
1676
|
+
@m2 = Class.new(@m)
|
1677
|
+
@dataset.set_model(:bit, nil => @m, 1 => @m2)
|
1678
|
+
all = @dataset.all
|
1679
|
+
all[0].class.should == @m2
|
1680
|
+
all[1].class.should == @m
|
1681
|
+
all[2].class.should == @m2
|
1682
|
+
all[3].class.should == @m
|
1683
|
+
#...
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
specify "should raise Sequel::Error if no suitable class is found in the polymorphic hash" do
|
1687
|
+
@m2 = Class.new(@m)
|
1688
|
+
@dataset.set_model(:bit, 1 => @m2)
|
1689
|
+
proc {@dataset.all}.should raise_error(Sequel::Error)
|
1690
|
+
end
|
1691
|
+
|
1692
|
+
specify "should supply naked records if the naked option is specified" do
|
1693
|
+
@dataset.set_model(:bit, nil => @m)
|
1694
|
+
@dataset.each(:naked => true) {|r| r.class.should == Hash}
|
1695
|
+
end
|
1696
|
+
end
|
1697
|
+
|
1698
|
+
context "Dataset#destroy" do
|
1699
|
+
setup do
|
1700
|
+
db = Object.new
|
1701
|
+
m = Module.new do
|
1702
|
+
def transaction; yield; end
|
1703
|
+
end
|
1704
|
+
db.extend(m)
|
1705
|
+
|
1706
|
+
$DESTROYED = []
|
1707
|
+
|
1708
|
+
@m = Class.new do
|
1709
|
+
def initialize(c)
|
1710
|
+
@c = c
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
attr_accessor :c
|
1714
|
+
|
1715
|
+
def ==(o)
|
1716
|
+
@c == o.c
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
def destroy
|
1720
|
+
$DESTROYED << self
|
1721
|
+
end
|
1722
|
+
end
|
1723
|
+
$MODELS = [@m.new(12), @m.new(13)]
|
1724
|
+
|
1725
|
+
c = Class.new(Sequel::Dataset) do
|
1726
|
+
def fetch_rows(sql, &block)
|
1727
|
+
(12..13).each(&block)
|
1728
|
+
end
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
@d = c.new(db).from(:test)
|
1732
|
+
@d.set_model(@m)
|
1733
|
+
end
|
1734
|
+
|
1735
|
+
specify "should call destroy for every model instance in the dataset" do
|
1736
|
+
count = @d.destroy
|
1737
|
+
count.should == 2
|
1738
|
+
$DESTROYED.should == $MODELS
|
1739
|
+
end
|
1740
|
+
|
1741
|
+
specify "should raise error if no models are associated with the dataset" do
|
1742
|
+
proc {@d.naked.destroy}.should raise_error(Sequel::Error)
|
1743
|
+
end
|
1744
|
+
end
|
1745
|
+
|
1746
|
+
context "Dataset#<<" do
|
1747
|
+
setup do
|
1748
|
+
@d = Sequel::Dataset.new(nil)
|
1749
|
+
@d.meta_def(:insert) do
|
1750
|
+
1234567890
|
1751
|
+
end
|
1752
|
+
end
|
1753
|
+
|
1754
|
+
specify "should call #insert" do
|
1755
|
+
(@d << {:name => 1}).should == 1234567890
|
1756
|
+
end
|
1757
|
+
end
|
1758
|
+
|
1759
|
+
context "A paginated dataset" do
|
1760
|
+
setup do
|
1761
|
+
@d = Sequel::Dataset.new(nil)
|
1762
|
+
@d.meta_def(:count) {153}
|
1763
|
+
|
1764
|
+
@paginated = @d.paginate(1, 20)
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
specify "should set the limit and offset options correctly" do
|
1768
|
+
@paginated.opts[:limit].should == 20
|
1769
|
+
@paginated.opts[:offset].should == 0
|
1770
|
+
end
|
1771
|
+
|
1772
|
+
specify "should set the page count correctly" do
|
1773
|
+
@paginated.page_count.should == 8
|
1774
|
+
@d.paginate(1, 50).page_count.should == 4
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
specify "should set the current page number correctly" do
|
1778
|
+
@paginated.current_page.should == 1
|
1779
|
+
@d.paginate(3, 50).current_page.should == 3
|
1780
|
+
end
|
1781
|
+
|
1782
|
+
specify "should return the next page number or nil if we're on the last" do
|
1783
|
+
@paginated.next_page.should == 2
|
1784
|
+
@d.paginate(4, 50).next_page.should be_nil
|
1785
|
+
end
|
1786
|
+
|
1787
|
+
specify "should return the previous page number or nil if we're on the last" do
|
1788
|
+
@paginated.prev_page.should be_nil
|
1789
|
+
@d.paginate(4, 50).prev_page.should == 3
|
1790
|
+
end
|
1791
|
+
|
1792
|
+
specify "should return the page range" do
|
1793
|
+
@paginated.page_range.should == (1..8)
|
1794
|
+
@d.paginate(4, 50).page_range.should == (1..4)
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
specify "should return the record range for the current page" do
|
1798
|
+
@paginated.current_page_record_range.should == (1..20)
|
1799
|
+
@d.paginate(4, 50).current_page_record_range.should == (151..153)
|
1800
|
+
@d.paginate(5, 50).current_page_record_range.should == (0..0)
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
specify "should return the record count for the current page" do
|
1804
|
+
@paginated.current_page_record_count.should == 20
|
1805
|
+
@d.paginate(3, 50).current_page_record_count.should == 50
|
1806
|
+
@d.paginate(4, 50).current_page_record_count.should == 3
|
1807
|
+
@d.paginate(5, 50).current_page_record_count.should == 0
|
1808
|
+
end
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
context "Dataset#columns" do
|
1812
|
+
setup do
|
1813
|
+
@dataset = DummyDataset.new(nil).from(:items)
|
1814
|
+
@dataset.meta_def(:columns=) {|c| @columns = c}
|
1815
|
+
@dataset.meta_def(:first) {@columns = select_sql(nil)}
|
1816
|
+
end
|
1817
|
+
|
1818
|
+
specify "should return the value of @columns" do
|
1819
|
+
@dataset.columns = [:a, :b, :c]
|
1820
|
+
@dataset.columns.should == [:a, :b, :c]
|
1821
|
+
end
|
1822
|
+
|
1823
|
+
specify "should call first if @columns is nil" do
|
1824
|
+
@dataset.columns = nil
|
1825
|
+
@dataset.columns.should == 'SELECT * FROM items'
|
1826
|
+
@dataset.opts[:from] = [:nana]
|
1827
|
+
@dataset.columns.should == 'SELECT * FROM items'
|
1828
|
+
end
|
1829
|
+
end
|
1830
|
+
|
1831
|
+
require 'stringio'
|
1832
|
+
|
1833
|
+
context "Dataset#print" do
|
1834
|
+
setup do
|
1835
|
+
@output = StringIO.new
|
1836
|
+
@orig_stdout = $stdout
|
1837
|
+
$stdout = @output
|
1838
|
+
@dataset = DummyDataset.new(nil).from(:items)
|
1839
|
+
end
|
1840
|
+
|
1841
|
+
teardown do
|
1842
|
+
$stdout = @orig_stdout
|
1843
|
+
end
|
1844
|
+
|
1845
|
+
specify "should print out a table with the values" do
|
1846
|
+
@dataset.print(:a, :b)
|
1847
|
+
@output.rewind
|
1848
|
+
@output.read.should == \
|
1849
|
+
"+-+-+\n|a|b|\n+-+-+\n|1|2|\n|3|4|\n|5|6|\n+-+-+\n"
|
1850
|
+
end
|
1851
|
+
|
1852
|
+
specify "should default to the dataset's columns" do
|
1853
|
+
@dataset.meta_def(:columns) {[:a, :b]}
|
1854
|
+
@dataset.print
|
1855
|
+
@output.rewind
|
1856
|
+
@output.read.should == \
|
1857
|
+
"+-+-+\n|a|b|\n+-+-+\n|1|2|\n|3|4|\n|5|6|\n+-+-+\n"
|
1858
|
+
end
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
context "Dataset#multi_insert" do
|
1862
|
+
setup do
|
1863
|
+
@dbc = Class.new do
|
1864
|
+
attr_reader :sqls
|
1865
|
+
|
1866
|
+
def execute(sql)
|
1867
|
+
@sqls ||= []
|
1868
|
+
@sqls << sql
|
1869
|
+
end
|
1870
|
+
|
1871
|
+
def transaction
|
1872
|
+
@sqls ||= []
|
1873
|
+
@sqls << 'BEGIN'
|
1874
|
+
yield
|
1875
|
+
@sqls << 'COMMIT'
|
1876
|
+
end
|
1877
|
+
end
|
1878
|
+
@db = @dbc.new
|
1879
|
+
|
1880
|
+
@ds = Sequel::Dataset.new(@db).from(:items)
|
1881
|
+
|
1882
|
+
@list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
|
1883
|
+
end
|
1884
|
+
|
1885
|
+
specify "should join all inserts into a single SQL string" do
|
1886
|
+
@ds.multi_insert(@list)
|
1887
|
+
@db.sqls.should == [
|
1888
|
+
'BEGIN',
|
1889
|
+
"INSERT INTO items (name) VALUES ('abc')",
|
1890
|
+
"INSERT INTO items (name) VALUES ('def')",
|
1891
|
+
"INSERT INTO items (name) VALUES ('ghi')",
|
1892
|
+
'COMMIT'
|
1893
|
+
]
|
1894
|
+
end
|
1895
|
+
|
1896
|
+
specify "should accept the commit_every option for committing every x records" do
|
1897
|
+
@ds.multi_insert(@list, :commit_every => 2)
|
1898
|
+
@db.sqls.should == [
|
1899
|
+
'BEGIN',
|
1900
|
+
"INSERT INTO items (name) VALUES ('abc')",
|
1901
|
+
"INSERT INTO items (name) VALUES ('def')",
|
1902
|
+
'COMMIT',
|
1903
|
+
'BEGIN',
|
1904
|
+
"INSERT INTO items (name) VALUES ('ghi')",
|
1905
|
+
'COMMIT'
|
1906
|
+
]
|
1907
|
+
end
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
context "Dataset#query" do
|
1911
|
+
setup do
|
1912
|
+
@d = Sequel::Dataset.new(nil)
|
1913
|
+
end
|
1914
|
+
|
1915
|
+
specify "should support #from" do
|
1916
|
+
q = @d.query {from :xxx}
|
1917
|
+
q.class.should == @d.class
|
1918
|
+
q.sql.should == "SELECT * FROM xxx"
|
1919
|
+
end
|
1920
|
+
|
1921
|
+
specify "should support #select" do
|
1922
|
+
q = @d.query do
|
1923
|
+
select :a, :b___mongo
|
1924
|
+
from :yyy
|
1925
|
+
end
|
1926
|
+
q.class.should == @d.class
|
1927
|
+
q.sql.should == "SELECT a, b AS mongo FROM yyy"
|
1928
|
+
end
|
1929
|
+
|
1930
|
+
specify "should support #where" do
|
1931
|
+
q = @d.query do
|
1932
|
+
from :zzz
|
1933
|
+
where {:x + 2 > :y + 3}
|
1934
|
+
end
|
1935
|
+
q.class.should == @d.class
|
1936
|
+
q.sql.should == "SELECT * FROM zzz WHERE ((x + 2) > (y + 3))"
|
1937
|
+
|
1938
|
+
q = @d.from(:zzz).query do
|
1939
|
+
where {:x > 1 && :y > 2}
|
1940
|
+
end
|
1941
|
+
q.class.should == @d.class
|
1942
|
+
q.sql.should == "SELECT * FROM zzz WHERE ((x > 1) AND (y > 2))"
|
1943
|
+
|
1944
|
+
q = @d.from(:zzz).query do
|
1945
|
+
where :x => 33
|
1946
|
+
end
|
1947
|
+
q.class.should == @d.class
|
1948
|
+
q.sql.should == "SELECT * FROM zzz WHERE (x = 33)"
|
1949
|
+
end
|
1950
|
+
|
1951
|
+
specify "should support #group_by and #having" do
|
1952
|
+
q = @d.query do
|
1953
|
+
from :abc
|
1954
|
+
group_by :id
|
1955
|
+
having {:x >= 2}
|
1956
|
+
end
|
1957
|
+
q.class.should == @d.class
|
1958
|
+
q.sql.should == "SELECT * FROM abc GROUP BY id HAVING (x >= 2)"
|
1959
|
+
end
|
1960
|
+
|
1961
|
+
specify "should support #order, #order_by" do
|
1962
|
+
q = @d.query do
|
1963
|
+
from :xyz
|
1964
|
+
order_by :stamp
|
1965
|
+
end
|
1966
|
+
q.class.should == @d.class
|
1967
|
+
q.sql.should == "SELECT * FROM xyz ORDER BY stamp"
|
1968
|
+
end
|
1969
|
+
|
1970
|
+
specify "should raise on non-chainable method calls" do
|
1971
|
+
proc {@d.query {count}}.should raise_error(Sequel::Error)
|
1972
|
+
end
|
1973
|
+
|
1974
|
+
specify "should raise on each, insert, update, delete" do
|
1975
|
+
proc {@d.query {each}}.should raise_error(Sequel::Error)
|
1976
|
+
proc {@d.query {insert(:x => 1)}}.should raise_error(Sequel::Error)
|
1977
|
+
proc {@d.query {update(:x => 1)}}.should raise_error(Sequel::Error)
|
1978
|
+
proc {@d.query {delete}}.should raise_error(Sequel::Error)
|
1979
|
+
end
|
1980
|
+
end
|
1981
|
+
|
1982
|
+
context "Dataset" do
|
1983
|
+
setup do
|
1984
|
+
@d = Sequel::Dataset.new(nil).from(:x)
|
1985
|
+
end
|
1986
|
+
|
1987
|
+
specify "should support self-changing select!" do
|
1988
|
+
@d.select!(:y)
|
1989
|
+
@d.sql.should == "SELECT y FROM x"
|
1990
|
+
end
|
1991
|
+
|
1992
|
+
specify "should support self-changing from!" do
|
1993
|
+
@d.from!(:y)
|
1994
|
+
@d.sql.should == "SELECT * FROM y"
|
1995
|
+
end
|
1996
|
+
|
1997
|
+
specify "should support self-changing order!" do
|
1998
|
+
@d.order!(:y)
|
1999
|
+
@d.sql.should == "SELECT * FROM x ORDER BY y"
|
2000
|
+
end
|
2001
|
+
|
2002
|
+
specify "should support self-changing filter!" do
|
2003
|
+
@d.filter!(:y => 1)
|
2004
|
+
@d.sql.should == "SELECT * FROM x WHERE (y = 1)"
|
2005
|
+
end
|
2006
|
+
|
2007
|
+
specify "should support self-changing filter! with block" do
|
2008
|
+
@d.filter! {:y == 2}
|
2009
|
+
@d.sql.should == "SELECT * FROM x WHERE (y = 2)"
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
specify "should raise for ! methods that don't return a dataset" do
|
2013
|
+
proc {@d.opts!}.should raise_error(NameError)
|
2014
|
+
end
|
2015
|
+
|
2016
|
+
specify "should raise for missing methods" do
|
2017
|
+
proc {@d.xuyz}.should raise_error(NameError)
|
2018
|
+
proc {@d.xyz!}.should raise_error(NameError)
|
2019
|
+
proc {@d.xyz?}.should raise_error(NameError)
|
2020
|
+
end
|
2021
|
+
|
2022
|
+
specify "should support chaining of bang methods" do
|
2023
|
+
@d.order!(:y)
|
2024
|
+
@d.filter!(:y => 1)
|
2025
|
+
@d.sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
|
2026
|
+
end
|
2027
|
+
end
|
2028
|
+
|
2029
|
+
context "Dataset#transform" do
|
2030
|
+
setup do
|
2031
|
+
@c = Class.new(Sequel::Dataset) do
|
2032
|
+
attr_accessor :raw
|
2033
|
+
attr_accessor :sql
|
2034
|
+
|
2035
|
+
def fetch_rows(sql, &block)
|
2036
|
+
block[@raw]
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
def insert(v)
|
2040
|
+
@sql = insert_sql(v)
|
2041
|
+
end
|
2042
|
+
|
2043
|
+
def update(v)
|
2044
|
+
@sql = update_sql(v)
|
2045
|
+
end
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
@ds = @c.new(nil).from(:items)
|
2049
|
+
@ds.transform(:x => [
|
2050
|
+
proc {|v| Marshal.load(v)},
|
2051
|
+
proc {|v| Marshal.dump(v)}
|
2052
|
+
])
|
2053
|
+
end
|
2054
|
+
|
2055
|
+
specify "should change the dataset to transform values loaded from the database" do
|
2056
|
+
@ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2057
|
+
@ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2058
|
+
@ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2059
|
+
@ds.all.should == [{:x => [1, 2, 3], :y => 'hello'}]
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
specify "should change the dataset to transform values saved to the database" do
|
2063
|
+
@ds.insert(:x => :toast)
|
2064
|
+
@ds.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2065
|
+
|
2066
|
+
@ds.insert(:y => 'butter')
|
2067
|
+
@ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
|
2068
|
+
|
2069
|
+
@ds.update(:x => ['dream'])
|
2070
|
+
@ds.sql.should == "UPDATE items SET x = '#{Marshal.dump(['dream'])}'"
|
2071
|
+
end
|
2072
|
+
|
2073
|
+
specify "should be transferred to cloned datasets" do
|
2074
|
+
@ds2 = @ds.filter(:a => 1)
|
2075
|
+
|
2076
|
+
@ds2.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2077
|
+
@ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2078
|
+
|
2079
|
+
@ds2.insert(:x => :toast)
|
2080
|
+
@ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2081
|
+
end
|
2082
|
+
|
2083
|
+
specify "should work correctly together with set_row_proc" do
|
2084
|
+
@ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
|
2085
|
+
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2086
|
+
@ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
|
2087
|
+
|
2088
|
+
f = nil
|
2089
|
+
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2090
|
+
@ds.each(:naked => true) {|r| f = r}
|
2091
|
+
f.should == {:x => "wow", :y => 'hello'}
|
2092
|
+
end
|
2093
|
+
end
|
2094
|
+
|
2095
|
+
context "Dataset#transform" do
|
2096
|
+
setup do
|
2097
|
+
@c = Class.new(Sequel::Dataset) do
|
2098
|
+
attr_accessor :raw
|
2099
|
+
attr_accessor :sql
|
2100
|
+
|
2101
|
+
def fetch_rows(sql, &block)
|
2102
|
+
block[@raw]
|
2103
|
+
end
|
2104
|
+
|
2105
|
+
def insert(v)
|
2106
|
+
@sql = insert_sql(v)
|
2107
|
+
end
|
2108
|
+
|
2109
|
+
def update(v)
|
2110
|
+
@sql = update_sql(v)
|
2111
|
+
end
|
2112
|
+
end
|
2113
|
+
|
2114
|
+
@ds = @c.new(nil).from(:items)
|
2115
|
+
end
|
2116
|
+
|
2117
|
+
specify "should raise Sequel::Error for invalid transformations" do
|
2118
|
+
proc {@ds.transform(:x => 'mau')}.should raise_error(Sequel::Error::InvalidTransform)
|
2119
|
+
proc {@ds.transform(:x => :mau)}.should raise_error(Sequel::Error::InvalidTransform)
|
2120
|
+
proc {@ds.transform(:x => [])}.should raise_error(Sequel::Error::InvalidTransform)
|
2121
|
+
proc {@ds.transform(:x => ['mau'])}.should raise_error(Sequel::Error::InvalidTransform)
|
2122
|
+
proc {@ds.transform(:x => [proc {|v|}, proc {|v|}])}.should_not raise_error(Sequel::Error::InvalidTransform)
|
2123
|
+
end
|
2124
|
+
|
2125
|
+
specify "should support stock YAML transformation" do
|
2126
|
+
@ds.transform(:x => :yaml)
|
2127
|
+
|
2128
|
+
@ds.raw = {:x => [1, 2, 3].to_yaml, :y => 'hello'}
|
2129
|
+
@ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2130
|
+
|
2131
|
+
@ds.insert(:x => :toast)
|
2132
|
+
@ds.sql.should == "INSERT INTO items (x) VALUES ('#{:toast.to_yaml}')"
|
2133
|
+
@ds.insert(:y => 'butter')
|
2134
|
+
@ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
|
2135
|
+
@ds.update(:x => ['dream'])
|
2136
|
+
@ds.sql.should == "UPDATE items SET x = '#{['dream'].to_yaml}'"
|
2137
|
+
|
2138
|
+
@ds2 = @ds.filter(:a => 1)
|
2139
|
+
@ds2.raw = {:x => [1, 2, 3].to_yaml, :y => 'hello'}
|
2140
|
+
@ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2141
|
+
@ds2.insert(:x => :toast)
|
2142
|
+
@ds2.sql.should == "INSERT INTO items (x) VALUES ('#{:toast.to_yaml}')"
|
2143
|
+
|
2144
|
+
@ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
|
2145
|
+
@ds.raw = {:x => "wow".to_yaml, :y => 'hello'}
|
2146
|
+
@ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
|
2147
|
+
f = nil
|
2148
|
+
@ds.raw = {:x => "wow".to_yaml, :y => 'hello'}
|
2149
|
+
@ds.each(:naked => true) {|r| f = r}
|
2150
|
+
f.should == {:x => "wow", :y => 'hello'}
|
2151
|
+
end
|
2152
|
+
|
2153
|
+
specify "should support stock Marshal transformation" do
|
2154
|
+
@ds.transform(:x => :marshal)
|
2155
|
+
|
2156
|
+
@ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2157
|
+
@ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2158
|
+
|
2159
|
+
@ds.insert(:x => :toast)
|
2160
|
+
@ds.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2161
|
+
@ds.insert(:y => 'butter')
|
2162
|
+
@ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
|
2163
|
+
@ds.update(:x => ['dream'])
|
2164
|
+
@ds.sql.should == "UPDATE items SET x = '#{Marshal.dump(['dream'])}'"
|
2165
|
+
|
2166
|
+
@ds2 = @ds.filter(:a => 1)
|
2167
|
+
@ds2.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
|
2168
|
+
@ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
|
2169
|
+
@ds2.insert(:x => :toast)
|
2170
|
+
@ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
|
2171
|
+
|
2172
|
+
@ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
|
2173
|
+
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2174
|
+
@ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
|
2175
|
+
f = nil
|
2176
|
+
@ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
|
2177
|
+
@ds.each(:naked => true) {|r| f = r}
|
2178
|
+
f.should == {:x => "wow", :y => 'hello'}
|
2179
|
+
end
|
2180
|
+
|
2181
|
+
specify "should return self" do
|
2182
|
+
@ds.transform(:x => :marshal).should be(@ds)
|
2183
|
+
end
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
context "Dataset#to_csv" do
|
2187
|
+
setup do
|
2188
|
+
@c = Class.new(Sequel::Dataset) do
|
2189
|
+
attr_accessor :data
|
2190
|
+
attr_accessor :cols
|
2191
|
+
|
2192
|
+
def fetch_rows(sql, &block)
|
2193
|
+
@columns = @cols
|
2194
|
+
@data.each {|r| r.keys = @columns; block[r]}
|
2195
|
+
end
|
2196
|
+
|
2197
|
+
# naked should return self here because to_csv wants a naked result set.
|
2198
|
+
def naked
|
2199
|
+
self
|
2200
|
+
end
|
2201
|
+
end
|
2202
|
+
|
2203
|
+
@ds = @c.new(nil).from(:items)
|
2204
|
+
|
2205
|
+
@ds.cols = [:a, :b, :c]
|
2206
|
+
@ds.data = [
|
2207
|
+
[1, 2, 3], [4, 5, 6], [7, 8, 9]
|
2208
|
+
]
|
2209
|
+
end
|
2210
|
+
|
2211
|
+
specify "should format a CSV representation of the records" do
|
2212
|
+
@ds.to_csv.should ==
|
2213
|
+
"a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
|
2214
|
+
end
|
2215
|
+
|
2216
|
+
specify "should exclude column titles if so specified" do
|
2217
|
+
@ds.to_csv(false).should ==
|
2218
|
+
"1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
|
2219
|
+
end
|
2220
|
+
end
|
2221
|
+
|
2222
|
+
context "Dataset#each_hash" do
|
2223
|
+
setup do
|
2224
|
+
@c = Class.new(Sequel::Dataset) do
|
2225
|
+
def each(&block)
|
2226
|
+
a = [[1, 2, 3], [4, 5, 6]]
|
2227
|
+
a.each {|r| r.keys = [:a, :b, :c]; block[r]}
|
2228
|
+
end
|
2229
|
+
end
|
2230
|
+
|
2231
|
+
@ds = @c.new(nil).from(:items)
|
2232
|
+
end
|
2233
|
+
|
2234
|
+
specify "should yield records converted to hashes" do
|
2235
|
+
hashes = []
|
2236
|
+
@ds.each_hash {|h| hashes << h}
|
2237
|
+
hashes.should == [{:a => 1, :b => 2, :c => 3}, {:a => 4, :b => 5, :c => 6}]
|
2238
|
+
end
|
2239
|
+
end
|
2240
|
+
|
2241
|
+
context "Dataset magic methods" do
|
2242
|
+
setup do
|
2243
|
+
@c = Class.new(Sequel::Dataset) do
|
2244
|
+
@@sqls = []
|
2245
|
+
|
2246
|
+
def self.sqls; @@sqls; end
|
2247
|
+
|
2248
|
+
def fetch_rows(sql)
|
2249
|
+
@@sqls << sql
|
2250
|
+
yield({:a => 1, :b => 2})
|
2251
|
+
end
|
2252
|
+
end
|
2253
|
+
|
2254
|
+
@ds = @c.new(nil).from(:items)
|
2255
|
+
end
|
2256
|
+
|
2257
|
+
specify "should support order_by_xxx" do
|
2258
|
+
@ds.should_not respond_to(:order_by_name)
|
2259
|
+
proc {@ds.order_by_name}.should_not raise_error
|
2260
|
+
@ds.should respond_to(:order_by_name)
|
2261
|
+
@ds.order_by_name.should be_a_kind_of(@c)
|
2262
|
+
@ds.order_by_name.sql.should == "SELECT * FROM items ORDER BY name"
|
2263
|
+
end
|
2264
|
+
|
2265
|
+
specify "should support group_by_xxx" do
|
2266
|
+
@ds.should_not respond_to(:group_by_name)
|
2267
|
+
proc {@ds.group_by_name}.should_not raise_error
|
2268
|
+
@ds.should respond_to(:group_by_name)
|
2269
|
+
@ds.group_by_name.should be_a_kind_of(@c)
|
2270
|
+
@ds.group_by_name.sql.should == "SELECT * FROM items GROUP BY name"
|
2271
|
+
end
|
2272
|
+
|
2273
|
+
specify "should support count_by_xxx" do
|
2274
|
+
@ds.should_not respond_to(:count_by_name)
|
2275
|
+
proc {@ds.count_by_name}.should_not raise_error
|
2276
|
+
@ds.should respond_to(:count_by_name)
|
2277
|
+
@ds.count_by_name.should be_a_kind_of(@c)
|
2278
|
+
@ds.count_by_name.sql.should == "SELECT name, count(name) AS count FROM items GROUP BY name ORDER BY count"
|
2279
|
+
end
|
2280
|
+
|
2281
|
+
specify "should support filter_by_xxx" do
|
2282
|
+
@ds.should_not respond_to(:filter_by_name)
|
2283
|
+
proc {@ds.filter_by_name('sharon')}.should_not raise_error
|
2284
|
+
@ds.should respond_to(:filter_by_name)
|
2285
|
+
@ds.filter_by_name('sharon').should be_a_kind_of(@c)
|
2286
|
+
@ds.filter_by_name('sharon').sql.should == "SELECT * FROM items WHERE (name = 'sharon')"
|
2287
|
+
end
|
2288
|
+
|
2289
|
+
specify "should support all_by_xxx" do
|
2290
|
+
@ds.should_not respond_to(:all_by_name)
|
2291
|
+
proc {@ds.all_by_name('sharon')}.should_not raise_error
|
2292
|
+
@ds.should respond_to(:all_by_name)
|
2293
|
+
@ds.all_by_name('sharon').should == [{:a => 1, :b => 2}]
|
2294
|
+
@c.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon')"] * 2
|
2295
|
+
end
|
2296
|
+
|
2297
|
+
specify "should support find_by_xxx" do
|
2298
|
+
@ds.should_not respond_to(:find_by_name)
|
2299
|
+
proc {@ds.find_by_name('sharon')}.should_not raise_error
|
2300
|
+
@ds.should respond_to(:find_by_name)
|
2301
|
+
@ds.find_by_name('sharon').should == {:a => 1, :b => 2}
|
2302
|
+
@c.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"] * 2
|
2303
|
+
end
|
2304
|
+
|
2305
|
+
specify "should support first_by_xxx" do
|
2306
|
+
@ds.should_not respond_to(:first_by_name)
|
2307
|
+
proc {@ds.first_by_name('sharon')}.should_not raise_error
|
2308
|
+
@ds.should respond_to(:first_by_name)
|
2309
|
+
@ds.first_by_name('sharon').should == {:a => 1, :b => 2}
|
2310
|
+
@c.sqls.should == ["SELECT * FROM items ORDER BY name LIMIT 1"] * 2
|
2311
|
+
end
|
2312
|
+
|
2313
|
+
specify "should support last_by_xxx" do
|
2314
|
+
@ds.should_not respond_to(:last_by_name)
|
2315
|
+
proc {@ds.last_by_name('sharon')}.should_not raise_error
|
2316
|
+
@ds.should respond_to(:last_by_name)
|
2317
|
+
@ds.last_by_name('sharon').should == {:a => 1, :b => 2}
|
2318
|
+
@c.sqls.should == ["SELECT * FROM items ORDER BY name DESC LIMIT 1"] * 2
|
2319
|
+
end
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
context "Dataset#create_view" do
|
2323
|
+
setup do
|
2324
|
+
@dbc = Class.new(Sequel::Database) do
|
2325
|
+
attr_reader :sqls
|
2326
|
+
|
2327
|
+
def execute(sql)
|
2328
|
+
@sqls ||= []
|
2329
|
+
@sqls << sql
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
@db = @dbc.new
|
2333
|
+
|
2334
|
+
@ds = @db[:items].order(:abc).filter(:category => 'ruby')
|
2335
|
+
end
|
2336
|
+
|
2337
|
+
specify "should create a view with the dataset's sql" do
|
2338
|
+
@ds.create_view(:xyz)
|
2339
|
+
@db.sqls.should == ["CREATE VIEW xyz AS #{@ds.sql}"]
|
2340
|
+
end
|
2341
|
+
end
|
2342
|
+
|
2343
|
+
context "Dataset#create_or_replace_view" do
|
2344
|
+
setup do
|
2345
|
+
@dbc = Class.new(Sequel::Database) do
|
2346
|
+
attr_reader :sqls
|
2347
|
+
|
2348
|
+
def execute(sql)
|
2349
|
+
@sqls ||= []
|
2350
|
+
@sqls << sql
|
2351
|
+
end
|
2352
|
+
end
|
2353
|
+
@db = @dbc.new
|
2354
|
+
|
2355
|
+
@ds = @db[:items].order(:abc).filter(:category => 'ruby')
|
2356
|
+
end
|
2357
|
+
|
2358
|
+
specify "should create a view with the dataset's sql" do
|
2359
|
+
@ds.create_or_replace_view(:xyz)
|
2360
|
+
@db.sqls.should == ["CREATE OR REPLACE VIEW xyz AS #{@ds.sql}"]
|
2361
|
+
end
|
2362
|
+
end
|
2363
|
+
|
2364
|
+
context "Dataset#update_sql" do
|
2365
|
+
setup do
|
2366
|
+
@ds = Sequel::Dataset.new(nil).from(:items)
|
2367
|
+
end
|
2368
|
+
|
2369
|
+
specify "should accept strings" do
|
2370
|
+
@ds.update_sql("a = b").should == "UPDATE items SET a = b"
|
2371
|
+
end
|
2372
|
+
|
2373
|
+
specify "should accept hash with string keys" do
|
2374
|
+
@ds.update_sql('c' => 'd').should == "UPDATE items SET c = 'd'"
|
2375
|
+
end
|
2376
|
+
|
2377
|
+
specify "should accept array subscript references" do
|
2378
|
+
@ds.update_sql((:day|1) => 'd').should == "UPDATE items SET day[1] = 'd'"
|
2379
|
+
end
|
2380
|
+
|
2381
|
+
end
|