sequel 1.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ module Sequel::Plugins
4
+
5
+ module Timestamped
6
+ def self.apply(m, opts)
7
+ m.class_def(:get_stamp) {@values[:stamp]}
8
+ m.meta_def(:stamp_opts) {opts}
9
+ m.before_save {@values[:stamp] = Time.now}
10
+ end
11
+
12
+ module InstanceMethods
13
+ def abc; timestamped_opts; end
14
+ end
15
+
16
+ module ClassMethods
17
+ def deff; timestamped_opts; end
18
+ end
19
+
20
+ module DatasetMethods
21
+ def ghi; timestamped_opts; end
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ describe Sequel::Model, "using a plugin" do
28
+
29
+ it "should fail if the plugin is not found" do
30
+ proc do
31
+ c = Class.new(Sequel::Model) do
32
+ is :something_or_other
33
+ end
34
+ end.should raise_error(LoadError)
35
+ end
36
+
37
+ it "should apply the plugin to the class" do
38
+ c = nil
39
+ proc do
40
+ c = Class.new(Sequel::Model) do
41
+ set_dataset MODEL_DB[:items]
42
+ is :timestamped, :a => 1, :b => 2
43
+ end
44
+ end.should_not raise_error(LoadError)
45
+
46
+ c.should respond_to(:stamp_opts)
47
+ c.stamp_opts.should == {:a => 1, :b => 2}
48
+
49
+ # instance methods
50
+ m = c.new
51
+ m.should respond_to(:get_stamp)
52
+ m.should respond_to(:abc)
53
+ m.abc.should == {:a => 1, :b => 2}
54
+ t = Time.now
55
+ m[:stamp] = t
56
+ m.get_stamp.should == t
57
+
58
+ # class methods
59
+ c.should respond_to(:deff)
60
+ c.deff.should == {:a => 1, :b => 2}
61
+
62
+ # dataset methods
63
+ c.dataset.should respond_to(:ghi)
64
+ c.dataset.ghi.should == {:a => 1, :b => 2}
65
+ end
66
+
67
+ it "should fail to apply if the plugin has DatasetMethod and the model has no datset" do
68
+ proc do
69
+ Class.new(Sequel::Model) do
70
+ is :timestamped, :a => 1, :b => 2
71
+ end
72
+ end.should raise_error(Sequel::Error)
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+ --exclude
2
+ gems
3
+ --exclude
4
+ spec
@@ -0,0 +1,593 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Model#save" do
4
+
5
+ before(:each) do
6
+ MODEL_DB.reset
7
+
8
+ @c = Class.new(Sequel::Model(:items)) do
9
+ def columns
10
+ [:id, :x, :y]
11
+ end
12
+ end
13
+ end
14
+
15
+ it "should insert a record for a new model instance" do
16
+ o = @c.new(:x => 1)
17
+ o.save
18
+
19
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
20
+ end
21
+
22
+ it "should update a record for an existing model instance" do
23
+ o = @c.load(:id => 3, :x => 1)
24
+ o.save
25
+
26
+ MODEL_DB.sqls.first.should =~
27
+ /UPDATE items SET (id = 3, x = 1|x = 1, id = 3) WHERE \(id = 3\)/
28
+ end
29
+
30
+ it "should update only the given columns if given" do
31
+ o = @c.load(:id => 3, :x => 1, :y => nil)
32
+ o.save(:y)
33
+
34
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = NULL WHERE (id = 3)"
35
+ end
36
+
37
+ it "should mark saved columns as not changed" do
38
+ o = @c.new(:id => 3, :x => 1, :y => nil)
39
+ o[:y] = 4
40
+ o.changed_columns.should == [:y]
41
+ o.save(:x)
42
+ o.changed_columns.should == [:y]
43
+ o.save(:y)
44
+ o.changed_columns.should == []
45
+ end
46
+
47
+ end
48
+
49
+ describe "Model#save_changes" do
50
+
51
+ before(:each) do
52
+ MODEL_DB.reset
53
+
54
+ @c = Class.new(Sequel::Model(:items)) do
55
+ def columns
56
+ [:id, :x, :y]
57
+ end
58
+ end
59
+ end
60
+
61
+ it "should do nothing if no changed columns" do
62
+ o = @c.new(:id => 3, :x => 1, :y => nil)
63
+ o.save_changes
64
+
65
+ MODEL_DB.sqls.should be_empty
66
+
67
+ o = @c.load(:id => 3, :x => 1, :y => nil)
68
+ o.save_changes
69
+
70
+ MODEL_DB.sqls.should be_empty
71
+ end
72
+
73
+ it "should update only changed columns" do
74
+ o = @c.load(:id => 3, :x => 1, :y => nil)
75
+ o.x = 2
76
+
77
+ o.save_changes
78
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
79
+ o.save_changes
80
+ o.save_changes
81
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
82
+ MODEL_DB.reset
83
+
84
+ o.y = 4
85
+ o.save_changes
86
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
87
+ o.save_changes
88
+ o.save_changes
89
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
90
+ end
91
+
92
+ it "should not consider columns changed if the values did not change" do
93
+ o = @c.load(:id => 3, :x => 1, :y => nil)
94
+ o.x = 1
95
+
96
+ o.save_changes
97
+ MODEL_DB.sqls.should == []
98
+ o.x = 3
99
+ o.save_changes
100
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 3 WHERE (id = 3)"]
101
+ MODEL_DB.reset
102
+
103
+ o[:y] = nil
104
+ o.save_changes
105
+ MODEL_DB.sqls.should == []
106
+ o[:y] = 4
107
+ o.save_changes
108
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
109
+ end
110
+ end
111
+
112
+ describe "Model#set" do
113
+
114
+ before(:each) do
115
+ MODEL_DB.reset
116
+
117
+ @c = Class.new(Sequel::Model(:items)) do
118
+ def columns
119
+ [:id, :x, :y]
120
+ end
121
+ end
122
+ end
123
+
124
+ it "should generate an update statement" do
125
+ o = @c.new(:id => 1)
126
+ o.set(:x => 1)
127
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
128
+ end
129
+
130
+ it "should update attribute values" do
131
+ o = @c.new(:id => 1)
132
+ o.x.should be_nil
133
+ o.set(:x => 1)
134
+ o.x.should == 1
135
+ end
136
+
137
+ it "should support string keys" do
138
+ o = @c.new(:id => 1)
139
+ o.x.should be_nil
140
+ o.set('x' => 1)
141
+ o.x.should == 1
142
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
143
+ end
144
+
145
+ it "should be aliased by #update" do
146
+ o = @c.new(:id => 1)
147
+ o.update(:x => 1)
148
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
149
+ end
150
+ end
151
+
152
+
153
+ describe "Model#new?" do
154
+
155
+ before(:each) do
156
+ MODEL_DB.reset
157
+
158
+ @c = Class.new(Sequel::Model(:items)) do
159
+ end
160
+ end
161
+
162
+ it "should be true for a new instance" do
163
+ n = @c.new(:x => 1)
164
+ n.should be_new
165
+ end
166
+
167
+ it "should be false after saving" do
168
+ n = @c.new(:x => 1)
169
+ n.save
170
+ n.should_not be_new
171
+ end
172
+
173
+ it "should alias new_record? to new?" do
174
+ n = @c.new(:x => 1)
175
+ n.should respond_to(:new_record?)
176
+ n.should be_new_record
177
+ n.save
178
+ n.should_not be_new_record
179
+ end
180
+
181
+ end
182
+
183
+ describe Sequel::Model, "w/ primary key" do
184
+
185
+ it "should default to ':id'" do
186
+ model_a = Class.new Sequel::Model
187
+ model_a.primary_key.should be_equal(:id)
188
+ end
189
+
190
+ it "should be changed through 'set_primary_key'" do
191
+ model_a = Class.new(Sequel::Model) { set_primary_key :a }
192
+ model_a.primary_key.should be_equal(:a)
193
+ end
194
+
195
+ it "should support multi argument composite keys" do
196
+ model_a = Class.new(Sequel::Model) { set_primary_key :a, :b }
197
+ model_a.primary_key.should be_eql([:a, :b])
198
+ end
199
+
200
+ it "should accept single argument composite keys" do
201
+ model_a = Class.new(Sequel::Model) { set_primary_key [:a, :b] }
202
+ model_a.primary_key.should be_eql([:a, :b])
203
+ end
204
+
205
+ end
206
+
207
+ describe Sequel::Model, "w/o primary key" do
208
+ it "should return nil for primary key" do
209
+ Class.new(Sequel::Model) { no_primary_key }.primary_key.should be_nil
210
+ end
211
+
212
+ it "should raise a Sequel::Error on 'this'" do
213
+ instance = Class.new(Sequel::Model) { no_primary_key }.new
214
+ proc { instance.this }.should raise_error(Sequel::Error)
215
+ end
216
+ end
217
+
218
+ describe Sequel::Model, "with this" do
219
+
220
+ before { @example = Class.new Sequel::Model(:examples) }
221
+
222
+ it "should return a dataset identifying the record" do
223
+ instance = @example.new :id => 3
224
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (id = 3) LIMIT 1")
225
+ end
226
+
227
+ it "should support arbitary primary keys" do
228
+ @example.set_primary_key :a
229
+
230
+ instance = @example.new :a => 3
231
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (a = 3) LIMIT 1")
232
+ end
233
+
234
+ it "should support composite primary keys" do
235
+ @example.set_primary_key :x, :y
236
+ instance = @example.new :x => 4, :y => 5
237
+
238
+ parts = [
239
+ 'SELECT * FROM examples WHERE %s LIMIT 1',
240
+ '(x = 4) AND (y = 5)',
241
+ '(y = 5) AND (x = 4)'
242
+ ].map { |expr| Regexp.escape expr }
243
+ regexp = Regexp.new parts.first % "(?:#{parts[1]}|#{parts[2]})"
244
+
245
+ instance.this.sql.should match(regexp)
246
+ end
247
+
248
+ end
249
+
250
+ describe "Model#pk" do
251
+ before(:each) do
252
+ @m = Class.new(Sequel::Model)
253
+ end
254
+
255
+ it "should be default return the value of the :id column" do
256
+ m = @m.new(:id => 111, :x => 2, :y => 3)
257
+ m.pk.should == 111
258
+ end
259
+
260
+ it "should be return the primary key value for custom primary key" do
261
+ @m.set_primary_key :x
262
+ m = @m.new(:id => 111, :x => 2, :y => 3)
263
+ m.pk.should == 2
264
+ end
265
+
266
+ it "should be return the primary key value for composite primary key" do
267
+ @m.set_primary_key [:y, :x]
268
+ m = @m.new(:id => 111, :x => 2, :y => 3)
269
+ m.pk.should == [3, 2]
270
+ end
271
+
272
+ it "should raise if no primary key" do
273
+ @m.set_primary_key nil
274
+ m = @m.new(:id => 111, :x => 2, :y => 3)
275
+ proc {m.pk}.should raise_error(Sequel::Error)
276
+
277
+ @m.no_primary_key
278
+ m = @m.new(:id => 111, :x => 2, :y => 3)
279
+ proc {m.pk}.should raise_error(Sequel::Error)
280
+ end
281
+ end
282
+
283
+ describe "Model#pk_hash" do
284
+ before(:each) do
285
+ @m = Class.new(Sequel::Model)
286
+ end
287
+
288
+ it "should be default return the value of the :id column" do
289
+ m = @m.new(:id => 111, :x => 2, :y => 3)
290
+ m.pk_hash.should == {:id => 111}
291
+ end
292
+
293
+ it "should be return the primary key value for custom primary key" do
294
+ @m.set_primary_key :x
295
+ m = @m.new(:id => 111, :x => 2, :y => 3)
296
+ m.pk_hash.should == {:x => 2}
297
+ end
298
+
299
+ it "should be return the primary key value for composite primary key" do
300
+ @m.set_primary_key [:y, :x]
301
+ m = @m.new(:id => 111, :x => 2, :y => 3)
302
+ m.pk_hash.should == {:y => 3, :x => 2}
303
+ end
304
+
305
+ it "should raise if no primary key" do
306
+ @m.set_primary_key nil
307
+ m = @m.new(:id => 111, :x => 2, :y => 3)
308
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
309
+
310
+ @m.no_primary_key
311
+ m = @m.new(:id => 111, :x => 2, :y => 3)
312
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
313
+ end
314
+ end
315
+
316
+ describe Sequel::Model, "update_with_params" do
317
+
318
+ before(:each) do
319
+ MODEL_DB.reset
320
+
321
+ @c = Class.new(Sequel::Model(:items)) do
322
+ def self.columns; [:x, :y]; end
323
+ end
324
+ @o1 = @c.new
325
+ @o2 = @c.load(:id => 5)
326
+ end
327
+
328
+ it "should filter the given params using the model columns" do
329
+ @o1.update_with_params(:x => 1, :z => 2)
330
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
331
+
332
+ MODEL_DB.reset
333
+ @o2.update_with_params(:y => 1, :abc => 2)
334
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
335
+ end
336
+
337
+ it "should be aliased by create_with" do
338
+ @o1.update_with(:x => 1, :z => 2)
339
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
340
+
341
+ MODEL_DB.reset
342
+ @o2.update_with(:y => 1, :abc => 2)
343
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
344
+ end
345
+
346
+ it "should support virtual attributes" do
347
+ @c.class_def(:blah=) {|v| self.x = v}
348
+ @o1.update_with(:blah => 333)
349
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (333)"
350
+ end
351
+ end
352
+
353
+ describe Sequel::Model, "create_with_params" do
354
+
355
+ before(:each) do
356
+ MODEL_DB.reset
357
+
358
+ @c = Class.new(Sequel::Model(:items)) do
359
+ def self.columns; [:x, :y]; end
360
+ end
361
+ end
362
+
363
+ it "should filter the given params using the model columns" do
364
+ @c.create_with_params(:x => 1, :z => 2)
365
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
366
+
367
+ MODEL_DB.reset
368
+ @c.create_with_params(:y => 1, :abc => 2)
369
+ MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
370
+ end
371
+
372
+ it "should be aliased by create_with" do
373
+ @c.create_with(:x => 1, :z => 2)
374
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
375
+
376
+ MODEL_DB.reset
377
+ @c.create_with(:y => 1, :abc => 2)
378
+ MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
379
+ end
380
+
381
+ it "should support virtual attributes" do
382
+ @c.class_def(:blah=) {|v| self.x = v}
383
+ o = @c.create_with(:blah => 333)
384
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (333)"
385
+ end
386
+ end
387
+
388
+ describe Sequel::Model, "#destroy" do
389
+
390
+ before(:each) do
391
+ MODEL_DB.reset
392
+ @model = Class.new(Sequel::Model(:items))
393
+ @model.dataset.meta_def(:delete) {MODEL_DB.execute delete_sql}
394
+
395
+ @instance = @model.new(:id => 1234)
396
+ #@model.stub!(:delete).and_return(:true)
397
+ end
398
+
399
+ it "should run within a transaction" do
400
+ @model.db.should_receive(:transaction)
401
+ @instance.destroy
402
+ end
403
+
404
+ it "should run before_destroy and after_destroy hooks" do
405
+ @model.before_destroy {MODEL_DB.execute('before blah')}
406
+ @model.after_destroy {MODEL_DB.execute('after blah')}
407
+ @instance.destroy
408
+
409
+ MODEL_DB.sqls.should == [
410
+ "before blah",
411
+ "DELETE FROM items WHERE (id = 1234)",
412
+ "after blah"
413
+ ]
414
+ end
415
+ end
416
+
417
+ describe Sequel::Model, "#exists?" do
418
+ before(:each) do
419
+ @model = Class.new(Sequel::Model(:items))
420
+ @m = @model.new
421
+ end
422
+
423
+ it "should returns true when #this.count > 0" do
424
+ @m.this.meta_def(:count) {1}
425
+ @m.exists?.should be_true
426
+ end
427
+
428
+ it "should return false when #this.count == 0" do
429
+ @m.this.meta_def(:count) {0}
430
+ @m.exists?.should be_false
431
+ end
432
+ end
433
+
434
+ describe Sequel::Model, "#each" do
435
+ setup do
436
+ @model = Class.new(Sequel::Model(:items))
437
+ @m = @model.new(:a => 1, :b => 2, :id => 4444)
438
+ end
439
+
440
+ specify "should iterate over the values" do
441
+ h = {}
442
+ @m.each {|k, v| h[k] = v}
443
+ h.should == {:a => 1, :b => 2, :id => 4444}
444
+ end
445
+ end
446
+
447
+ describe Sequel::Model, "#keys" do
448
+ setup do
449
+ @model = Class.new(Sequel::Model(:items))
450
+ @m = @model.new(:a => 1, :b => 2, :id => 4444)
451
+ end
452
+
453
+ specify "should return the value keys" do
454
+ @m.keys.size.should == 3
455
+ @m.keys.should include(:a, :b, :id)
456
+
457
+ @m = @model.new()
458
+ @m.keys.should == []
459
+ end
460
+ end
461
+
462
+ describe Sequel::Model, "#===" do
463
+ specify "should compare instances by values" do
464
+ a = Sequel::Model.new(:id => 1, :x => 3)
465
+ b = Sequel::Model.new(:id => 1, :x => 4)
466
+ c = Sequel::Model.new(:id => 1, :x => 3)
467
+
468
+ a.should_not == b
469
+ a.should == c
470
+ b.should_not == c
471
+ end
472
+ end
473
+
474
+ describe Sequel::Model, "#===" do
475
+ specify "should compare instances by pk only" do
476
+ a = Sequel::Model.new(:id => 1, :x => 3)
477
+ b = Sequel::Model.new(:id => 1, :x => 4)
478
+ c = Sequel::Model.new(:id => 2, :x => 3)
479
+
480
+ a.should === b
481
+ a.should_not === c
482
+ end
483
+ end
484
+
485
+ describe Sequel::Model, "#initialize" do
486
+ setup do
487
+ @c = Class.new(Sequel::Model) do
488
+ end
489
+ end
490
+
491
+ specify "should accept values" do
492
+ m = @c.new(:id => 1, :x => 2)
493
+ m.values.should == {:id => 1, :x => 2}
494
+ end
495
+
496
+ specify "should accept no values" do
497
+ m = @c.new
498
+ m.values.should == {}
499
+ end
500
+
501
+ specify "should accept nil values" do
502
+ m = @c.new(nil)
503
+ m.values.should == {}
504
+ end
505
+
506
+ specify "should accept a block to execute" do
507
+ m = @c.new {|o| o[:id] = 1234}
508
+ m.id.should == 1234
509
+ end
510
+
511
+ specify "should accept virtual attributes" do
512
+ @c.class_def(:blah=) {|x| @blah = x}
513
+ @c.class_def(:blah) {@blah}
514
+
515
+ m = @c.new(:id => 1, :x => 2, :blah => 3)
516
+ m.values.should == {:id => 1, :x => 2}
517
+ m.blah.should == 3
518
+ end
519
+
520
+ specify "should convert string keys into symbol keys" do
521
+ m = @c.new('id' => 1, 'x' => 2)
522
+ m.values.should == {:id => 1, :x => 2}
523
+ end
524
+ end
525
+
526
+ describe Sequel::Model, ".create" do
527
+
528
+ before(:each) do
529
+ MODEL_DB.reset
530
+ @c = Class.new(Sequel::Model(:items)) do
531
+ def columns; [:x]; end
532
+ end
533
+ end
534
+
535
+ it "should be able to create rows in the associated table" do
536
+ o = @c.create(:x => 1)
537
+ o.class.should == @c
538
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (1)')) LIMIT 1"]
539
+ end
540
+
541
+ it "should be able to create rows without any values specified" do
542
+ o = @c.create
543
+ o.class.should == @c
544
+ MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id IN ('INSERT INTO items DEFAULT VALUES')) LIMIT 1"]
545
+ end
546
+
547
+ it "should accept a block and run it" do
548
+ o1, o2, o3 = nil, nil, nil
549
+ o = @c.create {|o3| o1 = o3; o2 = :blah; o3.x = 333}
550
+ o.class.should == @c
551
+ o1.should === o
552
+ o3.should === o
553
+ o2.should == :blah
554
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (333)')) LIMIT 1"]
555
+ end
556
+
557
+ it "should create a row for a model with custom primary key" do
558
+ @c.set_primary_key :x
559
+ o = @c.create(:x => 30)
560
+ o.class.should == @c
561
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (30)", "SELECT * FROM items WHERE (x = 30) LIMIT 1"]
562
+ end
563
+ end
564
+
565
+ describe Sequel::Model, "#refresh" do
566
+ setup do
567
+ MODEL_DB.reset
568
+ @c = Class.new(Sequel::Model(:items)) do
569
+ def columns; [:x]; end
570
+ end
571
+ end
572
+
573
+ specify "should reload the instance values from the database" do
574
+ @m = @c.new(:id => 555)
575
+ @m[:x] = 'blah'
576
+ @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
577
+ @m.refresh
578
+ @m[:x].should == 'kaboom'
579
+ end
580
+
581
+ specify "should raise if the instance is not found" do
582
+ @m = @c.new(:id => 555)
583
+ @m.this.should_receive(:first).and_return(nil)
584
+ proc {@m.refresh}.should raise_error(Sequel::Error)
585
+ end
586
+
587
+ specify "should be aliased by #reload" do
588
+ @m = @c.new(:id => 555)
589
+ @m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
590
+ @m.reload
591
+ @m[:x].should == 'kaboom'
592
+ end
593
+ end