sequel 2.1.0 → 2.2.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.
@@ -76,10 +76,18 @@ describe Sequel::Model::Associations::AssociationReflection, "#select" do
76
76
  @c.association_reflection(:c).should include(:select)
77
77
  @c.association_reflection(:c).select.should == [:par_parents__id]
78
78
  end
79
- it "should use the associated_table.* if :select is not present" do
79
+ it "should be the associated_table.* if :select is not present for a many_to_many associaiton" do
80
+ @c.many_to_many :cs, :class=>'ParParent'
81
+ @c.association_reflection(:cs).should_not include(:select)
82
+ @c.association_reflection(:cs).select.should == :par_parents.*
83
+ end
84
+ it "should be if :select is not present for a many_to_one and one_to_many associaiton" do
85
+ @c.one_to_many :cs, :class=>'ParParent'
86
+ @c.association_reflection(:cs).should_not include(:select)
87
+ @c.association_reflection(:cs).select.should == nil
80
88
  @c.many_to_one :c, :class=>'ParParent'
81
89
  @c.association_reflection(:c).should_not include(:select)
82
- @c.association_reflection(:c).select.should == :par_parents.*
90
+ @c.association_reflection(:c).select.should == nil
83
91
  end
84
92
  end
85
93
 
@@ -15,20 +15,40 @@ describe Sequel::Model, "associate" do
15
15
  klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
16
16
  klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
17
17
  end
18
+ it "should allow extending the dataset with :extend option" do
19
+ MODEL_DB.reset
20
+ klass = Class.new(Sequel::Model(:nodes)) do
21
+ columns :id, :a_id
22
+ end
23
+ mod = Module.new do
24
+ def blah
25
+ 1
26
+ end
27
+ end
28
+ mod2 = Module.new do
29
+ def blar
30
+ 2
31
+ end
32
+ end
33
+
34
+ klass.associate :many_to_one, :a, :class=>klass, :extend=>mod
35
+ klass.associate :one_to_many, :bs, :class=>klass, :extend=>[mod]
36
+ klass.associate :many_to_many, :cs, :class=>klass, :extend=>[mod, mod2]
37
+
38
+ node = klass.load(:id=>1)
39
+ node.a_dataset.blah.should == 1
40
+ node.bs_dataset.blah.should == 1
41
+ node.cs_dataset.blah.should == 1
42
+ node.cs_dataset.blar.should == 2
43
+ end
18
44
  end
19
45
 
20
46
  describe Sequel::Model, "many_to_one" do
21
-
22
- before(:each) do
47
+ before do
23
48
  MODEL_DB.reset
24
49
 
25
50
  @c2 = Class.new(Sequel::Model(:nodes)) do
26
51
  columns :id, :parent_id, :par_parent_id, :blah
27
- def self.create_new(values)
28
- obj = self.new(values)
29
- obj.instance_variable_set(:@new, false)
30
- obj
31
- end
32
52
  end
33
53
 
34
54
  @dataset = @c2.dataset
@@ -42,7 +62,7 @@ describe Sequel::Model, "many_to_one" do
42
62
  p.class.should == @c2
43
63
  p.values.should == {:x => 1, :id => 1}
44
64
 
45
- MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
65
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
46
66
  end
47
67
 
48
68
  it "should use implicit class if omitted" do
@@ -55,7 +75,7 @@ describe Sequel::Model, "many_to_one" do
55
75
  p = d.par_parent
56
76
  p.class.should == ParParent
57
77
 
58
- MODEL_DB.sqls.should == ["SELECT par_parents.* FROM par_parents WHERE (id = 234) LIMIT 1"]
78
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
59
79
  end
60
80
 
61
81
  it "should use class inside module if given as a string" do
@@ -70,7 +90,7 @@ describe Sequel::Model, "many_to_one" do
70
90
  p = d.par_parent
71
91
  p.class.should == Par::Parent
72
92
 
73
- MODEL_DB.sqls.should == ["SELECT parents.* FROM parents WHERE (id = 234) LIMIT 1"]
93
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
74
94
  end
75
95
 
76
96
  it "should use explicit key if given" do
@@ -81,13 +101,22 @@ describe Sequel::Model, "many_to_one" do
81
101
  p.class.should == @c2
82
102
  p.values.should == {:x => 1, :id => 1}
83
103
 
84
- MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 567) LIMIT 1"]
104
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
85
105
  end
86
106
 
87
107
  it "should use :select option if given" do
88
108
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :select=>[:id, :name]
89
109
  @c2.new(:id => 1, :blah => 567).parent
