sequel 1.5.1 → 2.0.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.
@@ -1,50 +1,43 @@
1
1
  module Sequel
2
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
3
  # Creates table.
33
4
  def self.create_table
34
5
  db.create_table_sql_list(table_name, *schema.create_info).each {|s| db << s}
35
- @columns = nil
6
+ @db_schema = get_db_schema unless @@lazy_load_schema
36
7
  columns
37
8
  end
38
9
 
10
+ # Drops the table if it exists and then runs create_table.
11
+ def self.create_table!
12
+ drop_table if table_exists?
13
+ create_table
14
+ end
15
+
39
16
  # Drops table.
40
17
  def self.drop_table
41
18
  db.execute db.drop_table_sql(table_name)
42
19
  end
20
+
21
+ # Returns table schema created with set_schema for direct descendant of Model.
22
+ # Does not retreive schema information from the database, see db_schema if you
23
+ # want that.
24
+ def self.schema
25
+ @schema || ((superclass != Model) && (superclass.schema))
26
+ end
27
+
28
+ # Defines a table schema (see Schema::Generator for more information).
29
+ #
30
+ # This is only needed if you want to use the create_table or drop_table
31
+ # methods.
32
+ def self.set_schema(name = nil, &block)
33
+ set_dataset(db[name]) if name
34
+ @schema = Schema::Generator.new(db, &block)
35
+ set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
36
+ end
43
37
 
44
- # Like create_table but invokes drop_table when table_exists? is true.
45
- def self.create_table!
46
- drop_table if table_exists?
47
- create_table
38
+ # Returns true if table exists, false otherwise.
39
+ def self.table_exists?
40
+ db.table_exists?(table_name)
48
41
  end
49
42
  end
50
43
  end
@@ -1,17 +1,3 @@
1
- class Array
2
- # Removes and returns the last member of the array if it is a hash. Otherwise,
3
- # an empty hash is returned This method is useful when writing methods that
4
- # take an options hash as the last parameter. For example:
5
- #
6
- # def validate_each(*args, &block)
7
- # opts = args.extract_options!
8
- # ...
9
- # end
10
- def extract_options!
11
- last.is_a?(Hash) ? pop : {}
12
- end
13
- end
14
-
15
1
  # The Validations module provides validation capabilities as a mixin. When
16
2
  # included into a class, it enhances the class with class and instance
17
3
  # methods for defining validations and validating class instances.
@@ -41,12 +27,15 @@ module Validation
41
27
  # Validates the object.
42
28
  def validate
43
29
  errors.clear
30
+ return false if before_validation == false
44
31
  self.class.validate(self)
32
+ after_validation
33
+ nil
45
34
  end
46
35
 
47
36
  # Validates the object and returns true if no errors are reported.
48
37
  def valid?
49
- validate
38
+ return false if validate == false
50
39
  errors.empty?
51
40
  end
52
41
 
@@ -57,9 +46,9 @@ module Validation
57
46
  @errors = Hash.new {|h, k| h[k] = []}
58
47
  end
59
48
 
60
- # Returns true if no errors are stored.
61
- def empty?
62
- @errors.empty?
49
+ # Adds an error for the given attribute.
50
+ def add(att, msg)
51
+ @errors[att] << msg
63
52
  end
64
53
 
65
54
  # Clears all errors.
@@ -67,25 +56,14 @@ module Validation
67
56
  @errors.clear
68
57
  end
69
58
 
70
- # Returns size of errors array
71
- def size
72
- @errors.size
73
- end
74
-
75
59
  # Iterates over errors
76
60
  def each(&block)
77
61
  @errors.each(&block)
78
62
  end
79
63
 
80
- # Returns the errors for the given attribute.
81
- def on(att)
82
- @errors[att]
83
- end
84
- alias_method :[], :on
85
-
86
- # Adds an error for the given attribute.
87
- def add(att, msg)
88
- @errors[att] << msg
64
+ # Returns true if no errors are stored.
65
+ def empty?
66
+ @errors.empty?
89
67
  end
90
68
 
91
69
  # Returns an array of fully-formatted error messages.
@@ -95,6 +73,17 @@ module Validation
95
73
  m
96
74
  end
97
75
  end
76
+
77
+ # Returns the errors for the given attribute.
78
+ def on(att)
79
+ @errors[att]
80
+ end
81
+ alias_method :[], :on
82
+
83
+ # Returns size of errors array
84
+ def size
85
+ @errors.size
86
+ end
98
87
  end
99
88
 
100
89
  # The Generator class is used to generate validation definitions using
