sequel_model 0.1

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,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