90
- MODEL_DB.sqls.should == ["SELECT id, name FROM nodes WHERE (id = 567) LIMIT 1"]
110
+ MODEL_DB.sqls.should == ["SELECT id, name FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
111
+ end
112
+
113
+ it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
114
+ c2 = @c2
115
+ @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
116
+ ds.filter(:x > 1)
117
+ end
118
+ @c2.load(:id => 100).child_20
119
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
91
120
  end
92
121
 
93
122
  it "should return nil if key value is nil" do
@@ -107,9 +136,9 @@ describe Sequel::Model, "many_to_one" do
107
136
  d = @c2.new(:id => 1, :parent_id=>555)
108
137
  MODEL_DB.sqls.should == []
109
138
  d.parent.should == nil
110
- MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (id = 555) LIMIT 1']
139
+ MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
111
140
  d.parent.should == nil
112
- MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (id = 555) LIMIT 1']
141
+ MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
113
142
  end
114
143
 
115
144
  it "should define a setter method" do
@@ -130,7 +159,7 @@ describe Sequel::Model, "many_to_one" do
130
159
  it "should not persist changes until saved" do
131
160
  @c2.many_to_one :parent, :class => @c2
132
161
 
133
- d = @c2.create_new(:id => 1)
162
+ d = @c2.load(:id => 1)
134
163
  MODEL_DB.reset
135
164
  d.parent = @c2.new(:id => 345)
136
165
  MODEL_DB.sqls.should == []
@@ -141,14 +170,14 @@ describe Sequel::Model, "many_to_one" do
141
170
  it "should set cached instance variable when accessed" do
142
171
  @c2.many_to_one :parent, :class => @c2
143
172
 
144
- d = @c2.create_new(:id => 1)
173
+ d = @c2.load(:id => 1)
145
174
  MODEL_DB.reset
146
175
  d.parent_id = 234
147
176
  d.associations[:parent].should == nil
148
177
  ds = @c2.dataset
149
178
  def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
150
179
  e = d.parent
151
- MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
180
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
152
181
  d.associations[:parent].should == e
153
182
  end
154
183
 
@@ -182,7 +211,7 @@ describe Sequel::Model, "many_to_one" do
182
211
  d.parent_id = 234
183
212
  d.associations[:parent] = 42
184
213
  d.parent(true).should_not == 42
185
- MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
214
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
186
215
  end
187
216
 
188
217
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
@@ -198,16 +227,16 @@ describe Sequel::Model, "many_to_one" do
198
227
  MODEL_DB.sqls.should == []
199
228
  d.parent = e
200
229
  e.children.should_not(include(d))
201
- MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
230
+ MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
202
231
 
203
232
  MODEL_DB.reset
204
233
  d = @c2.new(:id => 1)
205
234
  e = @c2.new(:id => 2)
206
235
  e.children.should_not(include(d))
207
- MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
236
+ MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
208
237
  d.parent = e
209
238
  e.children.should(include(d))
210
- MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
239
+ MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
211
240
  end
212
241
 
213
242
  it "should have the setter remove the object from the previous associated object's reciprocal one_to_many cached association list if it exists" do
@@ -250,16 +279,117 @@ describe Sequel::Model, "many_to_one" do
250
279
  it "should have belongs_to alias" do
251
280
  @c2.belongs_to :parent, :class => @c2
252
281
 
253
- d = @c2.create_new(:id => 1)
282
+ d = @c2.load(:id => 1)
254
283
  MODEL_DB.reset
255
284
  d.parent_id = 234
256
285
  d.associations[:parent].should == nil
257
286
  ds = @c2.dataset
258
287
  def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
259
288
  e = d.parent
260
- MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
289
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
261
290
  d.associations[:parent].should == e
262
291
  end
292
+
293
+ it "should make the change to the foreign_key value inside a _association= method" do
294
+ @c2.many_to_one :parent, :class => @c2
295
+ @c2.private_instance_methods.sort.should(include("_parent="))
296
+ p = @c2.new
297
+ c = @c2.load(:id=>123)
298
+ def p._parent=(x)
299
+ @x = x
300
+ end
301
+ p.should_not_receive(:parent_id=)
302
+ p.parent = c
303
+ p.instance_variable_get(:@x).should == c
304
+ end
305
+
306
+ it "should support (before|after)_(add|remove) callbacks" do
307
+ h = []
308
+ @c2.many_to_one :parent, :class => @c2, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
309
+ @c2.class_eval do
310
+ @@blah = h
311
+ def []=(a, v)
312
+ a == :parent_id ? (@@blah << (v ? 4 : 5)) : super
313
+ end
314
+ def blah(x)
315
+ @@blah << x.pk
316
+ end
317
+ def blahr(x)
318
+ @@blah << 6
319
+ end
320
+ end
321
+ p = @c2.load(:id=>10)
322
+ c = @c2.load(:id=>123)
323
+ h.should == []
324
+ p.parent = c
325
+ h.should == [10, -123, 123, 4, 3]
326
+ p.parent = nil
327
+ h.should == [10, -123, 123, 4, 3, 123, 5, 6]
328
+ end
329
+
330
+ it "should support after_load association callback" do
331
+ h = []
332
+ @c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
333
+ @c2.class_eval do
334
+ @@blah = h
335
+ def al(v)
336
+ @@blah << v.pk
337
+ end
338
+ def @dataset.fetch_rows(sql)
339
+ yield({:id=>20})
340
+ end
341
+ end
342
+ p = @c2.load(:id=>10, :parent_id=>20)
343
+ parent = p.parent
344
+ h.should == [[10, 20], 20]
345
+ parent.pk.should == 20
346
+ end
347
+
348
+ it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
349
+ # The reason for this is that assignment in ruby always returns the argument instead of the result
350
+ # of the method, so we can't return nil to signal that the association callback prevented the modification
351
+ p = @c2.new
352
+ c = @c2.load(:id=>123)
353
+ p.raise_on_save_failure = false
354
+ @c2.many_to_one :parent, :class => @c2, :before_add=>:ba, :before_remove=>:br
355
+ p.should_receive(:ba).once.with(c).and_return(false)
356
+ p.should_not_receive(:_parent=)
357
+ proc{p.parent = c}.should raise_error(Sequel::Error)
358
+ p.parent.should == nil
359
+ p.associations[:parent] = c
360
+ p.parent.should == c
361
+ p.should_receive(:br).once.with(c).and_return(false)
362
+ proc{p.parent = nil}.should raise_error(Sequel::Error)
363
+ end
364
+
365
+ it "should call the remove callbacks for the previous object and the add callbacks for the new object" do
366
+ c = @c2.load(:id=>123)
367
+ d = @c2.load(:id=>321)
368
+ p = @c2.new
369
+ p.associations[:parent] = d
370
+ h = []
371
+ @c2.many_to_one :parent, :class => @c2, :before_add=>:ba, :before_remove=>:br, :after_add=>:aa, :after_remove=>:ar
372
+ @c2.class_eval do
373
+ @@blah = h
374
+ def []=(a, v)
375
+ a == :parent_id ? (@@blah << 5) : super
376
+ end
377
+ def ba(x)
378
+ @@blah << x.pk
379
+ end
380
+ def br(x)
381
+ @@blah << x.pk * -1
382
+ end
383
+ def aa(x)
384
+ @@blah << x.pk * 2
385
+ end
386
+ def ar(x)
387
+ @@blah << x.pk * -2
388
+ end
389
+ end
390
+ p.parent = c
391
+ h.should == [-321, 123, 5, 246, -642]
392
+ end
263
393
  end
264
394
 
265
395
  describe Sequel::Model, "one_to_many" do
@@ -301,7 +431,7 @@ describe Sequel::Model, "one_to_many" do
301
431
  n = @c2.new(:id => 1234)
302
432
  a = n.attributes_dataset
303
433
  a.should be_a_kind_of(Sequel::Dataset)
304
- a.sql.should == 'SELECT attributes.* FROM attributes WHERE (node_id = 1234)'
434
+ a.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
305
435
  end
306
436
 
307
437
  it "should use implicit class if omitted" do
@@ -313,7 +443,7 @@ describe Sequel::Model, "one_to_many" do
313
443
  n = @c2.new(:id => 1234)
314
444
  v = n.historical_values_dataset
315
445
  v.should be_a_kind_of(Sequel::Dataset)
316
- v.sql.should == 'SELECT historical_values.* FROM historical_values WHERE (node_id = 1234)'
446
+ v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
317
447
  v.model_classes.should == {nil => HistoricalValue}
318
448
  end
319
449
 
@@ -328,7 +458,7 @@ describe Sequel::Model, "one_to_many" do
328
458
  n = @c2.new(:id => 1234)
329
459
  v = n.historical_values_dataset
330
460
  v.should be_a_kind_of(Sequel::Dataset)
331
- v.sql.should == 'SELECT values.* FROM values WHERE (node_id = 1234)'
461
+ v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
332
462
  v.model_classes.should == {nil => Historical::Value}
333
463
  end
334
464
 
@@ -338,7 +468,7 @@ describe Sequel::Model, "one_to_many" do
338
468
  n = @c2.new(:id => 1234)
339
469
  a = n.attributes_dataset
340
470
  a.should be_a_kind_of(Sequel::Dataset)
341
- a.sql.should == 'SELECT attributes.* FROM attributes WHERE (nodeid = 1234)'
471
+ a.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
342
472
  end
343
473
 
344
474
  it "should define an add_ method" do
@@ -387,21 +517,21 @@ describe Sequel::Model, "one_to_many" do
387
517
  @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
388
518
 
389
519
  n = @c2.new(:id => 1234)
390
- n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (node_id = 1234)"
520
+ n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
391
521
  end
392
522
 
393
523
  it "should support an order option" do
394
524
  @c2.one_to_many :attributes, :class => @c1, :order => :kind
395
525
 
396
526
  n = @c2.new(:id => 1234)
397
- n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes WHERE (node_id = 1234) ORDER BY kind"
527
+ n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
398
528
  end
399
529
 
400
530
  it "should support an array for the order option" do
401
531
  @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
402
532
 
403
533
  n = @c2.new(:id => 1234)
404
- n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes WHERE (node_id = 1234) ORDER BY kind1, kind2"
534
+ n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
405
535
  end
406
536
 
407
537
  it "should return array with all members of the association" do
@@ -414,7 +544,7 @@ describe Sequel::Model, "one_to_many" do
414
544
  atts.first.should be_a_kind_of(@c1)
415
545
  atts.first.values.should == {}
416
546
 
417
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
547
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
418
548
  end
419
549
 
420
550
  it "should accept a block" do
@@ -429,10 +559,10 @@ describe Sequel::Model, "one_to_many" do
429
559
  atts.first.should be_a_kind_of(@c1)
430
560
  atts.first.values.should == {}
431
561
 
432
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE ((node_id = 1234) AND (xxx IS NULL))']
562
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))']
433
563
  end