@@ -114,6 +103,16 @@ module Validation
114
103
 
115
104
  # Validation class methods.
116
105
  module ClassMethods
106
+ # Returns true if validations are defined.
107
+ def has_validations?
108
+ !validations.empty?
109
+ end
110
+
111
+ # Instructs the model to skip validations defined in superclasses
112
+ def skip_superclass_validations
113
+ @skip_superclass_validations = true
114
+ end
115
+
117
116
  # Defines validations by converting a longhand block into a series of
118
117
  # shorthand definitions. For example:
119
118
  #
@@ -135,16 +134,6 @@ module Validation
135
134
  Generator.new(self, &block)
136
135
  end
137
136
 
138
- # Returns the validations hash for the class.
139
- def validations
140
- @validations ||= Hash.new {|h, k| h[k] = []}
141
- end
142
-
143
- # Returns true if validations are defined.
144
- def has_validations?
145
- !validations.empty?
146
- end
147
-
148
137
  # Validates the given instance.
149
138
  def validate(o)
150
139
  if superclass.respond_to?(:validate) && !@skip_superclass_validations
@@ -156,21 +145,6 @@ module Validation
156
145
  end
157
146
  end
158
147
 
159
- def skip_superclass_validations
160
- @skip_superclass_validations = true
161
- end
162
-
163
- # Adds a validation for each of the given attributes using the supplied
164
- # block. The block must accept three arguments: instance, attribute and
165
- # value, e.g.:
166
- #
167
- # validates_each :name, :password do |object, attribute, value|
168
- # object.errors[attribute] << 'is not nice' unless value.nice?
169
- # end
170
- def validates_each(*atts, &block)
171
- atts.each {|a| validations[a] << block}
172
- end
173
-
174
148
  # Validates acceptance of an attribute.
175
149
  def validates_acceptance_of(*atts)
