sequel 1.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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