434
564
 
435
- it "should support order option with block" do
565
+ it "should support :order option with block" do
436
566
  @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
437
567
  ds.filter(:xxx => @xxx)
438
568
  end
@@ -444,21 +574,38 @@ describe Sequel::Model, "one_to_many" do
444
574
  atts.first.should be_a_kind_of(@c1)
445
575
  atts.first.values.should == {}
446
576
 
447
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE ((node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
577
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
448
578
  end
449
579
 
450
580
  it "should have the block argument affect the _dataset method" do
451
581
  @c2.one_to_many :attributes, :class => @c1 do |ds|
452
582
  ds.filter(:xxx => 456)
453
583
  end
454
- @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes WHERE ((node_id = 1234) AND (xxx = 456))'
584
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx = 456))'
585
+ end
586
+
587
+ it "should support a :dataset option that is used instead of the default" do
588
+ c1 = @c1
589
+ @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.filter(:nodeid=>pk).invert}, :order=>:a, :limit=>10 do |ds|
590
+ ds.filter(:xxx => 5)
591
+ end
592
+
593
+ @c2.new(:id => 1234).all_other_attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
594
+ n = @c2.new(:id => 1234)
595
+ atts = n.all_other_attributes
596
+ atts.should be_a_kind_of(Array)
597
+ atts.size.should == 1
598
+ atts.first.should be_a_kind_of(@c1)
599
+ atts.first.values.should == {}
600
+
601
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
455
602
  end