176
150
  opts = {
@@ -198,6 +172,17 @@ module Validation
198
172
  end
199
173
  end
200
174
 
175
+ # Adds a validation for each of the given attributes using the supplied
176
+ # block. The block must accept three arguments: instance, attribute and
177
+ # value, e.g.:
178
+ #
179
+ # validates_each :name, :password do |object, attribute, value|
180
+ # object.errors[attribute] << 'is not nice' unless value.nice?
181
+ # end
182
+ def validates_each(*atts, &block)
183
+ atts.each {|a| validations[a] << block}
184
+ end
185
+
201
186
  # Validates the format of an attribute.
202
187
  def validates_format_of(*atts)
203
188
  opts = {
@@ -239,20 +224,23 @@ module Validation
239
224
  end
240
225
  end
241
226
 
242
- NUMBER_RE = /^\d*\.{0,1}\d+$/
243
- INTEGER_RE = /\A[+-]?\d+\Z/
244
-
245
227
  # Validates whether an attribute is a number.
246
228
  def validates_numericality_of(*atts)
247
229
  opts = {
248
230
  :message => 'is not a number',
249
231
  }.merge!(atts.extract_options!)
250
232
 
251
- re = opts[:only_integer] ? INTEGER_RE : NUMBER_RE
252
-
253
233
  validates_each(*atts) do |o, a, v|
254
234
  next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
255
- o.errors[a] << opts[:message] unless v.to_s =~ re
235
+ begin
236
+ if opts[:only_integer]
237
+ Kernel.Integer(v.to_s)
238
+ else
239
+ Kernel.Float(v.to_s)
240
+ end
241
+ rescue
242
+ o.errors[a] << opts[:message]
243
+ end
256
244
  end
257
245
  end
258
246
 
@@ -297,17 +285,10 @@ module Validation
297
285
  o.errors[a] << opts[:message] unless allow
298
286
  end
299
287
  end
300
- end
301
- end
302
288
 
303
- module Sequel
304
- class Model
305
- include Validation
306
-
307
- alias_method :save!, :save
308
- def save(*args)
309
- return false unless valid?
310
- save!(*args)
289
+ # Returns the validations hash for the class.
290
+ def validations
291
+ @validations ||= Hash.new {|h, k| h[k] = []}
311
292
  end
312
293
  end
313
294
  end
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model::Associations::AssociationReflection, "#associated_class" do
4
+ before do
5
+ @c = Class.new(Sequel::Model)
6
+ class ParParent < Sequel::Model; end
7
+ end
8
+
9
+ it "should use the :class value if present" do
10
+ @c.many_to_one :c, :class=>ParParent
11
+ @c.association_reflection(:c).should include(:class)
12
+ @c.association_reflection(:c).associated_class.should == ParParent
13
+ end
14
+ it "should figure out the class if the :class value is not present" do
15
+ @c.many_to_one :c, :class=>'ParParent'
16
+ @c.association_reflection(:c).should_not include(:class)
17
+ @c.association_reflection(:c).associated_class.should == ParParent
18
+ end
19
+ end
20
+
21
+ describe Sequel::Model::Associations::AssociationReflection, "#associated_primary_key" do
22
+ before do
23
+ @c = Class.new(Sequel::Model)
24
+ class ParParent < Sequel::Model; end
25
+ end
26
+
27
+ it "should use the :right_primary_key value if present" do
28
+ @c.many_to_one :c, :class=>ParParent, :associated_primary_key=>:blah__blah
29
+ @c.association_reflection(:c).should include(:associated_primary_key)
30
+ @c.association_reflection(:c).associated_primary_key.should == :blah__blah
31
+ end
32
+ it "should use the associated table's primary key if :associated_primary_key is not present" do
33
+ @c.many_to_one :c, :class=>'ParParent'
34
+ @c.association_reflection(:c).should_not include(:associated_primary_key)
35
+ @c.association_reflection(:c).associated_primary_key.should == :id
36
+ end
37
+ end
38
+
39
+ describe Sequel::Model::Associations::AssociationReflection, "#reciprocal" do
40
+ it "should use the :reciprocal value if present" do
41
+ @c = Class.new(Sequel::Model)
42
+ @d = Class.new(Sequel::Model)
43
+ @c.many_to_one :c, :class=>@d, :reciprocal=>:@xx
44
+ @c.association_reflection(:c).should include(:reciprocal)
45
+ @c.association_reflection(:c).reciprocal.should == :@xx
46
+ end
47
+
48
+ it "should figure out the reciprocal if the :reciprocal value is not present" do
49
+ class ParParent < Sequel::Model; end
50
+ class ParParentTwo < Sequel::Model; end
51
+ class ParParentThree < Sequel::Model; end
52
+ ParParent.many_to_one :par_parent_two
53
+ ParParentTwo.one_to_many :par_parents
54
+ ParParent.many_to_many :par_parent_threes
55
+ ParParentThree.many_to_many :par_parents
56
+
57
+ ParParent.association_reflection(:par_parent_two).should_not include(:reciprocal)
58
+ ParParent.association_reflection(:par_parent_two).reciprocal.should == :@par_parents
59
+ ParParentTwo.association_reflection(:par_parents).should_not include(:reciprocal)
60
+ ParParentTwo.association_reflection(:par_parents).reciprocal.should == :@par_parent_two
61
+ ParParent.association_reflection(:par_parent_threes).should_not include(:reciprocal)
62
+ ParParent.association_reflection(:par_parent_threes).reciprocal.should == :@par_parents
63
+ ParParentThree.association_reflection(:par_parents).should_not include(:reciprocal)
64
+ ParParentThree.association_reflection(:par_parents).reciprocal.should == :@par_parent_threes
65
+ end
66
+ end
67
+
68
+ describe Sequel::Model::Associations::AssociationReflection, "#select" do
69
+ before do
70
+ @c = Class.new(Sequel::Model)
71
+ class ParParent < Sequel::Model; end
72
+ end
73
+
74
+ it "should use the :select value if present" do
75
+ @c.many_to_one :c, :class=>ParParent, :select=>[:par_parents__id]
76
+ @c.association_reflection(:c).should include(:select)
77
+ @c.association_reflection(:c).select.should == [:par_parents__id]
78
+ end
79
+ it "should use the associated_table.* if :select is not present" do
80
+ @c.many_to_one :c, :class=>'ParParent'
81
+ @c.association_reflection(:c).should_not include(:select)
82
+ @c.association_reflection(:c).select.should == :par_parents.*
83
+ end
84
+ end
85
+
@@ -11,9 +11,9 @@ describe Sequel::Model, "associate" do
11
11
  klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
12
12
  klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
13
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
14
+ klass.association_reflection(:"par_parent0").associated_class.should == ParParent
15
+ klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
16
+ klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
17
17
  end
18
18
  end
19
19
 
@@ -42,7 +42,7 @@ describe Sequel::Model, "many_to_one" do
42
42
  p.class.should == @c2
43
43
  p.values.should == {:x => 1, :id => 1}
44
44
 
45
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
45
+ MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
46
46
  end
47
47
 
48
48
  it "should use implicit class if omitted" do
@@ -55,7 +55,7 @@ describe Sequel::Model, "many_to_one" do
55
55
  p = d.par_parent
56
56
  p.class.should == ParParent
57
57
 
58
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (id = 234) LIMIT 1"]
58
+ MODEL_DB.sqls.should == ["SELECT par_parents.* FROM par_parents WHERE (id = 234) LIMIT 1"]
59
59
  end
60
60
 
61
61
  it "should use class inside module if given as a string" do
@@ -70,7 +70,7 @@ describe Sequel::Model, "many_to_one" do
70
70
  p = d.par_parent
71
71
  p.class.should == Par::Parent
72
72
 
73
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (id = 234) LIMIT 1"]
73
+ MODEL_DB.sqls.should == ["SELECT parents.* FROM parents WHERE (id = 234) LIMIT 1"]
74
74
  end
75
75
 
76
76
  it "should use explicit key if given" do
@@ -81,7 +81,13 @@ describe Sequel::Model, "many_to_one" do
81
81
  p.class.should == @c2
82
82
  p.values.should == {:x => 1, :id => 1}
83
83
 
84
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
84
+ MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 567) LIMIT 1"]
85
+ end
86
+
87
+ it "should use :select option if given" do
88
+ @c2.many_to_one :parent, :class => @c2, :key => :blah, :select=>[:id, :name]
89
+ @c2.new(:id => 1, :blah => 567).parent
90
+ MODEL_DB.sqls.should == ["SELECT id, name FROM nodes WHERE (id = 567) LIMIT 1"]
85
91
  end
86
92
 
87
93
  it "should return nil if key value is nil" do
@@ -101,9 +107,9 @@ describe Sequel::Model, "many_to_one" do
101
107
  d = @c2.new(:id => 1, :parent_id=>555)
102
108
  MODEL_DB.sqls.should == []
103
109
  d.parent.should == nil
104
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (id = 555) LIMIT 1']
110
+ MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (id = 555) LIMIT 1']
105
111
  d.parent.should == nil
106
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (id = 555) LIMIT 1']
112
+ MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (id = 555) LIMIT 1']
107
113
  end
108
114
 
109
115
  it "should define a setter method" do
@@ -142,7 +148,7 @@ describe Sequel::Model, "many_to_one" do
142
148
  ds = @c2.dataset
143
149
  def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
144
150
  e = d.parent
145
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
151
+ MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
146
152
  d.instance_variable_get("@parent").should == e
147
153
  end
148
154
 
@@ -176,7 +182,7 @@ describe Sequel::Model, "many_to_one" do
176
182
  d.parent_id = 234
177
183
  d.instance_variable_set(:@parent, 42)
178
184
  d.parent(true).should_not == 42
179
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
185
+ MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
180
186
  end
181
187
 
182
188
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
@@ -192,16 +198,16 @@ describe Sequel::Model, "many_to_one" do
192
198
  MODEL_DB.sqls.should == []
193
199
  d.parent = e
194
200
  e.children.should_not(include(d))
195
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (parent_id = 2)']
201
+ MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
196
202
 
197
203
  MODEL_DB.reset
198
204
  d = @c2.new(:id => 1)
199
205
  e = @c2.new(:id => 2)
200
206
  e.children.should_not(include(d))
201
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (parent_id = 2)']
207
+ MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
202
208
  d.parent = e
203
209
  e.children.should(include(d))
204
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (parent_id = 2)']
210
+ MODEL_DB.sqls.should == ['SELECT nodes.* FROM nodes WHERE (parent_id = 2)']
205
211
  end
206
212
 
207
213
  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
@@ -238,7 +244,7 @@ describe Sequel::Model, "many_to_one" do
238
244
  ds = @c2.dataset
239
245
  def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
240
246
  e = d.parent
241
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
247
+ MODEL_DB.sqls.should == ["SELECT nodes.* FROM nodes WHERE (id = 234) LIMIT 1"]
242
248
  d.instance_variable_get("@parent").should == e
243
249
  end
244
250
  end
@@ -282,7 +288,7 @@ describe Sequel::Model, "one_to_many" do
282
288
  n = @c2.new(:id => 1234)
283
289
  a = n.attributes_dataset
284
290
  a.should be_a_kind_of(Sequel::Dataset)
285
- a.sql.should == 'SELECT * FROM attributes WHERE (node_id = 1234)'
291
+ a.sql.should == 'SELECT attributes.* FROM attributes WHERE (node_id = 1234)'
286
292
  end
287
293
 
288
294
  it "should use implicit class if omitted" do
@@ -294,7 +300,7 @@ describe Sequel::Model, "one_to_many" do
294
300
  n = @c2.new(:id => 1234)
295
301
  v = n.historical_values_dataset
296
302
  v.should be_a_kind_of(Sequel::Dataset)
297
- v.sql.should == 'SELECT * FROM historical_values WHERE (node_id = 1234)'
303
+ v.sql.should == 'SELECT historical_values.* FROM historical_values WHERE (node_id = 1234)'
298
304
  v.model_classes.should == {nil => HistoricalValue}
299
305
  end
300
306
 
@@ -309,7 +315,7 @@ describe Sequel::Model, "one_to_many" do
309
315
  n = @c2.new(:id => 1234)
310
316
  v = n.historical_values_dataset
311
317
  v.should be_a_kind_of(Sequel::Dataset)
312
- v.sql.should == 'SELECT * FROM values WHERE (node_id = 1234)'
318
+ v.sql.should == 'SELECT values.* FROM values WHERE (node_id = 1234)'
313
319
  v.model_classes.should == {nil => Historical::Value}
314
320
  end
315
321
 
@@ -319,7 +325,7 @@ describe Sequel::Model, "one_to_many" do
319
325
  n = @c2.new(:id => 1234)
320
326
  a = n.attributes_dataset
321
327
  a.should be_a_kind_of(Sequel::Dataset)
322
- a.sql.should == 'SELECT * FROM attributes WHERE (nodeid = 1234)'
328
+ a.sql.should == 'SELECT attributes.* FROM attributes WHERE (nodeid = 1234)'
323
329
  end
324
330
 
325
331
  it "should define an add_ method" do
@@ -344,18 +350,25 @@ describe Sequel::Model, "one_to_many" do
344
350
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
345
351
  end
346
352
 
353
+ it "should support a select option" do
354
+ @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
355
+
356
+ n = @c2.new(:id => 1234)
357
+ n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (node_id = 1234)"
358
+ end
359
+
347
360
  it "should support an order option" do
348
361
  @c2.one_to_many :attributes, :class => @c1, :order => :kind
349
362
 
350
363
  n = @c2.new(:id => 1234)
351
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (node_id = 1234) ORDER BY kind"
364
+ n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes WHERE (node_id = 1234) ORDER BY kind"
352
365
  end
353
366
 
354
367
  it "should support an array for the order option" do
355
368
  @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
356
369
 
357
370
  n = @c2.new(:id => 1234)
358
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (node_id = 1234) ORDER BY kind1, kind2"
371
+ n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes WHERE (node_id = 1234) ORDER BY kind1, kind2"
359
372
  end
360
373
 
361
374
  it "should return array with all members of the association" do
@@ -368,7 +381,7 @@ describe Sequel::Model, "one_to_many" do
368
381
  atts.first.should be_a_kind_of(@c1)
369
382
  atts.first.values.should == {}
370
383
 
371
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
384
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
372
385
  end
373
386
 
374
387
  it "should accept a block" do
@@ -383,7 +396,7 @@ describe Sequel::Model, "one_to_many" do
383
396
  atts.first.should be_a_kind_of(@c1)
384
397
  atts.first.values.should == {}
385
398
 
386
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234) AND (xxx IS NULL)']
399
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE ((node_id = 1234) AND (xxx IS NULL))']
387
400
  end
