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,48 @@
1
+ module Sequel
2
+ class Model
3
+ # Defines a table schema (see Schema::Generator for more information).
4
+ #
5
+ # This is only needed if you want to use the create_table or drop_table
6
+ # methods.
7
+ def self.set_schema(name = nil, &block)
8
+ if name
9
+ set_dataset(db[name])
10
+ end
11
+ @schema = Schema::Generator.new(db, &block)
12
+ if @schema.primary_key_name
13
+ set_primary_key @schema.primary_key_name
14
+ end
15
+ end
16
+
17
+ # Returns table schema for direct descendant of Model.
18
+ def self.schema
19
+ @schema || ((superclass != Model) && (superclass.schema))
20
+ end
21
+
22
+ # Returns name of table.
23
+ def self.table_name
24
+ dataset.opts[:from].first
25
+ end
26
+
27
+ # Returns true if table exists, false otherwise.
28
+ def self.table_exists?
29
+ db.table_exists?(table_name)
30
+ end
31
+
32
+ # Creates table.
33
+ def self.create_table
34
+ db.create_table_sql_list(table_name, *schema.create_info).each {|s| db << s}
35
+ end
36
+
37
+ # Drops table.
38
+ def self.drop_table
39
+ db.execute db.drop_table_sql(table_name)
40
+ end
41
+
42
+ # Like create_table but invokes drop_table when table_exists? is true.
43
+ def self.create_table!
44
+ drop_table if table_exists?
45
+ create_table
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ gem "assistance", ">= 0.1.2" # because we need Validations
2
+
3
+ require "assistance"
4
+
5
+ module Sequel
6
+ class Model
7
+ include Validation
8
+
9
+ alias_method :save!, :save
10
+ def save(*args)
11
+ return false unless valid?
12
+ save!(*args)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,712 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "associate" do
4
+ it "should use explicit class if given a class, symbol, or string" do
5
+ MODEL_DB.reset
6
+ klass = Class.new(Sequel::Model(:nodes))
7
+ class ParParent < Sequel::Model
8
+ end
9
+
10
+ klass.associate :many_to_one, :par_parent0, :class=>ParParent
11
+ klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
12
+ klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
13
+
14
+ klass.send(:associated_class, klass.association_reflection(:"par_parent0")).should == ParParent
15
+ klass.send(:associated_class, klass.association_reflection(:"par_parent1s")).should == ParParent
16
+ klass.send(:associated_class, klass.association_reflection(:"par_parent2s")).should == ParParent
17
+ end
18
+ end
19
+
20
+ describe Sequel::Model, "many_to_one" do
21
+
22
+ before(:each) do
23
+ MODEL_DB.reset
24
+
25
+ @c2 = Class.new(Sequel::Model(:nodes)) do
26
+ def columns; [:id, :parent_id]; end
27
+ end
28
+
29
+ @dataset = @c2.dataset
30
+ end
31
+
32
+ it "should use implicit key if omitted" do
33
+ @c2.many_to_one :parent, :class => @c2
34
+
35
+ d = @c2.new(:id => 1, :parent_id => 234)
36
+ p = d.parent
37
+ p.class.should == @c2
38
+ p.values.should == {:x => 1, :id => 1}
39
+
40
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
41
+ end
42
+
43
+ it "should use implicit class if omitted" do
44
+ class ParParent < Sequel::Model
45
+ end
46
+
47
+ @c2.many_to_one :par_parent
48
+
49
+ d = @c2.new(:id => 1, :par_parent_id => 234)
50
+ p = d.par_parent
51
+ p.class.should == ParParent
52
+
53
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (id = 234) LIMIT 1"]
54
+ end
55
+
56
+ it "should use explicit key if given" do
57
+ @c2.many_to_one :parent, :class => @c2, :key => :blah
58
+
59
+ d = @c2.new(:id => 1, :blah => 567)
60
+ p = d.parent
61
+ p.class.should == @c2
62
+ p.values.should == {:x => 1, :id => 1}
63
+
64
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
65
+ end
66
+
67
+ it "should return nil if key value is nil" do
68
+ @c2.many_to_one :parent, :class => @c2
69
+
70
+ d = @c2.new(:id => 1)
71
+ d.parent.should == nil
72
+ end
73
+
74
+ it "should define a setter method" do
75
+ @c2.many_to_one :parent, :class => @c2
76
+
77
+ d = @c2.new(:id => 1)
78
+ d.parent = @c2.new(:id => 4321)
79
+ d.values.should == {:id => 1, :parent_id => 4321}
80
+
81
+ d.parent = nil
82
+ d.values.should == {:id => 1, :parent_id => nil}
83
+
84
+ e = @c2.new(:id => 6677)
85
+ d.parent = e
86
+ d.values.should == {:id => 1, :parent_id => 6677}
87
+ end
88
+
89
+ it "should not persist changes until saved" do
90
+ @c2.many_to_one :parent, :class => @c2
91
+
92
+ d = @c2.create(:id => 1)
93
+ MODEL_DB.reset
94
+ d.parent = @c2.new(:id => 345)
95
+ MODEL_DB.sqls.should == []
96
+ d.save_changes
97
+ MODEL_DB.sqls.should == ['UPDATE nodes SET parent_id = 345 WHERE (id = 1)']
98
+ end
99
+
100
+ it "should set cached instance variable when accessed" do
101
+ @c2.many_to_one :parent, :class => @c2
102
+
103
+ d = @c2.create(:id => 1)
104
+ MODEL_DB.reset
105
+ d.parent_id = 234
106
+ d.instance_variables.include?("@parent").should == false
107
+ e = d.parent
108
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
109
+ d.instance_variable_get("@parent").should == e
110
+ end
111
+
112
+ it "should set cached instance variable when assigned" do
113
+ @c2.many_to_one :parent, :class => @c2
114
+
115
+ d = @c2.create(:id => 1)
116
+ MODEL_DB.reset
117
+ d.instance_variables.include?("@parent").should == false
118
+ d.parent = @c2.new(:id => 234)
119
+ e = d.parent
120
+ d.instance_variable_get("@parent").should == e
121
+ MODEL_DB.sqls.should == []
122
+ end
123
+
124
+ it "should use cached instance variable if available" do
125
+ @c2.many_to_one :parent, :class => @c2
126
+
127
+ d = @c2.create(:id => 1, :parent_id => 234)
128
+ MODEL_DB.reset
129
+ d.instance_variable_set(:@parent, 42)
130
+ d.parent.should == 42
131
+ MODEL_DB.sqls.should == []
132
+ end
133
+
134
+ it "should not use cached instance variable if asked to reload" do
135
+ @c2.many_to_one :parent, :class => @c2
136
+
137
+ d = @c2.create(:id => 1)
138
+ MODEL_DB.reset
139
+ d.parent_id = 234
140
+ d.instance_variable_set(:@parent, 42)
141
+ d.parent(true).should_not == 42
142
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
143
+ end
144
+
145
+ it "should have belongs_to alias" do
146
+ @c2.belongs_to :parent, :class => @c2
147
+
148
+ d = @c2.create(:id => 1)
149
+ MODEL_DB.reset
150
+ d.parent_id = 234
151
+ d.instance_variables.include?("@parent").should == false
152
+ e = d.parent
153
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
154
+ d.instance_variable_get("@parent").should == e
155
+ end
156
+ end
157
+
158
+ describe Sequel::Model, "one_to_many" do
159
+
160
+ before(:each) do
161
+ MODEL_DB.reset
162
+
163
+ @c1 = Class.new(Sequel::Model(:attributes)) do
164
+ def columns; [:id, :node_id]; end
165
+ end
166
+
167
+ @c2 = Class.new(Sequel::Model(:nodes)) do
168
+ attr_accessor :xxx
169
+
170
+ def self.name; 'Node'; end
171
+ def self.to_s; 'Node'; end
172
+ end
173
+ @dataset = @c2.dataset
174
+
175
+ @c2.dataset.extend(Module.new {
176
+ def fetch_rows(sql)
177
+ @db << sql
178
+ yield Hash.new
179
+ end
180
+ })
181
+
182
+ @c1.dataset.extend(Module.new {
183
+ def fetch_rows(sql)
184
+ @db << sql
185
+ yield Hash.new
186
+ end
187
+ })
188
+ end
189
+
190
+ it "should use implicit key if omitted" do
191
+ @c2.one_to_many :attributes, :class => @c1
192
+
193
+ n = @c2.new(:id => 1234)
194
+ a = n.attributes_dataset
195
+ a.should be_a_kind_of(Sequel::Dataset)
196
+ a.sql.should == 'SELECT * FROM attributes WHERE (node_id = 1234)'
197
+ end
198
+
199
+ it "should use implicit class if omitted" do
200
+ class HistoricalValue < Sequel::Model
201
+ end
202
+
203
+ @c2.one_to_many :historical_values
204
+
205
+ n = @c2.new(:id => 1234)
206
+ v = n.historical_values_dataset
207
+ v.should be_a_kind_of(Sequel::Dataset)
208
+ v.sql.should == 'SELECT * FROM historical_values WHERE (node_id = 1234)'
209
+ v.model_classes.should == {nil => HistoricalValue}
210
+ end
211
+
212
+ it "should use explicit key if given" do
213
+ @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
214
+
215
+ n = @c2.new(:id => 1234)
216
+ a = n.attributes_dataset
217
+ a.should be_a_kind_of(Sequel::Dataset)
218
+ a.sql.should == 'SELECT * FROM attributes WHERE (nodeid = 1234)'
219
+ end
220
+
221
+ it "should define an add_ method" do
222
+ @c2.one_to_many :attributes, :class => @c1
223
+
224
+ n = @c2.new(:id => 1234)
225
+ a = @c1.new(:id => 2345)
226
+ a.save!
227
+ MODEL_DB.reset
228
+ a.should == n.add_attribute(a)
229
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
230
+ end
231
+
232
+ it "should define a remove_ method" do
233
+ @c2.one_to_many :attributes, :class => @c1
234
+
235
+ n = @c2.new(:id => 1234)
236
+ a = @c1.new(:id => 2345)
237
+ a.save!
238
+ MODEL_DB.reset
239
+ a.should == n.remove_attribute(a)
240
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
241
+ end
242
+
243
+ it "should support an order option" do
244
+ @c2.one_to_many :attributes, :class => @c1, :order => :kind
245
+
246
+ n = @c2.new(:id => 1234)
247
+ n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (node_id = 1234) ORDER BY kind"
248
+ end
249
+
250
+ it "should support an array for the order option" do
251
+ @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
252
+
253
+ n = @c2.new(:id => 1234)
254
+ n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (node_id = 1234) ORDER BY kind1, kind2"
255
+ end
256
+
257
+ it "should return array with all members of the association" do
258
+ @c2.one_to_many :attributes, :class => @c1
259
+
260
+ n = @c2.new(:id => 1234)
261
+ atts = n.attributes
262
+ atts.should be_a_kind_of(Array)
263
+ atts.size.should == 1
264
+ atts.first.should be_a_kind_of(@c1)
265
+ atts.first.values.should == {}
266
+
267
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
268
+ end
269
+
270
+ it "should accept a block" do
271
+ @c2.one_to_many :attributes, :class => @c1 do |ds|
272
+ ds.filter(:xxx => @xxx)
273
+ end
274
+
275
+ n = @c2.new(:id => 1234)
276
+ atts = n.attributes
277
+ atts.should be_a_kind_of(Array)
278
+ atts.size.should == 1
279
+ atts.first.should be_a_kind_of(@c1)
280
+ atts.first.values.should == {}
281
+
282
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234) AND (xxx IS NULL)']
283
+ end
284
+
285
+ it "should support order option with block" do
286
+ @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
287
+ ds.filter(:xxx => @xxx)
288
+ end
289
+
290
+ n = @c2.new(:id => 1234)
291
+ atts = n.attributes
292
+ atts.should be_a_kind_of(Array)
293
+ atts.size.should == 1
294
+ atts.first.should be_a_kind_of(@c1)
295
+ atts.first.values.should == {}
296
+
297
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234) AND (xxx IS NULL) ORDER BY kind']
298
+ end
299
+
300
+ it "should set cached instance variable when accessed" do
301
+ @c2.one_to_many :attributes, :class => @c1
302
+
303
+ n = @c2.new(:id => 1234)
304
+ MODEL_DB.reset
305
+ n.instance_variables.include?("@attributes").should == false
306
+ atts = n.attributes
307
+ atts.should == n.instance_variable_get("@attributes")
308
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
309
+ end
310
+
311
+ it "should use cached instance variable if available" do
312
+ @c2.one_to_many :attributes, :class => @c1
313
+
314
+ n = @c2.new(:id => 1234)
315
+ MODEL_DB.reset
316
+ n.instance_variable_set(:@attributes, 42)
317
+ n.attributes.should == 42
318
+ MODEL_DB.sqls.should == []
319
+ end
320
+
321
+ it "should not use cached instance variable if asked to reload" do
322
+ @c2.one_to_many :attributes, :class => @c1
323
+
324
+ n = @c2.new(:id => 1234)
325
+ MODEL_DB.reset
326
+ n.instance_variable_set(:@attributes, 42)
327
+ n.attributes(true).should_not == 42
328
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
329
+ end
330
+
331
+ it "should add item to cached instance variable if it exists when calling add_" do
332
+ @c2.one_to_many :attributes, :class => @c1
333
+
334
+ n = @c2.new(:id => 1234)
335
+ att = @c1.new(:id => 345)
336
+ MODEL_DB.reset
337
+ a = []
338
+ n.instance_variable_set(:@attributes, a)
339
+ n.add_attribute(att)
340
+ a.should == [att]
341
+ end
342
+
343
+ it "should remove item from cached instance variable if it exists when calling remove_" do
344
+ @c2.one_to_many :attributes, :class => @c1
345
+
346
+ n = @c2.new(:id => 1234)
347
+ att = @c1.new(:id => 345)
348
+ MODEL_DB.reset
349
+ a = [att]
350
+ n.instance_variable_set(:@attributes, a)
351
+ n.remove_attribute(att)
352
+ a.should == []
353
+ end
354
+
355
+ it "should have has_many alias" do
356
+ @c2.has_many :attributes, :class => @c1
357
+
358
+ n = @c2.new(:id => 1234)
359
+ atts = n.attributes
360
+ atts.should be_a_kind_of(Array)
361
+ atts.size.should == 1
362
+ atts.first.should be_a_kind_of(@c1)
363
+ atts.first.values.should == {}
364
+
365
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
366
+ end
367
+
368
+ it "should populate the reciprocal many_to_one instance variable when loading the one_to_many association" do
369
+ @c2.one_to_many :attributes, :class => @c1, :key => :node_id
370
+ @c1.many_to_one :node, :class => @c2, :key => :node_id
371
+
372
+ n = @c2.new(:id => 1234)
373
+ atts = n.attributes
374
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
375
+ atts.should be_a_kind_of(Array)
376
+ atts.size.should == 1
377
+ atts.first.should be_a_kind_of(@c1)
378
+ atts.first.values.should == {}
379
+ atts.first.node.should == n
380
+
381
+ MODEL_DB.sqls.length.should == 1
382
+ end
383
+
384
+ it "should use an explicit reciprocal instance variable if given" do
385
+ @c2.one_to_many :attributes, :class => @c1, :key => :node_id, :reciprocal=>'@wxyz'
386
+
387
+ n = @c2.new(:id => 1234)
388
+ atts = n.attributes
389
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
390
+ atts.should be_a_kind_of(Array)
391
+ atts.size.should == 1
392
+ atts.first.should be_a_kind_of(@c1)
393
+ atts.first.values.should == {}
394
+ atts.first.instance_variable_get('@wxyz').should == n
395
+
396
+ MODEL_DB.sqls.length.should == 1
397
+ end
398
+ end
399
+
400
+ describe Sequel::Model, "many_to_many" do
401
+
402
+ before(:each) do
403
+ MODEL_DB.reset
404
+
405
+ @c1 = Class.new(Sequel::Model(:attributes)) do
406
+ def self.name; 'Attribute'; end
407
+ def self.to_s; 'Attribute'; end
408
+ end
409
+
410
+ @c2 = Class.new(Sequel::Model(:nodes)) do
411
+ attr_accessor :xxx
412
+
413
+ def self.name; 'Node'; end
414
+ def self.to_s; 'Node'; end
415
+ end
416
+ @dataset = @c2.dataset
417
+
418
+ [@c1, @c2].each do |c|
419
+ c.dataset.extend(Module.new {
420
+ def fetch_rows(sql)
421
+ @db << sql
422
+ yield Hash.new
423
+ end
424
+ })
425
+ end
426
+ end
427
+
428
+ it "should use implicit key values and join table if omitted" do
429
+ @c2.many_to_many :attributes, :class => @c1
430
+
431
+ n = @c2.new(:id => 1234)
432
+ a = n.attributes_dataset
433
+ a.should be_a_kind_of(Sequel::Dataset)
434
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
435
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
436
+ ].should(include(a.sql))
437
+ end
438
+
439
+ it "should use implicit class if omitted" do
440
+ class Tag < Sequel::Model
441
+ end
442
+
443
+ @c2.many_to_many :tags
444
+
445
+ n = @c2.new(:id => 1234)
446
+ a = n.tags_dataset
447
+ a.should be_a_kind_of(Sequel::Dataset)
448
+ ['SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234)',
449
+ 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.node_id = 1234) AND (nodes_tags.tag_id = tags.id)'
450
+ ].should(include(a.sql))
451
+ end
452
+
453
+ it "should use explicit key values and join table if given" do
454
+ @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
455
+
456
+ n = @c2.new(:id => 1234)
457
+ a = n.attributes_dataset
458
+ a.should be_a_kind_of(Sequel::Dataset)
459
+ ['SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.nodeid = 1234) AND (attribute2node.attributeid = attributes.id)',
460
+ 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234)'
461
+ ].should(include(a.sql))
462
+ end
463
+
464
+ it "should support an order option" do
465
+ @c2.many_to_many :attributes, :class => @c1, :order => :blah
466
+
467
+ n = @c2.new(:id => 1234)
468
+ a = n.attributes_dataset
469
+ a.should be_a_kind_of(Sequel::Dataset)
470
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) ORDER BY blah',
471
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) ORDER BY blah'
472
+ ].should(include(a.sql))
473
+ end
474
+
475
+ it "should support an array for the order option" do
476
+ @c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
477
+
478
+ n = @c2.new(:id => 1234)
479
+ a = n.attributes_dataset
480
+ a.should be_a_kind_of(Sequel::Dataset)
481
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) ORDER BY blah1, blah2',
482
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) ORDER BY blah1, blah2'
483
+ ].should(include(a.sql))
484
+ end
485
+
486
+ it "should support a select option" do
487
+ @c2.many_to_many :attributes, :class => @c1, :select => :blah
488
+
489
+ n = @c2.new(:id => 1234)
490
+ a = n.attributes_dataset
491
+ a.should be_a_kind_of(Sequel::Dataset)
492
+ ['SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
493
+ 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
494
+ ].should(include(a.sql))
495
+ end
496
+
497
+ it "should support an array for the select option" do
498
+ @c2.many_to_many :attributes, :class => @c1, :select => [:attributes.all, :attribute_nodes__blah2]
499
+
500
+ n = @c2.new(:id => 1234)
501
+ a = n.attributes_dataset
502
+ a.should be_a_kind_of(Sequel::Dataset)
503
+ ['SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
504
+ 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
505
+ ].should(include(a.sql))
506
+ end
507
+
508
+ it "should accept a block" do
509
+ @c2.many_to_many :attributes, :class => @c1 do |ds|
510
+ ds.filter(:xxx => @xxx)
511
+ end
512
+
513
+ n = @c2.new(:id => 1234)
514
+ n.xxx = 555
515
+ a = n.attributes
516
+ a.should be_a_kind_of(Array)
517
+ a.size.should == 1
518
+ a.first.should be_a_kind_of(@c1)
519
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) WHERE (xxx = 555)',
520
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) WHERE (xxx = 555)'
521
+ ].should(include(MODEL_DB.sqls.first))
522
+ end
523
+
524
+ it "should allow the order option while accepting a block" do
525
+ @c2.many_to_many :attributes, :class => @c1, :order=>[:blah1, :blah2] do |ds|
526
+ ds.filter(:xxx => @xxx)
527
+ end
528
+
529
+ n = @c2.new(:id => 1234)
530
+ n.xxx = 555
531
+ a = n.attributes
532
+ a.should be_a_kind_of(Array)
533
+ a.size.should == 1
534
+ a.first.should be_a_kind_of(@c1)
535
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) WHERE (xxx = 555) ORDER BY blah1, blah2',
536
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) WHERE (xxx = 555) ORDER BY blah1, blah2'
537
+ ].should(include(MODEL_DB.sqls.first))
538
+ end
539
+
540
+ it "should define an add_ method" do
541
+ @c2.many_to_many :attributes, :class => @c1
542
+
543
+ n = @c2.new(:id => 1234)
544
+ a = @c1.new(:id => 2345)
545
+ a.should == n.add_attribute(a)
546
+ ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
547
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
548
+ ].should(include(MODEL_DB.sqls.first))
549
+ end
550
+
551
+ it "should define a remove_ method" do
552
+ @c2.many_to_many :attributes, :class => @c1
553
+
554
+ n = @c2.new(:id => 1234)
555
+ a = @c1.new(:id => 2345)
556
+ a.should == n.remove_attribute(a)
557
+ ['DELETE FROM attributes_nodes WHERE (node_id = 1234) AND (attribute_id = 2345)',
558
+ 'DELETE FROM attributes_nodes WHERE (attribute_id = 2345) AND (node_id = 1234)'
559
+ ].should(include(MODEL_DB.sqls.first))
560
+ end
561
+
562
+ it "should provide an array with all members of the association" do
563
+ @c2.many_to_many :attributes, :class => @c1
564
+
565
+ n = @c2.new(:id => 1234)
566
+ atts = n.attributes
567
+ atts.should be_a_kind_of(Array)
568
+ atts.size.should == 1
569
+ atts.first.should be_a_kind_of(@c1)
570
+
571
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
572
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
573
+ ].should(include(MODEL_DB.sqls.first))
574
+ end
575
+
576
+ it "should set cached instance variable when accessed" do
577
+ @c2.many_to_many :attributes, :class => @c1
578
+
579
+ n = @c2.new(:id => 1234)
580
+ MODEL_DB.reset
581
+ n.instance_variables.include?("@attributes").should == false
582
+ atts = n.attributes
583
+ atts.should == n.instance_variable_get("@attributes")
584
+ MODEL_DB.sqls.length.should == 1
585
+ end
586
+
587
+ it "should use cached instance variable if available" do
588
+ @c2.many_to_many :attributes, :class => @c1
589
+
590
+ n = @c2.new(:id => 1234)
591
+ MODEL_DB.reset
592
+ n.instance_variable_set(:@attributes, 42)
593
+ n.attributes.should == 42
594
+ MODEL_DB.sqls.should == []
595
+ end
596
+
597
+ it "should not use cached instance variable if asked to reload" do
598
+ @c2.many_to_many :attributes, :class => @c1
599
+
600
+ n = @c2.new(:id => 1234)
601
+ MODEL_DB.reset
602
+ n.instance_variable_set(:@attributes, 42)
603
+ n.attributes(true).should_not == 42
604
+ MODEL_DB.sqls.length.should == 1
605
+ end
606
+
607
+ it "should add item to cached instance variable if it exists when calling add_" do
608
+ @c2.many_to_many :attributes, :class => @c1
609
+
610
+ n = @c2.new(:id => 1234)
611
+ att = @c1.new(:id => 345)
612
+ MODEL_DB.reset
613
+ a = []
614
+ n.instance_variable_set(:@attributes, a)
615
+ n.add_attribute(att)
616
+ a.should == [att]
617
+ end
618
+
619
+ it "should remove item from cached instance variable if it exists when calling remove_" do
620
+ @c2.many_to_many :attributes, :class => @c1
621
+
622
+ n = @c2.new(:id => 1234)
623
+ att = @c1.new(:id => 345)
624
+ MODEL_DB.reset
625
+ a = [att]
626
+ n.instance_variable_set(:@attributes, a)
627
+ n.remove_attribute(att)
628
+ a.should == []
629
+ end
630
+
631
+ it "should have has_and_belongs_to_many alias" do
632
+ @c2.has_and_belongs_to_many :attributes, :class => @c1
633
+
634
+ n = @c2.new(:id => 1234)
635
+ a = n.attributes_dataset
636
+ a.should be_a_kind_of(Sequel::Dataset)
637
+ ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
638
+ 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
639
+ ].should(include(a.sql))
640
+ end
641
+
642
+ end
643
+
644
+ describe Sequel::Model, "all_association_reflections" do
645
+ before(:each) do
646
+ MODEL_DB.reset
647
+ @c1 = Class.new(Sequel::Model(:nodes)) do
648
+ def self.name; 'Node'; end
649
+ def self.to_s; 'Node'; end
650
+ end
651
+ end
652
+
653
+ it "should include all association reflection hashes" do
654
+ @c1.all_association_reflections.should == []
655
+ @c1.associate :many_to_one, :parent, :class => @c1
656
+ @c1.all_association_reflections.should == [{
657
+ :type => :many_to_one, :name => :parent, :class_name => 'Node',
658
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true
659
+ }]
660
+ @c1.associate :one_to_many, :children, :class => @c1
661
+ @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.should == [{
662
+ :type => :one_to_many, :name => :children, :class_name => 'Node',
663
+ :class => @c1, :key => :node_id, :block => nil, :cache => true}, {
664
+ :type => :many_to_one, :name => :parent, :class_name => 'Node',
665
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true}]
666
+ end
667
+ end
668
+
669
+ describe Sequel::Model, "association_reflection" do
670
+ before(:each) do
671
+ MODEL_DB.reset
672
+ @c1 = Class.new(Sequel::Model(:nodes)) do
673
+ def self.name; 'Node'; end
674
+ def self.to_s; 'Node'; end
675
+ end
676
+ end
677
+
678
+ it "should return nil for nonexistent association" do
679
+ @c1.association_reflection(:blah).should == nil
680
+ end
681
+
682
+ it "should return association reflection hash if association exists" do
683
+ @c1.associate :many_to_one, :parent, :class => @c1
684
+ @c1.association_reflection(:parent).should == {
685
+ :type => :many_to_one, :name => :parent, :class_name => 'Node',
686
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true
687
+ }
688
+ @c1.associate :one_to_many, :children, :class => @c1
689
+ @c1.association_reflection(:children).should == {
690
+ :type => :one_to_many, :name => :children, :class_name => 'Node',
691
+ :class => @c1, :key => :node_id, :block => nil, :cache => true
692
+ }
693
+ end
694
+ end
695
+
696
+ describe Sequel::Model, "associations" do
697
+ before(:each) do
698
+ MODEL_DB.reset
699
+ @c1 = Class.new(Sequel::Model(:nodes)) do
700
+ def self.name; 'Node'; end
701
+ def self.to_s; 'Node'; end
702
+ end
703
+ end
704
+
705
+ it "should include all association names" do
706
+ @c1.associations.should == []
707
+ @c1.associate :many_to_one, :parent, :class => @c1
708
+ @c1.associations.should == [:parent]
709
+ @c1.associate :one_to_many, :children, :class => @c1
710
+ @c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
711
+ end
712
+ end