456
603
 
457
604
  it "should support a :limit option" do
458
605
  @c2.one_to_many :attributes, :class => @c1 , :limit=>10
459
- @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes WHERE (node_id = 1234) LIMIT 10'
606
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10'
460
607
  @c2.one_to_many :attributes, :class => @c1 , :limit=>[10,10]
461
- @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes WHERE (node_id = 1234) LIMIT 10 OFFSET 10'
608
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10 OFFSET 10'
462
609
  end
463
610
 
464
611
  it "should have the :eager option affect the _dataset method" do
@@ -474,7 +621,7 @@ describe Sequel::Model, "one_to_many" do
474
621
  n.associations.include?(:attributes).should == false
475
622
  atts = n.attributes
476
623
  atts.should == n.associations[:attributes]
477
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
624
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
478
625
  end
479
626
 
480
627
  it "should use cached instance variable if available" do
@@ -494,7 +641,7 @@ describe Sequel::Model, "one_to_many" do
494
641
  MODEL_DB.reset
495
642
  n.associations[:attributes] = 42
496
643
  n.attributes(true).should_not == 42
497
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
644
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
498
645
  end
499
646
 
500
647
  it "should add item to cached instance variable if it exists when calling add_" do
@@ -563,7 +710,7 @@ describe Sequel::Model, "one_to_many" do
563
710
  atts.first.should be_a_kind_of(@c1)