388
401
 
389
402
  it "should support order option with block" do
@@ -398,7 +411,7 @@ describe Sequel::Model, "one_to_many" do
398
411
  atts.first.should be_a_kind_of(@c1)
399
412
  atts.first.values.should == {}
400
413
 
401
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234) AND (xxx IS NULL) ORDER BY kind']
414
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE ((node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
402
415
  end
403
416
 
404
417
  it "should set cached instance variable when accessed" do
@@ -409,7 +422,7 @@ describe Sequel::Model, "one_to_many" do
409
422
  n.instance_variables.include?("@attributes").should == false
410
423
  atts = n.attributes
411
424
  atts.should == n.instance_variable_get("@attributes")
412
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
425
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
413
426
  end
414
427
 
415
428
  it "should use cached instance variable if available" do
@@ -429,7 +442,7 @@ describe Sequel::Model, "one_to_many" do
429
442
  MODEL_DB.reset
430
443
  n.instance_variable_set(:@attributes, 42)
431
444
  n.attributes(true).should_not == 42
432
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
445
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
433
446
  end
434
447
 
435
448
  it "should add item to cached instance variable if it exists when calling add_" do
@@ -488,7 +501,7 @@ describe Sequel::Model, "one_to_many" do
488
501
  atts.first.should be_a_kind_of(@c1)
489
502
  atts.first.values.should == {}
490
503
 
491
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
504
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
492
505
  end
493
506
 
494
507
  it "should populate the reciprocal many_to_one instance variable when loading the one_to_many association" do
@@ -497,7 +510,7 @@ describe Sequel::Model, "one_to_many" do
497
510
 
498
511
  n = @c2.new(:id => 1234)
499
512
  atts = n.attributes
500
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
513
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
501
514
  atts.should be_a_kind_of(Array)
502
515
  atts.size.should == 1
503
516
  atts.first.should be_a_kind_of(@c1)
@@ -512,7 +525,7 @@ describe Sequel::Model, "one_to_many" do
512
525
 
513
526
  n = @c2.new(:id => 1234)
514
527
  atts = n.attributes
515
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (node_id = 1234)']
528
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes WHERE (node_id = 1234)']
516
529
  atts.should be_a_kind_of(Array)
517
530
  atts.size.should == 1
518
531
  atts.first.should be_a_kind_of(@c1)
@@ -521,6 +534,55 @@ describe Sequel::Model, "one_to_many" do
521
534
 
522
535
  MODEL_DB.sqls.length.should == 1
523
536
  end
537
+
538
+ it "should have an remove_all_ method that removes all associations" do
539
+ @c2.one_to_many :attributes, :class => @c1
540
+ @c2.new(:id => 1234).remove_all_attributes
541
+ MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
542
+ end
543
+
544
+ it "remove_all should set the cached instance variable to []" do
545
+ @c2.one_to_many :attributes, :class => @c1
546
+ node = @c2.new(:id => 1234)
547
+ node.remove_all_attributes
548
+ node.instance_variable_get(:@attributes).should == []
549
+ end
550
+
551
+ it "remove_all should return the array of previously associated items if the cached instance variable exists" do
552
+ @c2.one_to_many :attributes, :class => @c1
553
+ attrib = @c1.new(:id=>3)
554
+ node = @c2.new(:id => 1234)
555
+ d = @c1.dataset
556
+ def d.fetch_rows(s); end
557
+ node.attributes.should == []
558
+ def attrib.save!; end
559
+ node.add_attribute(attrib)
560
+ node.instance_variable_get(:@attributes).should == [attrib]
561
+ node.remove_all_attributes.should == [attrib]
562
+ end
563
+
564
+ it "remove_all should return nil if the cached instance variable does not exist" do
565
+ @c2.one_to_many :attributes, :class => @c1
566
+ @c2.new(:id => 1234).remove_all_attributes.should == nil
567
+ end
568
+
569
+ it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
570
+ @c2.one_to_many :attributes, :class => @c1
571
+ @c1.many_to_one :node, :class => @c2
572
+ d = @c1.dataset
573
+ def d.fetch_rows(s); end
574
+ d = @c2.dataset
575
+ def d.fetch_rows(s); end
576
+ attrib = @c1.new(:id=>3)
577
+ node = @c2.new(:id => 1234)
578
+ node.attributes.should == []
579
+ attrib.node.should == nil
580
+ def attrib.save!; end
581
+ node.add_attribute(attrib)
582
+ attrib.instance_variable_get(:@node).should == node
583
+ node.remove_all_attributes
584
+ attrib.instance_variable_get(:@node).should == :null
585
+ end
524
586
  end
525
587
 
526
588
  describe Sequel::Model, "many_to_many" do
@@ -559,9 +621,7 @@ describe Sequel::Model, "many_to_many" do
559
621
  n = @c2.new(:id => 1234)
560
622
  a = n.attributes_dataset
561
623
  a.should be_a_kind_of(Sequel::Dataset)
562
- ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
563
- 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
564
- ].should(include(a.sql))
624
+ a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
565
625
  end
566
626
 
567
627
  it "should use implicit class if omitted" do
@@ -573,9 +633,7 @@ describe Sequel::Model, "many_to_many" do
573
633
  n = @c2.new(:id => 1234)
574
634
  a = n.tags_dataset
575
635
  a.should be_a_kind_of(Sequel::Dataset)
576
- ['SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234)',
577
- 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.node_id = 1234) AND (nodes_tags.tag_id = tags.id)'
578
- ].should(include(a.sql))
636
+ a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
579
637
  end
