sequel 1.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,239 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe "Model attribute setters" 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 mark the column value as changed" do
16
+ o = @c.new
17
+ o.changed_columns.should == []
18
+
19
+ o.x = 2
20
+ o.changed_columns.should == [:x]
21
+
22
+ o.y = 3
23
+ o.changed_columns.should == [:x, :y]
24
+
25
+ o.changed_columns.clear
26
+
27
+ o[:x] = 2
28
+ o.changed_columns.should == [:x]
29
+
30
+ o[:y] = 3
31
+ o.changed_columns.should == [:x, :y]
32
+ end
33
+
34
+ end
35
+
36
+ describe "Model#serialize" do
37
+
38
+ before(:each) do
39
+ MODEL_DB.reset
40
+ end
41
+
42
+ it "should translate values to YAML when creating records" do
43
+ @c = Class.new(Sequel::Model(:items)) do
44
+ no_primary_key
45
+ serialize :abc
46
+ end
47
+
48
+ @c.create(:abc => 1)
49
+ @c.create(:abc => "hello")
50
+
51
+ MODEL_DB.sqls.should == [ \
52
+ "INSERT INTO items (abc) VALUES ('--- 1\n')", \
53
+ "INSERT INTO items (abc) VALUES ('--- hello\n')", \
54
+ ]
55
+ end
56
+
57
+ it "should support calling after the class is defined" do
58
+ @c = Class.new(Sequel::Model(:items)) do
59
+ no_primary_key
60
+ end
61
+
62
+ @c.serialize :def
63
+
64
+ @c.create(:def => 1)
65
+ @c.create(:def => "hello")
66
+
67
+ MODEL_DB.sqls.should == [ \
68
+ "INSERT INTO items (def) VALUES ('--- 1\n')", \
69
+ "INSERT INTO items (def) VALUES ('--- hello\n')", \
70
+ ]
71
+ end
72
+
73
+ it "should support using the Marshal format" do
74
+ @c = Class.new(Sequel::Model(:items)) do
75
+ no_primary_key
76
+ serialize :abc, :format => :marshal
77
+ end
78
+
79
+ @c.create(:abc => 1)
80
+ @c.create(:abc => "hello")
81
+
82
+ MODEL_DB.sqls.should == [ \
83
+ "INSERT INTO items (abc) VALUES ('BAhpBg==\n')", \
84
+ "INSERT INTO items (abc) VALUES ('BAgiCmhlbGxv\n')", \
85
+ ]
86
+ end
87
+
88
+ it "should translate values to and from YAML using accessor methods" do
89
+ @c = Class.new(Sequel::Model(:items)) do
90
+ serialize :abc, :def
91
+ end
92
+
93
+ ds = @c.dataset
94
+ ds.extend(Module.new {
95
+ attr_accessor :raw
96
+
97
+ def fetch_rows(sql, &block)
98
+ block.call(@raw)
99
+ end
100
+
101
+ @@sqls = nil
102
+
103
+ def insert(*args)
104
+ @@sqls = insert_sql(*args)
105
+ end
106
+
107
+ def update(*args)
108
+ @@sqls = update_sql(*args)
109
+ end
110
+
111
+ def sqls
112
+ @@sqls
113
+ end
114
+
115
+ def columns
116
+ [:id, :abc, :def]
117
+ end
118
+ }
119
+ )
120
+
121
+ ds.raw = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
122
+ o = @c.first
123
+ o.id.should == 1
124
+ o.abc.should == 1
125
+ o.def.should == "hello"
126
+
127
+ o.set(:abc => 23)
128
+ ds.sqls.should == "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)"
129
+
130
+ ds.raw = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
131
+ o = @c.create(:abc => [1, 2, 3])
132
+ ds.sqls.should == "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}')"
133
+ end
134
+
135
+ end
136
+
137
+ describe Sequel::Model, "super_dataset" do
138
+ setup do
139
+ MODEL_DB.reset
140
+ class SubClass < Sequel::Model(:items) ; end
141
+ end
142
+
143
+ it "should call the superclass's dataset" do
144
+ SubClass.should_receive(:superclass).exactly(3).times.and_return(Sequel::Model(:items))
145
+ Sequel::Model(:items).should_receive(:dataset)
146
+ SubClass.super_dataset
147
+ end
148
+ end
149
+
150
+ describe Sequel::Model, "dataset" do
151
+ setup do
152
+ @a = Class.new(Sequel::Model(:items))
153
+ @b = Class.new(Sequel::Model)
154
+
155
+ class Elephant < Sequel::Model(:ele1)
156
+ end
157
+
158
+ class Maggot < Sequel::Model
159
+ end
160
+
161
+ class ShoeSize < Sequel::Model
162
+ end
163
+
164
+ class BootSize < ShoeSize
165
+ end
166
+ end
167
+
168
+ specify "should default to the plural of the class name" do
169
+ Maggot.dataset.sql.should == 'SELECT * FROM maggots'
170
+ ShoeSize.dataset.sql.should == 'SELECT * FROM shoe_sizes'
171
+ end
172
+
173
+ specify "should return the dataset for the superclass if available" do
174
+ BootSize.dataset.sql.should == 'SELECT * FROM shoe_sizes'
175
+ end
176
+
177
+ specify "should return the correct dataset if set explicitly" do
178
+ Elephant.dataset.sql.should == 'SELECT * FROM ele1'
179
+ @a.dataset.sql.should == 'SELECT * FROM items'
180
+ end
181
+
182
+ specify "should raise if no dataset is explicitly set and the class is anonymous" do
183
+ proc {@b.dataset}.should raise_error(Sequel::Error)
184
+ end
185
+
186
+ specify "should disregard namespaces for the table name" do
187
+ module BlahBlah
188
+ class MwaHaHa < Sequel::Model
189
+ end
190
+ end
191
+
192
+ BlahBlah::MwaHaHa.dataset.sql.should == 'SELECT * FROM mwa_ha_has'
193
+ end
194
+ end
195
+
196
+ describe "A model class with implicit table name" do
197
+ setup do
198
+ class Donkey < Sequel::Model
199
+ end
200
+ end
201
+
202
+ specify "should have a dataset associated with the model class" do
203
+ Donkey.dataset.model_classes.should == {nil => Donkey}
204
+ end
205
+ end
206
+
207
+ describe "A model inheriting from a model" do
208
+ setup do
209
+ class Feline < Sequel::Model
210
+ end
211
+
212
+ class Leopard < Feline
213
+ end
214
+ end
215
+
216
+ specify "should have a dataset associated with itself" do
217
+ Feline.dataset.model_classes.should == {nil => Feline}
218
+ Leopard.dataset.model_classes.should == {nil => Leopard}
219
+ end
220
+ end
221
+
222
+ describe "Model.db=" do
223
+ setup do
224
+ $db1 = Sequel::Database.new
225
+ $db2 = Sequel::Database.new
226
+
227
+ class BlueBlue < Sequel::Model
228
+ set_dataset $db1[:blue]
229
+ end
230
+ end
231
+
232
+ specify "should affect the underlying dataset" do
233
+ BlueBlue.db = $db2
234
+
235
+ BlueBlue.dataset.db.should === $db2
236
+ BlueBlue.dataset.db.should_not === $db1
237
+ end
238
+ end
239
+
@@ -0,0 +1,150 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "caching" do
4
+
5
+ before(:each) do
6
+ MODEL_DB.reset
7
+
8
+ @cache_class = Class.new(Hash) do
9
+ attr_accessor :ttl
10
+ def set(k, v, ttl); self[k] = v; @ttl = ttl; end
11
+ def get(k); self[k]; end
12
+ end
13
+ cache = @cache_class.new
14
+ @cache = cache
15
+
16
+ @c = Class.new(Sequel::Model(:items)) do
17
+ set_cache cache
18
+
19
+ def self.columns
20
+ [:name, :id]
21
+ end
22
+ end
23
+
24
+ $cache_dataset_row = {:name => 'sharon', :id => 1}
25
+ @dataset = @c.dataset
26
+ $sqls = []
27
+ @dataset.extend(Module.new {
28
+ def fetch_rows(sql)
29
+ $sqls << sql
30
+ yield $cache_dataset_row
31
+ end
32
+
33
+ def update(values)
34
+ $sqls << update_sql(values)
35
+ $cache_dataset_row.merge!(values)
36
+ end
37
+
38
+ def delete
39
+ $sqls << delete_sql
40
+ end
41
+ })
42
+ end
43
+
44
+ it "should set the model's cache store" do
45
+ @c.cache_store.should be(@cache)
46
+ end
47
+
48
+ it "should have a default ttl of 3600" do
49
+ @c.cache_ttl.should == 3600
50
+ end
51
+
52
+ it "should take a ttl option" do
53
+ @c.set_cache @cache, :ttl => 1234
54
+ @c.cache_ttl.should == 1234
55
+ end
56
+
57
+ it "should offer a set_cache_ttl method for setting the ttl" do
58
+ @c.cache_ttl.should == 3600
59
+ @c.set_cache_ttl 1234
60
+ @c.cache_ttl.should == 1234
61
+ end
62
+
63
+ it "should generate a cache key appropriate to the class" do
64
+ m = @c.new
65
+ m.values[:id] = 1
66
+ m.cache_key.should == "#{m.class}:1"
67
+
68
+ # custom primary key
69
+ @c.set_primary_key :ttt
70
+ m = @c.new
71
+ m.values[:ttt] = 333
72
+ m.cache_key.should == "#{m.class}:333"
73
+
74
+ # composite primary key
75
+ @c.set_primary_key [:a, :b, :c]
76
+ m = @c.new
77
+ m.values[:a] = 123
78
+ m.values[:c] = 456
79
+ m.values[:b] = 789
80
+ m.cache_key.should == "#{m.class}:123,789,456"
81
+ end
82
+
83
+ it "should raise error if attempting to generate cache_key and primary key value is null" do
84
+ m = @c.new
85
+ proc {m.cache_key}.should raise_error(Sequel::Error)
86
+
87
+ m.values[:id] = 1
88
+ proc {m.cache_key}.should_not raise_error(Sequel::Error)
89
+ end
90
+
91
+ it "should set the cache when reading from the database" do
92
+ $sqls.should == []
93
+ @cache.should be_empty
94
+
95
+ m = @c[1]
96
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
97
+ m.values.should == $cache_dataset_row
98
+ @cache[m.cache_key].should == m
99
+
100
+ # read from cache
101
+ m2 = @c[1]
102
+ $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
103
+ m2.should == m
104
+ m2.values.should == $cache_dataset_row
105
+ end
106
+
107
+ it "should delete the cache when writing to the database" do
108
+ # fill the cache
109
+ m = @c[1]
110
+ @cache[m.cache_key].should == m
111
+
112
+ m.set(:name => 'tutu')
113
+ @cache.has_key?(m.cache_key).should be_false
114
+ $sqls.last.should == "UPDATE items SET name = 'tutu' WHERE (id = 1)"
115
+
116
+ m = @c[1]
117
+ @cache[m.cache_key].should == m
118
+ m.name = 'hey'
119
+ m.save
120
+ @cache.has_key?(m.cache_key).should be_false
121
+ $sqls.last.should == "UPDATE items SET name = 'hey', id = 1 WHERE (id = 1)"
122
+ end
123
+
124
+ it "should delete the cache when deleting the record" do
125
+ # fill the cache
126
+ m = @c[1]
127
+ @cache[m.cache_key].should == m
128
+
129
+ m.delete
130
+ @cache.has_key?(m.cache_key).should be_false
131
+ $sqls.last.should == "DELETE FROM items WHERE (id = 1)"
132
+ end
133
+
134
+ it "should support #[] as a shortcut to #find with hash" do
135
+ m = @c[:id => 3]
136
+ @cache[m.cache_key].should be_nil
137
+ $sqls.last.should == "SELECT * FROM items WHERE (id = 3) LIMIT 1"
138
+
139
+ m = @c[1]
140
+ @cache[m.cache_key].should == m
141
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
142
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
143
+
144
+ @c[:id => 4]
145
+ $sqls.should == ["SELECT * FROM items WHERE (id = 3) LIMIT 1", \
146
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1", \
147
+ "SELECT * FROM items WHERE (id = 4) LIMIT 1"]
148
+ end
149
+
150
+ end
@@ -0,0 +1,153 @@
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
+ def columns; [:id, :parent_id]; end
13
+ end
14
+
15
+ @dataset = @c2.dataset
16
+
17
+ @dataset.extend(Module.new {
18
+ def fetch_rows(sql)
19
+ @db << sql
20
+ yield({:hey => 1})
21
+ end
22
+ })
23
+ end
24
+
25
+ it "should use implicit key if omitted" do
26
+ @c2.one_to_one :parent, :from => @c2
27
+
28
+ d = @c2.new(:id => 1, :parent_id => 234)
29
+ p = d.parent
30
+ p.class.should == @c2
31
+ p.values.should == {:hey => 1}
32
+
33
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 234) LIMIT 1"]
34
+ end
35
+
36
+ it "should use explicit key if given" do
37
+ @c2.one_to_one :parent, :from => @c2, :key => :blah
38
+
39
+ d = @c2.new(:id => 1, :blah => 567)
40
+ p = d.parent
41
+ p.class.should == @c2
42
+ p.values.should == {:hey => 1}
43
+
44
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
45
+ end
46
+
47
+ # the new implementation doesn't support plain datasets as associations
48
+ # it "should support plain dataset in the from option" do
49
+ # @c2.one_to_one :parent, :from => MODEL_DB[:xyz]
50
+ #
51
+ # d = @c2.new(:id => 1, :parent_id => 789)
52
+ # p = d.parent
53
+ # p.class.should == Hash
54
+ #
55
+ # MODEL_DB.sqls.should == ["SELECT * FROM xyz WHERE (id = 789) LIMIT 1"]
56
+ # end
57
+
58
+ # the new implementation doesn't support plain datasets as associations
59
+ # it "should support table name in the from option" do
60
+ # @c2.one_to_one :parent, :from => :abc
61
+ #
62
+ # d = @c2.new(:id => 1, :parent_id => 789)
63
+ # p = d.parent
64
+ # p.class.should == Hash
65
+ #
66
+ # MODEL_DB.sqls.should == ["SELECT * FROM abc WHERE (id = 789) LIMIT 1"]
67
+ # end
68
+
69
+ it "should return nil if key value is nil" do
70
+ @c2.one_to_one :parent, :from => @c2
71
+
72
+ d = @c2.new(:id => 1)
73
+ d.parent.should == nil
74
+ end
75
+
76
+ it "should define a setter method" do
77
+ @c2.one_to_one :parent, :from => @c2
78
+
79
+ d = @c2.load(:id => 1)
80
+ d.parent = @c2.new(:id => 4321)
81
+ d.values.should == {:id => 1, :parent_id => 4321}
82
+ d.save_changes
83
+ MODEL_DB.sqls.last.should == "UPDATE nodes SET parent_id = 4321 WHERE (id = 1)"
84
+
85
+ d.parent = nil
86
+ d.values.should == {:id => 1, :parent_id => nil}
87
+ d.save_changes
88
+ MODEL_DB.sqls.last.should == "UPDATE nodes SET parent_id = NULL WHERE (id = 1)"
89
+
90
+ e = @c2.new(:id => 6677)
91
+ d.parent = e
92
+ d.values.should == {:id => 1, :parent_id => 6677}
93
+ d.save_changes
94
+ MODEL_DB.sqls.last.should == "UPDATE nodes SET parent_id = 6677 WHERE (id = 1)"
95
+ end
96
+ end
97
+
98
+ describe Sequel::Model, "one_to_many" do
99
+
100
+ before(:each) do
101
+ MODEL_DB.reset
102
+
103
+ @c1 = Class.new(Sequel::Model(:attributes)) do
104
+ end
105
+
106
+ @c2 = Class.new(Sequel::Model(:nodes)) do
107
+ end
108
+ end
109
+
110
+ it "should define a getter method" do
111
+ @c2.one_to_many :attributes, :from => @c1, :key => :node_id
112
+
113
+ n = @c2.new(:id => 1234)
114
+ a = n.attributes_dataset
115
+ a.should be_a_kind_of(Sequel::Dataset)
116
+ a.sql.should == 'SELECT * FROM attributes WHERE (node_id = 1234)'
117
+ end
118
+
119
+ # the new implementation doesn't support plain datasets as associations
120
+ # it "should support plain dataset in the from option" do
121
+ # @c2.one_to_many :attributes, :from => MODEL_DB[:xyz], :key => :node_id
122
+ #
123
+ # n = @c2.new(:id => 1234)
124
+ # a = n.attributes
125
+ # a.should be_a_kind_of(Sequel::Dataset)
126
+ # a.sql.should == 'SELECT * FROM xyz WHERE (node_id = 1234)'
127
+ # end
128
+
129
+ # the new implementation doesn't support plain datasets as associations
130
+ # it "should support table name in the from option" do
131
+ # @c2.one_to_many :attributes, :from => :abc, :key => :node_id
132
+ #
133
+ # n = @c2.new(:id => 1234)
134
+ # a = n.attributes
135
+ # a.should be_a_kind_of(Sequel::Dataset)
136
+ # a.sql.should == 'SELECT * FROM abc WHERE (node_id = 1234)'
137
+ # end
138
+
139
+ it "should support implicit key names" do
140
+ $c1 = @c1
141
+
142
+ module Music
143
+ class BlueNote < Sequel::Model
144
+ one_to_many :attributes, :from => $c1
145
+ end
146
+ end
147
+
148
+ n = Music::BlueNote.new(:id => 1234)
149
+ a = n.attributes_dataset
150
+ a.should be_a_kind_of(Sequel::Dataset)
151
+ a.sql.should == 'SELECT * FROM attributes WHERE (blue_note_id = 1234)'
152
+ end
153
+ end