564
711
  atts.first.values.should == {}
565
712
 
566
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
713
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
567
714
  end
568
715
 
569
716
  it "should populate the reciprocal many_to_one instance variable when loading the one_to_many association" do
@@ -572,7 +719,7 @@ describe Sequel::Model, "one_to_many" do
572
719
 
573
720
  n = @c2.new(:id => 1234)
574
721
  atts = n.attributes
575
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
722
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
576
723
  atts.should be_a_kind_of(Array)
577
724
  atts.size.should == 1
578
725
  atts.first.should be_a_kind_of(@c1)
@@ -587,7 +734,7 @@ describe Sequel::Model, "one_to_many" do
587
734
 
588
735
  n = @c2.new(:id => 1234)
589
736
  atts = n.attributes
590
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
737
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
591
738
  atts.should be_a_kind_of(Array)
592
739
  atts.size.should == 1
593
740
  atts.first.should be_a_kind_of(@c1)
@@ -649,7 +796,7 @@ describe Sequel::Model, "one_to_many" do
649
796
  it "should add a getter method if the :one_to_one option is true" do
650
797
  @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
651
798
  att = @c2.new(:id => 1234).attribute
652
- MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
799
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
653
800
  att.should be_a_kind_of(@c1)
654
801
  att.values.should == {}
655
802
  end
@@ -682,8 +829,9 @@ describe Sequel::Model, "one_to_many" do
682
829
  MODEL_DB.sqls.clear
683
830
  attrib = @c1.load(:id=>3)
684
831
  @c2.new(:id => 1234).attribute = attrib
685
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234, id = 3 WHERE (id = 3)',
686
- 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))']
832
+ MODEL_DB.sqls.length.should == 2
833
+ MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234, id = 3|id = 3, node_id = 1234) WHERE \(id = 3\)/
834
+ MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
687
835
  end
688
836
 
689
837
  it "should raise an error if the one_to_one getter would be the same as the association name" do
@@ -703,6 +851,113 @@ describe Sequel::Model, "one_to_many" do
703
851
  meths.should(include("add_attribute"))
704
852
  meths.should(include("attributes_dataset"))
705
853
  end