580
638
 
581
639
  it "should use class inside module if given as a string" do
@@ -589,9 +647,7 @@ describe Sequel::Model, "many_to_many" do
589
647
  n = @c2.new(:id => 1234)
590
648
  a = n.tags_dataset
591
649
  a.should be_a_kind_of(Sequel::Dataset)
592
- ['SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234)',
593
- 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.node_id = 1234) AND (nodes_tags.tag_id = tags.id)'
594
- ].should(include(a.sql))
650
+ a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
595
651
  end
596
652
 
597
653
  it "should use explicit key values and join table if given" do
@@ -600,9 +656,7 @@ describe Sequel::Model, "many_to_many" do
600
656
  n = @c2.new(:id => 1234)
601
657
  a = n.attributes_dataset
602
658
  a.should be_a_kind_of(Sequel::Dataset)
603
- ['SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.nodeid = 1234) AND (attribute2node.attributeid = attributes.id)',
604
- 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234)'
605
- ].should(include(a.sql))
659
+ a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
606
660
  end
607
661
 
608
662
  it "should support an order option" do
@@ -611,9 +665,7 @@ describe Sequel::Model, "many_to_many" do
611
665
  n = @c2.new(:id => 1234)
612
666
  a = n.attributes_dataset
613
667
  a.should be_a_kind_of(Sequel::Dataset)
