sequel_model 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ module Sequel::Plugins
4
+
5
+ module Timestamped
6
+ def self.apply(m, opts)
7
+ m.class_def(:get_stamp) {@values[:stamp]}
8
+ m.meta_def(:stamp_opts) {opts}
9
+ m.before_save {@values[:stamp] = Time.now}
10
+ end
11
+
12
+ module InstanceMethods
13
+ def abc; timestamped_opts; end
14
+ end
15
+
16
+ module ClassMethods
17
+ def deff; timestamped_opts; end
18
+ end
19
+
20
+ # ??
21
+ # module DatasetMethods
22
+ # def ghi; timestamped_opts; end
23
+ # end
24
+ end
25
+
26
+ end
27
+
28
+ describe Sequel::Model, "using a plugin" do
29
+
30
+ it "should fail if the plugin is not found" do
31
+ proc do
32
+ c = Class.new(Sequel::Model) do
33
+ is :something_or_other
34
+ end
35
+ end.should raise_error(LoadError)
36
+ end
37
+
38
+ it "should apply the plugin to the class" do
39
+ c = nil
40
+ proc do
41
+ c = Class.new(Sequel::Model) do
42
+ is :timestamped, :a => 1, :b => 2
43
+ end
44
+ end.should_not raise_error(LoadError)
45
+
46
+ c.should respond_to(:stamp_opts)
47
+ c.stamp_opts.should == {:a => 1, :b => 2}
48
+
49
+ m = c.new
50
+ m.should respond_to(:get_stamp)
51
+ m.should respond_to(:abc)
52
+ m.abc.should == {:a => 1, :b => 2}
53
+ t = Time.now
54
+ m[:stamp] = t
55
+ m.get_stamp.should == t
56
+
57
+ c.should respond_to(:deff)
58
+ c.deff.should == {:a => 1, :b => 2}
59
+ end
60
+
61
+ end
@@ -0,0 +1,4 @@
1
+ --exclude
2
+ gems
3
+ --exclude
4
+ spec
@@ -0,0 +1,362 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Model#save" do
4
+
5
+ before(:each) do
6
+ MODEL_DB.reset
7
+
8
+ @c = Class.new(Sequel::Model(:items)) do
9
+ def columns
10
+ [:id, :x, :y]
11
+ end
12
+ end
13
+ end
14
+
15
+ it "should insert a record for a new model instance" do
16
+ o = @c.new(:x => 1)
17
+ o.save
18
+
19
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
20
+ end
21
+
22
+ it "should update a record for an existing model instance" do
23
+ o = @c.new(:id => 3, :x => 1)
24
+ o.save
25
+
26
+ MODEL_DB.sqls.first.should =~
27
+ /UPDATE items SET (id = 3, x = 1|x = 1, id = 3) WHERE \(id = 3\)/
28
+ end
29
+
30
+ it "should update only the given columns if given" do
31
+ o = @c.new(:id => 3, :x => 1, :y => nil)
32
+ o.save(:y)
33
+
34
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = NULL WHERE (id = 3)"
35
+ end
36
+
37
+ it "should mark saved columns as not changed" do
38
+ o = @c.new(:id => 3, :x => 1, :y => nil)
39
+ o[:y] = 4
40
+ o.changed_columns.should == [:y]
41
+ o.save(:x)
42
+ o.changed_columns.should == [:y]
43
+ o.save(:y)
44
+ o.changed_columns.should == []
45
+ end
46
+
47
+ end
48
+
49
+ describe "Model#save_changes" do
50
+
51
+ before(:each) do
52
+ MODEL_DB.reset
53
+
54
+ @c = Class.new(Sequel::Model(:items)) do
55
+ def columns
56
+ [:id, :x, :y]
57
+ end
58
+ end
59
+ end
60
+
61
+ it "should do nothing if no changed columns" do
62
+ o = @c.new(:id => 3, :x => 1, :y => nil)
63
+ o.save_changes
64
+
65
+ MODEL_DB.sqls.should be_empty
66
+ end
67
+
68
+ it "should update only changed columns" do
69
+ o = @c.new(:id => 3, :x => 1, :y => nil)
70
+ o.x = 2
71
+
72
+ o.save_changes
73
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
74
+ o.save_changes
75
+ o.save_changes
76
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
77
+ MODEL_DB.reset
78
+
79
+ o.y = 4
80
+ o.save_changes
81
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
82
+ o.save_changes
83
+ o.save_changes
84
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
85
+ end
86
+
87
+ end
88
+
89
+ describe "Model#set" do
90
+
91
+ before(:each) do
92
+ MODEL_DB.reset
93
+
94
+ @c = Class.new(Sequel::Model(:items)) do
95
+ def columns
96
+ [:id, :x, :y]
97
+ end
98
+ end
99
+ end
100
+
101
+ it "should generate an update statement" do
102
+ o = @c.new(:id => 1)
103
+ o.set(:x => 1)
104
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
105
+ end
106
+
107
+ it "should update attribute values" do
108
+ o = @c.new(:id => 1)
109
+ o.x.should be_nil
110
+ o.set(:x => 1)
111
+ o.x.should == 1
112
+ end
113
+
114
+ it "should be aliased by #update" do
115
+ o = @c.new(:id => 1)
116
+ o.update(:x => 1)
117
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
118
+ end
119
+ end
120
+
121
+
122
+ describe "Model#new?" do
123
+
124
+ before(:each) do
125
+ MODEL_DB.reset
126
+
127
+ @c = Class.new(Sequel::Model(:items)) do
128
+ end
129
+ end
130
+
131
+ it "should be true for a new instance" do
132
+ n = @c.new(:x => 1)
133
+ n.should be_new
134
+ end
135
+
136
+ it "should be false after saving" do
137
+ n = @c.new(:x => 1)
138
+ n.save
139
+ n.should_not be_new
140
+ end
141
+
142
+ it "should alias new_record? to new?" do
143
+ n = @c.new(:x => 1)
144
+ n.should respond_to(:new_record?)
145
+ n.should be_new_record
146
+ n.save
147
+ n.should_not be_new_record
148
+ end
149
+
150
+ end
151
+
152
+ describe Sequel::Model, "w/ primary key" do
153
+
154
+ it "should default to ':id'" do
155
+ model_a = Class.new Sequel::Model
156
+ model_a.primary_key.should be_equal(:id)
157
+ end
158
+
159
+ it "should be changed through 'set_primary_key'" do
160
+ model_a = Class.new(Sequel::Model) { set_primary_key :a }
161
+ model_a.primary_key.should be_equal(:a)
162
+ end
163
+
164
+ it "should support multi argument composite keys" do
165
+ model_a = Class.new(Sequel::Model) { set_primary_key :a, :b }
166
+ model_a.primary_key.should be_eql([:a, :b])
167
+ end
168
+
169
+ it "should accept single argument composite keys" do
170
+ model_a = Class.new(Sequel::Model) { set_primary_key [:a, :b] }
171
+ model_a.primary_key.should be_eql([:a, :b])
172
+ end
173
+
174
+ end
175
+
176
+ describe Sequel::Model, "w/o primary key" do
177
+ it "should return nil for primary key" do
178
+ Class.new(Sequel::Model) { no_primary_key }.primary_key.should be_nil
179
+ end
180
+
181
+ it "should raise a Sequel::Error on 'this'" do
182
+ instance = Class.new(Sequel::Model) { no_primary_key }.new
183
+ proc { instance.this }.should raise_error(Sequel::Error)
184
+ end
185
+ end
186
+
187
+ describe Sequel::Model, "with this" do
188
+
189
+ before { @example = Class.new Sequel::Model(:examples) }
190
+
191
+ it "should return a dataset identifying the record" do
192
+ instance = @example.new :id => 3
193
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (id = 3) LIMIT 1")
194
+ end
195
+
196
+ it "should support arbitary primary keys" do
197
+ @example.set_primary_key :a
198
+
199
+ instance = @example.new :a => 3
200
+ instance.this.sql.should be_eql("SELECT * FROM examples WHERE (a = 3) LIMIT 1")
201
+ end
202
+
203
+ it "should support composite primary keys" do
204
+ @example.set_primary_key :x, :y
205
+ instance = @example.new :x => 4, :y => 5
206
+
207
+ parts = [
208
+ 'SELECT * FROM examples WHERE %s LIMIT 1',
209
+ '(x = 4) AND (y = 5)',
210
+ '(y = 5) AND (x = 4)'
211
+ ].map { |expr| Regexp.escape expr }
212
+ regexp = Regexp.new parts.first % "(?:#{parts[1]}|#{parts[2]})"
213
+
214
+ instance.this.sql.should match(regexp)
215
+ end
216
+
217
+ end
218
+
219
+ describe "Model#pk" do
220
+
221
+ before(:each) do
222
+ @m = Class.new(Sequel::Model)
223
+ end
224
+
225
+ it "should be default return the value of the :id column" do
226
+ m = @m.new(:id => 111, :x => 2, :y => 3)
227
+ m.pk.should == 111
228
+ end
229
+
230
+ it "should be return the primary key value for custom primary key" do
231
+ @m.set_primary_key :x
232
+ m = @m.new(:id => 111, :x => 2, :y => 3)
233
+ m.pk.should == 2
234
+ end
235
+
236
+ it "should be return the primary key value for composite primary key" do
237
+ @m.set_primary_key [:y, :x]
238
+ m = @m.new(:id => 111, :x => 2, :y => 3)
239
+ m.pk.should == [3, 2]
240
+ end
241
+
242
+ it "should raise if no primary key" do
243
+ @m.set_primary_key nil
244
+ m = @m.new(:id => 111, :x => 2, :y => 3)
245
+ proc {m.pk}.should raise_error(Sequel::Error)
246
+
247
+ @m.no_primary_key
248
+ m = @m.new(:id => 111, :x => 2, :y => 3)
249
+ proc {m.pk}.should raise_error(Sequel::Error)
250
+ end
251
+
252
+ end
253
+
254
+ describe "Model#pk_hash" do
255
+
256
+ before(:each) do
257
+ @m = Class.new(Sequel::Model)
258
+ end
259
+
260
+ it "should be default return the value of the :id column" do
261
+ m = @m.new(:id => 111, :x => 2, :y => 3)
262
+ m.pk_hash.should == {:id => 111}
263
+ end
264
+
265
+ it "should be return the primary key value for custom primary key" do
266
+ @m.set_primary_key :x
267
+ m = @m.new(:id => 111, :x => 2, :y => 3)
268
+ m.pk_hash.should == {:x => 2}
269
+ end
270
+
271
+ it "should be return the primary key value for composite primary key" do
272
+ @m.set_primary_key [:y, :x]
273
+ m = @m.new(:id => 111, :x => 2, :y => 3)
274
+ m.pk_hash.should == {:y => 3, :x => 2}
275
+ end
276
+
277
+ it "should raise if no primary key" do
278
+ @m.set_primary_key nil
279
+ m = @m.new(:id => 111, :x => 2, :y => 3)
280
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
281
+
282
+ @m.no_primary_key
283
+ m = @m.new(:id => 111, :x => 2, :y => 3)
284
+ proc {m.pk_hash}.should raise_error(Sequel::Error)
285
+ end
286
+ end
287
+
288
+ describe Sequel::Model, "create_with_params" do
289
+
290
+ before(:each) do
291
+ MODEL_DB.reset
292
+
293
+ @c = Class.new(Sequel::Model(:items)) do
294
+ def self.columns; [:x, :y]; end
295
+ end
296
+ end
297
+
298
+ it "should filter the given params using the model columns" do
299
+ @c.create_with_params(:x => 1, :z => 2)
300
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
301
+
302
+ MODEL_DB.reset
303
+ @c.create_with_params(:y => 1, :abc => 2)
304
+ MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
305
+ end
306
+
307
+ it "should be aliased by create_with" do
308
+ @c.create_with(:x => 1, :z => 2)
309
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
310
+
311
+ MODEL_DB.reset
312
+ @c.create_with(:y => 1, :abc => 2)
313
+ MODEL_DB.sqls.first.should == "INSERT INTO items (y) VALUES (1)"
314
+ end
315
+
316
+ end
317
+
318
+ describe Sequel::Model, "#destroy" do
319
+
320
+ before(:each) do
321
+ MODEL_DB.reset
322
+ @model = Class.new(Sequel::Model(:items))
323
+ @model.dataset.meta_def(:delete) {MODEL_DB.execute delete_sql}
324
+
325
+ @instance = @model.new(:id => 1234)
326
+ #@model.stub!(:delete).and_return(:true)
327
+ end
328
+
329
+ it "should run within a transaction" do
330
+ @model.db.should_receive(:transaction)
331
+ @instance.destroy
332
+ end
333
+
334
+ it "should run before_destroy and after_destroy hooks" do
335
+ @model.before_destroy {MODEL_DB.execute('before blah')}
336
+ @model.after_destroy {MODEL_DB.execute('after blah')}
337
+ @instance.destroy
338
+
339
+ MODEL_DB.sqls.should == [
340
+ "before blah",
341
+ "DELETE FROM items WHERE (id = 1234)",
342
+ "after blah"
343
+ ]
344
+ end
345
+ end
346
+
347
+ describe Sequel::Model, "#exists?" do
348
+
349
+ before(:each) do
350
+ @model = Class.new(Sequel::Model(:items))
351
+ @model_a = @model.new
352
+ end
353
+
354
+ it "should returns true when current instance exists" do
355
+ @model_a.exists?.should be_true
356
+ end
357
+
358
+ it "should return false when the current instance does not exist" do
359
+ pending("how would this be false?")
360
+ end
361
+
362
+ end
@@ -0,0 +1,150 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "one_to_one" do
4
+
5
+ before(:each) do
6
+ MODEL_DB.reset
7
+
8
+ @c1 = Class.new(Sequel::Model(:attributes)) do
9
+ end
10
+
11
+ @c2 = Class.new(Sequel::Model(:nodes)) do
12
+ end
13
+
14
+ @dataset = @c2.dataset
15
+
16
+ $sqls = []
17
+ @dataset.extend(Module.new {
18
+ def fetch_rows(sql)
19
+ $sqls << sql
20
+ yield({:hey => 1})
21
+ end
22
+
23
+ def update(values)
24
+ $sqls << update_sql(values)
25
+ end
26
+ }
27
+ )
28
+ end
29
+
30
+ it "should use implicit key if omitted" do
31
+ @c2.one_to_one :parent, :from => @c2
32
+
33
+ d = @c2.new(:id => 1, :parent_id => 234)
34
+ p = d.parent
35
+ p.class.should == @c2
36
+ p.values.should == {:hey => 1}
37
+
38
+ $sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
39
+ end
40
+
41
+ it "should use explicit key if given" do
42
+ @c2.one_to_one :parent, :from => @c2, :key => :blah
43
+
44
+ d = @c2.new(:id => 1, :blah => 567)
45
+ p = d.parent
46
+ p.class.should == @c2
47
+ p.values.should == {:hey => 1}
48
+
49
+ $sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
50
+ end
51
+
52
+ it "should support plain dataset in the from option" do
53
+ @c2.one_to_one :parent, :from => MODEL_DB[:xyz]
54
+
55
+ d = @c2.new(:id => 1, :parent_id => 789)
56
+ p = d.parent
57
+ p.class.should == Hash
58
+
59
+ MODEL_DB.sqls.should == ["SELECT * FROM xyz WHERE (id = 789) LIMIT 1"]
60
+ end
61
+
62
+ it "should support table name in the from option" do
63
+ @c2.one_to_one :parent, :from => :abc
64
+
65
+ d = @c2.new(:id => 1, :parent_id => 789)
66
+ p = d.parent
67
+ p.class.should == Hash
68
+
69
+ MODEL_DB.sqls.should == ["SELECT * FROM abc WHERE (id = 789) LIMIT 1"]
70
+ end
71
+
72
+ it "should return nil if key value is nil" do
73
+ @c2.one_to_one :parent, :from => @c2
74
+
75
+ d = @c2.new(:id => 1)
76
+ d.parent.should == nil
77
+ end
78
+
79
+ it "should define a setter method" do
80
+ @c2.one_to_one :parent, :from => @c2
81
+
82
+ d = @c2.new(:id => 1)
83
+ d.parent = {:id => 4321}
84
+ d.values.should == {:id => 1, :parent_id => 4321}
85
+ $sqls.last.should == "UPDATE nodes SET parent_id = 4321 WHERE (id = 1)"
86
+
87
+ d.parent = nil
88
+ d.values.should == {:id => 1, :parent_id => nil}
89
+ $sqls.last.should == "UPDATE nodes SET parent_id = NULL WHERE (id = 1)"
90
+
91
+ e = @c2.new(:id => 6677)
92
+ d.parent = e
93
+ d.values.should == {:id => 1, :parent_id => 6677}
94
+ $sqls.last.should == "UPDATE nodes SET parent_id = 6677 WHERE (id = 1)"
95
+ end
96
+
97
+ it "should warn with a depreciation notice if :class option was used" do
98
+ pending("write this spec")
99
+ end
100
+
101
+ end
102
+
103
+ describe Sequel::Model, "one_to_many" do
104
+
105
+ before(:each) do
106
+ MODEL_DB.reset
107
+
108
+ @c1 = Class.new(Sequel::Model(:attributes)) do
109
+ end
110
+
111
+ @c2 = Class.new(Sequel::Model(:nodes)) do
112
+ end
113
+ end
114
+
115
+ it "should define a getter method" do
116
+ @c2.one_to_many :attributes, :from => @c1, :key => :node_id
117
+
118
+ n = @c2.new(:id => 1234)
119
+ a = n.attributes
120
+ a.should be_a_kind_of(Sequel::Dataset)
121
+ a.sql.should == 'SELECT * FROM attributes WHERE (node_id = 1234)'
122
+ end
123
+
124
+ it "should support plain dataset in the from option" do
125
+ @c2.one_to_many :attributes, :from => MODEL_DB[:xyz], :key => :node_id
126
+
127
+ n = @c2.new(:id => 1234)
128
+ a = n.attributes
129
+ a.should be_a_kind_of(Sequel::Dataset)
130
+ a.sql.should == 'SELECT * FROM xyz WHERE (node_id = 1234)'
131
+ end
132
+
133
+ it "should support table name in the from option" do
134
+ @c2.one_to_many :attributes, :from => :abc, :key => :node_id
135
+
136
+ n = @c2.new(:id => 1234)
137
+ a = n.attributes
138
+ a.should be_a_kind_of(Sequel::Dataset)
139
+ a.sql.should == 'SELECT * FROM abc WHERE (node_id = 1234)'
140
+ end
141
+
142
+ it "should warn with a depreciation notice if :class option was used" do
143
+ pending("write this spec")
144
+ end
145
+
146
+ it "should warn with a depreciation notice if :on option was used" do
147
+ pending("write this spec")
148
+ end
149
+
150
+ end