854
+
855
+ it "should call an _add_ method internally to add attributes" do
856
+ @c2.one_to_many :attributes, :class => @c1
857
+ @c2.private_instance_methods.sort.should(include("_add_attribute"))
858
+ p = @c2.load(:id=>10)
859
+ c = @c1.load(:id=>123)
860
+ def p._add_attribute(x)
861
+ @x = x
862
+ end
863
+ c.should_not_receive(:node_id=)
864
+ p.add_attribute(c)
865
+ p.instance_variable_get(:@x).should == c
866
+ end
867
+
868
+ it "should call a _remove_ method internally to remove attributes" do
869
+ @c2.one_to_many :attributes, :class => @c1
870
+ @c2.private_instance_methods.sort.should(include("_remove_attribute"))
871
+ p = @c2.load(:id=>10)
872
+ c = @c1.load(:id=>123)
873
+ def p._remove_attribute(x)
874
+ @x = x
875
+ end
876
+ c.should_not_receive(:node_id=)
877
+ p.remove_attribute(c)
878
+ p.instance_variable_get(:@x).should == c
879
+ end
880
+
881
+ it "should support (before|after)_(add|remove) callbacks" do
882
+ h = []
883
+ @c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
884
+ @c2.class_eval do
885
+ @@blah = h
886
+ def _add_attribute(v)
887
+ @@blah << 4
888
+ end
889
+ def _remove_attribute(v)
890
+ @@blah << 5
891
+ end
892
+ def blah(x)
893
+ @@blah << x.pk
894
+ end
895
+ def blahr(x)
896
+ @@blah << 6
897
+ end
898
+ end
899
+ p = @c2.load(:id=>10)
900
+ c = @c1.load(:id=>123)
901
+ h.should == []
902
+ p.add_attribute(c)
903
+ h.should == [10, -123, 123, 4, 3]
904
+ p.remove_attribute(c)
905
+ h.should == [10, -123, 123, 4, 3, 123, 5, 6]
906
+ end
907
+
908
+ it "should support after_load association callback" do
909
+ h = []
910
+ @c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
911
+ @c2.class_eval do
912
+ @@blah = h
913
+ def al(v)
914
+ v.each{|x| @@blah << x.pk}
915
+ end
916
+ end
917
+ @c1.class_eval do
918
+ def @dataset.fetch_rows(sql)
919
+ yield({:id=>20})
920
+ yield({:id=>30})
921
+ end
922
+ end
923
+ p = @c2.load(:id=>10, :parent_id=>20)
924
+ attributes = p.attributes
925
+ h.should == [[10, [20, 30]], 20, 30]
926
+ attributes.collect{|a| a.pk}.should == [20, 30]
927
+ end
928
+
929
+ it "should raise error and not call internal add or remove method if before callback returns false if raise_on_save_failure is true" do
930
+ p = @c2.load(:id=>10)
931
+ c = @c1.load(:id=>123)
932
+ @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
933
+ p.should_receive(:ba).once.with(c).and_return(false)
934
+ p.should_not_receive(:_add_attribute)
935
+ p.should_not_receive(:_remove_attribute)
936
+ p.associations[:attributes] = []
937
+ proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
938
+ p.attributes.should == []
939
+ p.associations[:attributes] = [c]
940
+ p.should_receive(:br).once.with(c).and_return(false)
941
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
942
+ p.attributes.should == [c]
943
+ end
944
+
945
+ it "should return nil and not call internal add or remove method if before callback returns false if raise_on_save_failure is false" do
946
+ p = @c2.load(:id=>10)
947
+ c = @c1.load(:id=>123)
948
+ p.raise_on_save_failure = false
949
+ @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
950
+ p.should_receive(:ba).once.with(c).and_return(false)
951
+ p.should_not_receive(:_add_attribute)
952
+ p.should_not_receive(:_remove_attribute)
953
+ p.associations[:attributes] = []
954
+ p.add_attribute(c).should == nil
955
+ p.attributes.should == []
956
+ p.associations[:attributes] = [c]
957
+ p.should_receive(:br).once.with(c).and_return(false)
958
+ p.remove_attribute(c).should == nil
959
+ p.attributes.should == [c]
960
+ end
706
961
  end
707
962
 
708
963
  describe Sequel::Model, "many_to_many" do
@@ -829,7 +1084,7 @@ describe Sequel::Model, "many_to_many" do
829
1084
  MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
830
1085
  end
831
1086
 
832
- it "should allow the order option while accepting a block" do
1087
+ it "should allow the :order option while accepting a block" do
833
1088
  @c2.many_to_many :attributes, :class => @c1, :order=>[:blah1, :blah2] do |ds|
834
1089
  ds.filter(:xxx => @xxx)
835
1090
  end
@@ -850,6 +1105,22 @@ describe Sequel::Model, "many_to_many" do
850
1105
  @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 456)'
851
1106
  end
852
1107
 
1108
+ it "should support a :dataset option that is used instead of the default" do
1109
+ c1 = @c1
1110
+ @c2.many_to_many :attributes, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(:an__nodeid=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
1111
+ ds.filter(:xxx => @xxx)
1112
+ end
1113
+
1114
+ n = @c2.new(:id => 1234)
1115
+ n.xxx = 555
1116
+ n.attributes_dataset.sql.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
1117
+ a = n.attributes
1118
+ a.should be_a_kind_of(Array)
1119
+ a.size.should == 1
1120
+ a.first.should be_a_kind_of(@c1)
1121
+ MODEL_DB.sqls.first.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
1122
+ end
1123
+
853
1124
  it "should support a :limit option" do
854
1125
  @c2.many_to_many :attributes, :class => @c1 , :limit=>10
855
1126
  @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) LIMIT 10'
@@ -1055,10 +1326,118 @@ describe Sequel::Model, "many_to_many" do
1055
1326
  node.remove_all_attributes
1056
1327
  attrib.associations[:nodes].should == []
1057
1328
  end
