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.
- data/CHANGELOG +127 -0
- data/COPYING +1 -0
- data/README +5 -4
- data/Rakefile +78 -25
- data/lib/sequel.rb +1 -2
- data/lib/sequel_model.rb +324 -0
- data/lib/sequel_model/associations.rb +351 -0
- data/lib/sequel_model/base.rb +120 -0
- data/lib/sequel_model/caching.rb +42 -0
- data/lib/sequel_model/eager_loading.rb +169 -0
- data/lib/sequel_model/hooks.rb +55 -0
- data/lib/sequel_model/plugins.rb +47 -0
- data/lib/sequel_model/pretty_table.rb +73 -0
- data/lib/sequel_model/record.rb +336 -0
- data/lib/sequel_model/schema.rb +48 -0
- data/lib/sequel_model/validations.rb +15 -0
- data/spec/associations_spec.rb +712 -0
- data/spec/base_spec.rb +239 -0
- data/spec/caching_spec.rb +150 -0
- data/spec/deprecated_relations_spec.rb +153 -0
- data/spec/eager_loading_spec.rb +260 -0
- data/spec/hooks_spec.rb +269 -0
- data/spec/model_spec.rb +543 -0
- data/spec/plugins_spec.rb +74 -0
- data/spec/rcov.opts +4 -0
- data/spec/record_spec.rb +593 -0
- data/spec/schema_spec.rb +69 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/validations_spec.rb +246 -0
- metadata +90 -56
@@ -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
|