614
- ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) ORDER BY blah',
615
- 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) ORDER BY blah'
616
- ].should(include(a.sql))
668
+ a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
617
669
  end
618
670
 
619
671
  it "should support an array for the order option" do
@@ -622,9 +674,7 @@ describe Sequel::Model, "many_to_many" do
622
674
  n = @c2.new(:id => 1234)
623
675
  a = n.attributes_dataset
624
676
  a.should be_a_kind_of(Sequel::Dataset)
625
- ['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',
626
- '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'
627
- ].should(include(a.sql))
677
+ a.sql.should == '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'
628
678
  end
629
679
 
630
680
  it "should support a select option" do
@@ -633,9 +683,7 @@ describe Sequel::Model, "many_to_many" do
633
683
  n = @c2.new(:id => 1234)
634
684
  a = n.attributes_dataset
635
685
  a.should be_a_kind_of(Sequel::Dataset)
636
- ['SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
637
- 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
638
- ].should(include(a.sql))
686
+ a.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
639
687
  end
640
688
 
641
689
  it "should support an array for the select option" do
@@ -644,9 +692,7 @@ describe Sequel::Model, "many_to_many" do
644
692
  n = @c2.new(:id => 1234)
645
693
  a = n.attributes_dataset
646
694
  a.should be_a_kind_of(Sequel::Dataset)