1329
+
1330
+ it "should call an _add_ method internally to add attributes" do
1331
+ @c2.many_to_many :attributes, :class => @c1
1332
+ @c2.private_instance_methods.sort.should(include("_add_attribute"))
1333
+ p = @c2.load(:id=>10)
1334
+ c = @c1.load(:id=>123)
1335
+ def p._add_attribute(x)
1336
+ @x = x
1337
+ end
1338
+ p.add_attribute(c)
1339
+ p.instance_variable_get(:@x).should == c
1340
+ MODEL_DB.sqls.should == []
1341
+ end
1342
+
1343
+ it "should call a _remove_ method internally to remove attributes" do
1344
+ @c2.many_to_many :attributes, :class => @c1
1345
+ @c2.private_instance_methods.sort.should(include("_remove_attribute"))
1346
+ p = @c2.load(:id=>10)
1347
+ c = @c1.load(:id=>123)
1348
+ def p._remove_attribute(x)
1349
+ @x = x
1350
+ end
1351
+ p.remove_attribute(c)
1352
+ p.instance_variable_get(:@x).should == c
1353
+ MODEL_DB.sqls.should == []
1354
+ end
1355
+
1356
+ it "should support (before|after)_(add|remove) callbacks" do
1357
+ h = []
1358
+ @c2.many_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
1359
+ @c2.class_eval do
1360
+ @@blah = h
1361
+ def _add_attribute(v)
1362
+ @@blah << 4
1363
+ end
1364
+ def _remove_attribute(v)
1365
+ @@blah << 5
1366
+ end
1367
+ def blah(x)
1368
+ @@blah << x.pk
1369
+ end
1370
+ def blahr(x)
1371
+ @@blah << 6
1372
+ end
1373
+ end
1374
+ p = @c2.load(:id=>10)
1375
+ c = @c1.load(:id=>123)
1376
+ h.should == []
1377
+ p.add_attribute(c)
1378
+ h.should == [10, -123, 123, 4, 3]
1379
+ p.remove_attribute(c)
1380
+ h.should == [10, -123, 123, 4, 3, 123, 5, 6]
1381
+ end
1382
+
1383
+ it "should support after_load association callback" do
1384
+ h = []
1385
+ @c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
1386
+ @c2.class_eval do
1387
+ @@blah = h
1388
+ def al(v)
1389
+ v.each{|x| @@blah << x.pk}
1390
+ end
1391
+ end
1392
+ @c1.class_eval do
1393
+ def @dataset.fetch_rows(sql)
1394
+ yield({:id=>20})
1395
+ yield({:id=>30})
1396
+ end
1397
+ end
1398
+ p = @c2.load(:id=>10, :parent_id=>20)
1399
+ attributes = p.attributes
1400
+ h.should == [[10, [20, 30]], 20, 30]
1401
+ attributes.collect{|a| a.pk}.should == [20, 30]
1402
+ end
1403
+
1404
+ it "should raise error and not call internal add or remove method if before callback returns false if raise_on_save_failure is true" do
1405
+ p = @c2.load(:id=>10)
1406
+ c = @c1.load(:id=>123)
1407
+ @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1408
+ p.should_receive(:ba).once.with(c).and_return(false)
1409
+ p.should_not_receive(:_add_attribute)
1410
+ p.should_not_receive(:_remove_attribute)
1411
+ p.associations[:attributes] = []
1412
+ p.raise_on_save_failure = true
1413
+ proc{p.add_attribute(c)}.should raise_error(Sequel::Error)
1414
+ p.attributes.should == []
1415
+ p.associations[:attributes] = [c]
1416
+ p.should_receive(:br).once.with(c).and_return(false)
1417
+ proc{p.remove_attribute(c)}.should raise_error(Sequel::Error)
1418
+ p.attributes.should == [c]
1419
+ end
1420
+
1421
+ it "should return nil and not call internal add or remove method if before callback returns false if raise_on_save_failure is false" do
1422
+ p = @c2.load(:id=>10)
1423
+ c = @c1.load(:id=>123)
1424
+ p.raise_on_save_failure = false
1425
+ @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1426
+ p.should_receive(:ba).once.with(c).and_return(false)
1427
+ p.should_not_receive(:_add_attribute)
1428
+ p.should_not_receive(:_remove_attribute)
1429
+ p.associations[:attributes] = []
1430
+ p.add_attribute(c).should == nil
1431
+ p.attributes.should == []
1432
+ p.associations[:attributes] = [c]
1433
+ p.should_receive(:br).once.with(c).and_return(false)
1434
+ p.remove_attribute(c).should == nil
1435
+ p.attributes.should == [c]
1436
+ end
1058
1437
  end
1059
1438
 
1060
- describe Sequel::Model, "all_association_reflections" do
1061
- before(:each) do
1439
+ describe Sequel::Model, " association reflection methods" do
1440
+ before do
1062
1441
  MODEL_DB.reset
1063
1442
  @c1 = Class.new(Sequel::Model(:nodes)) do
1064
1443
  def self.name; 'Node'; end
@@ -1066,64 +1445,40 @@ describe Sequel::Model, "all_association_reflections" do
1066
1445
  end
1067
1446
  end
1068
1447
 
1069
- it "should include all association reflection hashes" do
1448
+ it "#all_association_reflections should include all association reflection hashes" do
1070
1449
  @c1.all_association_reflections.should == []
1450
+
1071
1451
  @c1.associate :many_to_one, :parent, :class => @c1
1072
- @c1.all_association_reflections.should == [{
1073
- :type => :many_to_one, :name => :parent, :class_name => 'Node',
1074
- :class => @c1, :key => :parent_id, :block => nil, :cache => true,
1075
- :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
1076
- }]
1077
- @c1.associate :one_to_many, :children, :class => @c1
1078
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.should == [{
1079
- :type => :one_to_many, :name => :children, :class_name => 'Node',
1080
- :class => @c1, :key => :node_id, :block => nil, :cache => true,
1081
- :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1}, {
1082
- :type => :many_to_one, :name => :parent, :class_name => 'Node',
1083
- :class => @c1, :key => :parent_id, :block => nil, :cache => true,
1084
- :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1}]
1085
- end
1086
- end
1452
+ @c1.all_association_reflections.collect{|v| v[:name]}.should == [:parent]
1453
+ @c1.all_association_reflections.collect{|v| v[:type]}.should == [:many_to_one]
1454
+ @c1.all_association_reflections.collect{|v| v[:class]}.should == [@c1]
1087
1455
 
1088
- describe Sequel::Model, "association_reflection" do
1089
- before(:each) do
1090
- MODEL_DB.reset
1091
- @c1 = Class.new(Sequel::Model(:nodes)) do
1092
- def self.name; 'Node'; end
1093
- def self.to_s; 'Node'; end
1094
- end
1456
+ @c1.associate :one_to_many, :children, :class => @c1
1457
+ @c1.all_association_reflections.sort_by{|x|x[:name].to_s}
1458
+ @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:name]}.should == [:children, :parent]
1459
+ @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:type]}.should == [:one_to_many, :many_to_one]
1460
+ @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:class]}.should == [@c1, @c1]
1095
1461
  end
1096
1462
 
1097
- it "should return nil for nonexistent association" do
1463
+ it "#association_reflection should return nil for nonexistent association" do
1098
1464
  @c1.association_reflection(:blah).should == nil
1099
1465
  end
1100
1466
 
1101
- it "should return association reflection hash if association exists" do
1467
+ it "#association_reflection should return association reflection hash if association exists" do
1102
1468
  @c1.associate :many_to_one, :parent, :class => @c1
1103
- @c1.association_reflection(:parent).should == {
1104
- :type => :many_to_one, :name => :parent, :class_name => 'Node',
1105
- :class => @c1, :key => :parent_id, :block => nil, :cache => true,
1106
- :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
1107
- }
1108
- @c1.associate :one_to_many, :children, :class => @c1
1109
- @c1.association_reflection(:children).should == {
1110
- :type => :one_to_many, :name => :children, :class_name => 'Node',
1111
- :class => @c1, :key => :node_id, :block => nil, :cache => true,
1112
- :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
1113
- }
1114
- end
1115
- end
1469
+ @c1.association_reflection(:parent).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
1470
+ @c1.association_reflection(:parent)[:name].should == :parent
1471
+ @c1.association_reflection(:parent)[:type].should == :many_to_one
1472
+ @c1.association_reflection(:parent)[:class].should == @c1
1116
1473
 
1117
- describe Sequel::Model, "associations" do
1118
- before(:each) do
1119
- MODEL_DB.reset
1120
- @c1 = Class.new(Sequel::Model(:nodes)) do
1121
- def self.name; 'Node'; end
1122
- def self.to_s; 'Node'; end
1123
- end
1474
+ @c1.associate :one_to_many, :children, :class => @c1
1475
+ @c1.association_reflection(:children).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
1476
+ @c1.association_reflection(:children)[:name].should == :children
1477
+ @c1.association_reflection(:children)[:type].should == :one_to_many
1478
+ @c1.association_reflection(:children)[:class].should == @c1
1124
1479
  end
1125
1480
 
1126
- it "should include all association names" do
1481
+ it "#associations should include all association names" do
1127
1482
  @c1.associations.should == []
1128
1483
  @c1.associate :many_to_one, :parent, :class => @c1
1129
1484
  @c1.associations.should == [:parent]