647
- ['SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
648
- 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
649
- ].should(include(a.sql))
695
+ a.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
650
696
  end
651
697
 
652
698
  it "should accept a block" do
@@ -660,9 +706,7 @@ describe Sequel::Model, "many_to_many" do
660
706
  a.should be_a_kind_of(Array)
661
707
  a.size.should == 1
662
708
  a.first.should be_a_kind_of(@c1)
663
- ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234) WHERE (xxx = 555)',
664
- 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id) WHERE (xxx = 555)'
665
- ].should(include(MODEL_DB.sqls.first))
709
+ 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)'
666
710
  end
667
711
 
668
712
  it "should allow the order option while accepting a block" do
@@ -676,9 +720,7 @@ describe Sequel::Model, "many_to_many" do
676
720
  a.should be_a_kind_of(Array)
677
721
  a.size.should == 1
678
722
  a.first.should be_a_kind_of(@c1)
679
- ['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',
680
- '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'
681
- ].should(include(MODEL_DB.sqls.first))
723
+ 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) ORDER BY blah1, blah2'
682
724
  end
683
725
 
684
726
  it "should define an add_ method" do
@@ -698,9 +740,7 @@ describe Sequel::Model, "many_to_many" do
698
740
  n = @c2.new(:id => 1234)
699
741
  a = @c1.new(:id => 2345)
700
742
  a.should == n.remove_attribute(a)
701
- ['DELETE FROM attributes_nodes WHERE (node_id = 1234) AND (attribute_id = 2345)',
702
- 'DELETE FROM attributes_nodes WHERE (attribute_id = 2345) AND (node_id = 1234)'
703
- ].should(include(MODEL_DB.sqls.first))
743
+ MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
704
744
  end
705
745
 
706
746
  it "should provide an array with all members of the association" do
@@ -712,9 +752,7 @@ describe Sequel::Model, "many_to_many" do
712
752
  atts.size.should == 1
713
753
  atts.first.should be_a_kind_of(@c1)
714
754
 
715
- ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
716
- 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
717
- ].should(include(MODEL_DB.sqls.first))
755
+ 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))'
718
756
  end
719
757
 
720
758
  it "should set cached instance variable when accessed" do
@@ -800,11 +838,55 @@ describe Sequel::Model, "many_to_many" do
800
838
  n = @c2.new(:id => 1234)
801
839
  a = n.attributes_dataset
802
840
  a.should be_a_kind_of(Sequel::Dataset)
803
- ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)',
804
- 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.node_id = 1234) AND (attributes_nodes.attribute_id = attributes.id)'
805
- ].should(include(a.sql))
841
+ a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
806
842
  end
807
843
 
844
+ it "should have an remove_all_ method that removes all associations" do
845
+ @c2.many_to_many :attributes, :class => @c1
846
+ @c2.new(:id => 1234).remove_all_attributes
847
+ MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
848
+ end
849
+
850
+ it "remove_all should set the cached instance variable to []" do
851
+ @c2.many_to_many :attributes, :class => @c1
852
+ node = @c2.new(:id => 1234)
853
+ node.remove_all_attributes
854
+ node.instance_variable_get(:@attributes).should == []
855
+ end
856
+
857
+ it "remove_all should return the array of previously associated items if the cached instance variable exists" do
858
+ @c2.many_to_many :attributes, :class => @c1
859
+ attrib = @c1.new(:id=>3)
860
+ node = @c2.new(:id => 1234)
861
+ d = @c1.dataset
862
+ def d.fetch_rows(s); end
863
+ node.attributes.should == []
864
+ node.add_attribute(attrib)
865
+ node.instance_variable_get(:@attributes).should == [attrib]
866
+ node.remove_all_attributes.should == [attrib]
867
+ end
868
+
869
+ it "remove_all should return nil if the cached instance variable does not exist" do
870
+ @c2.many_to_many :attributes, :class => @c1
871
+ @c2.new(:id => 1234).remove_all_attributes.should == nil
872
+ end
873
+
874
+ it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
875
+ @c2.many_to_many :attributes, :class => @c1
876
+ @c1.many_to_many :nodes, :class => @c2
877
+ d = @c1.dataset
878
+ def d.fetch_rows(s); end
879
+ d = @c2.dataset
880
+ def d.fetch_rows(s); end
881
+ attrib = @c1.new(:id=>3)
882
+ node = @c2.new(:id => 1234)
883
+ node.attributes.should == []
884
+ attrib.nodes.should == []
885
+ node.add_attribute(attrib)
886
+ attrib.instance_variable_get(:@nodes).should == [node]
887
+ node.remove_all_attributes
888
+ attrib.instance_variable_get(:@nodes).should == []
889
+ end
808
890
  end
809
891
 
810
892
  describe Sequel::Model, "all_association_reflections" do
@@ -821,14 +903,17 @@ describe Sequel::Model, "all_association_reflections" do
821
903
  @c1.associate :many_to_one, :parent, :class => @c1
822
904
  @c1.all_association_reflections.should == [{
823
905
  :type => :many_to_one, :name => :parent, :class_name => 'Node',
824
- :class => @c1, :key => :parent_id, :block => nil, :cache => true
906
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true,
907
+ :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
825
908
  }]
826
909
  @c1.associate :one_to_many, :children, :class => @c1
827
910
  @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.should == [{
828
911
  :type => :one_to_many, :name => :children, :class_name => 'Node',
829
- :class => @c1, :key => :node_id, :block => nil, :cache => true}, {
912
+ :class => @c1, :key => :node_id, :block => nil, :cache => true,
913
+ :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1}, {
830
914
  :type => :many_to_one, :name => :parent, :class_name => 'Node',
831
- :class => @c1, :key => :parent_id, :block => nil, :cache => true}]
915
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true,
916
+ :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1}]
832
917
  end
833
918
  end
834
919
 
@@ -849,12 +934,14 @@ describe Sequel::Model, "association_reflection" do
849
934
  @c1.associate :many_to_one, :parent, :class => @c1
850
935
  @c1.association_reflection(:parent).should == {
851
936
  :type => :many_to_one, :name => :parent, :class_name => 'Node',
852
- :class => @c1, :key => :parent_id, :block => nil, :cache => true
937
+ :class => @c1, :key => :parent_id, :block => nil, :cache => true,
938
+ :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
853
939
  }
854
940
  @c1.associate :one_to_many, :children, :class => @c1
855
941
  @c1.association_reflection(:children).should == {
856
942
  :type => :one_to_many, :name => :children, :class_name => 'Node',
857
- :class => @c1, :key => :node_id, :block => nil, :cache => true
943
+ :class => @c1, :key => :node_id, :block => nil, :cache => true,
944
+ :graph_join_type=>:left_outer, :graph_conditions=>[], :eager_block => nil, :model => @c1
858
945
  }
859
946
  end
860
947
  end