sequel 0.2.0.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -136,6 +136,20 @@ context "Symbol#to_field_name" do
136
136
  :xyz___x.to_field_name.should == 'xyz AS x'
137
137
  :abc__def___x.to_field_name.should == 'abc.def AS x'
138
138
  end
139
+
140
+ specify "should support names with digits" do
141
+ :abc2.to_field_name.should == 'abc2'
142
+ :xx__yy3.to_field_name.should == 'xx.yy3'
143
+ :ab34__temp3_4ax.to_field_name.should == 'ab34.temp3_4ax'
144
+ :x1___y2.to_field_name.should == 'x1 AS y2'
145
+ :abc2__def3___ggg4.to_field_name.should == 'abc2.def3 AS ggg4'
146
+ end
147
+
148
+ specify "should support upper case and lower case" do
149
+ :ABC.to_field_name.should == 'ABC'
150
+ :Zvashtoy__aBcD.to_field_name.should == 'Zvashtoy.aBcD'
151
+
152
+ end
139
153
  end
140
154
 
141
155
  context "FieldCompositionMethods#field_title" do
@@ -476,6 +476,17 @@ context "A database" do
476
476
  db = Sequel::Database.new(:max_options => 4)
477
477
  db.should be_single_threaded
478
478
  db.should_not be_multi_threaded
479
-
479
+ end
480
+ end
481
+
482
+ context "Database#dataset" do
483
+ setup do
484
+ @db = Sequel::Database.new
485
+ end
486
+
487
+ specify "should delegate to Dataset#query if block is provided" do
488
+ @d = @db.query {select :x; from :y}
489
+ @d.should be_a_kind_of(Sequel::Dataset)
490
+ @d.sql.should == "SELECT x FROM y"
480
491
  end
481
492
  end
data/spec/dataset_spec.rb CHANGED
@@ -446,6 +446,24 @@ context "a grouped dataset" do
446
446
  end
447
447
  end
448
448
 
449
+ context "Dataset#group_by" do
450
+ setup do
451
+ @dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
452
+ end
453
+
454
+ specify "should raise when trying to generate an update statement" do
455
+ proc {@dataset.update_sql(:id => 0)}.should raise_error
456
+ end
457
+
458
+ specify "should raise when trying to generate a delete statement" do
459
+ proc {@dataset.delete_sql}.should raise_error
460
+ end
461
+
462
+ specify "should specify the grouping in generated select statement" do
463
+ @dataset.select_sql.should ==
464
+ "SELECT * FROM test GROUP BY type_id"
465
+ end
466
+ end
449
467
 
450
468
  context "Dataset#literal" do
451
469
  setup do
@@ -600,6 +618,32 @@ context "Dataset#order" do
600
618
  end
601
619
  end
602
620
 
621
+ context "Dataset#order_by" do
622
+ setup do
623
+ @dataset = Sequel::Dataset.new(nil).from(:test)
624
+ end
625
+
626
+ specify "should include an ORDER BY clause in the select statement" do
627
+ @dataset.order_by(:name).sql.should ==
628
+ 'SELECT * FROM test ORDER BY name'
629
+ end
630
+
631
+ specify "should accept multiple arguments" do
632
+ @dataset.order_by(:name, :price.DESC).sql.should ==
633
+ 'SELECT * FROM test ORDER BY name, price DESC'
634
+ end
635
+
636
+ specify "should overrun a previous ordering" do
637
+ @dataset.order_by(:name).order(:stamp).sql.should ==
638
+ 'SELECT * FROM test ORDER BY stamp'
639
+ end
640
+
641
+ specify "should accept a string" do
642
+ @dataset.order_by('dada ASC').sql.should ==
643
+ 'SELECT * FROM test ORDER BY dada ASC'
644
+ end
645
+ end
646
+
603
647
  context "Dataset#reverse_order" do
604
648
  setup do
605
649
  @dataset = Sequel::Dataset.new(nil).from(:test)
@@ -769,6 +813,26 @@ context "Dataset#count" do
769
813
  end
770
814
  end
771
815
 
816
+ context "Dataset#empty?" do
817
+ specify "should return true if #count == 0" do
818
+ @c = Class.new(Sequel::Dataset) do
819
+ def count
820
+ 0
821
+ end
822
+ end
823
+ @dataset = @c.new(nil).from(:test)
824
+ @dataset.empty?.should be_true
825
+
826
+ @c = Class.new(Sequel::Dataset) do
827
+ def count
828
+ 1
829
+ end
830
+ end
831
+ @dataset = @c.new(nil).from(:test)
832
+ @dataset.empty?.should be_false
833
+ end
834
+ end
835
+
772
836
  context "Dataset#join_table" do
773
837
  setup do
774
838
  @d = Sequel::Dataset.new(nil).from(:items)
@@ -1157,6 +1221,33 @@ context "Dataset#single_value" do
1157
1221
  end
1158
1222
  end
1159
1223
 
1224
+ context "Dataset#set_row_proc" do
1225
+ setup do
1226
+ @c = Class.new(Sequel::Dataset) do
1227
+ def fetch_rows(sql, &block)
1228
+ # yield a hash with kind as the 1 bit of a number
1229
+ (1..10).each {|i| block.call({:kind => i[0]})}
1230
+ end
1231
+ end
1232
+ @dataset = @c.new(nil).from(:items)
1233
+ end
1234
+
1235
+ specify "should cause dataset to pass all rows through the filter" do
1236
+ @dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
1237
+
1238
+ rows = @dataset.all
1239
+ rows.size.should == 10
1240
+
1241
+ rows.each {|r| r[:der].should == (r[:kind] + 2)}
1242
+ end
1243
+
1244
+ specify "should be copied over when dataset is cloned" do
1245
+ @dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
1246
+
1247
+ @dataset.filter(:a => 1).first.should == {:kind => 1, :der => 3}
1248
+ end
1249
+ end
1250
+
1160
1251
  context "Dataset#set_model" do
1161
1252
  setup do
1162
1253
  @c = Class.new(Sequel::Dataset) do
@@ -1570,4 +1661,187 @@ context "Dataset#multi_insert" do
1570
1661
  'COMMIT;'
1571
1662
  ]
1572
1663
  end
1664
+ end
1665
+
1666
+ context "Dataset#query" do
1667
+ setup do
1668
+ @d = Sequel::Dataset.new(nil)
1669
+ end
1670
+
1671
+ specify "should support #from" do
1672
+ q = @d.query {from :xxx}
1673
+ q.class.should == @d.class
1674
+ q.sql.should == "SELECT * FROM xxx"
1675
+ end
1676
+
1677
+ specify "should support #select" do
1678
+ q = @d.query do
1679
+ select :a, :b___mongo
1680
+ from :yyy
1681
+ end
1682
+ q.class.should == @d.class
1683
+ q.sql.should == "SELECT a, b AS mongo FROM yyy"
1684
+ end
1685
+
1686
+ specify "should support #where" do
1687
+ q = @d.query do
1688
+ from :zzz
1689
+ where {:x + 2 > :y + 3}
1690
+ end
1691
+ q.class.should == @d.class
1692
+ q.sql.should == "SELECT * FROM zzz WHERE ((x + 2) > (y + 3))"
1693
+
1694
+ q = @d.from(:zzz).query do
1695
+ where {:x > 1 && :y > 2}
1696
+ end
1697
+ q.class.should == @d.class
1698
+ q.sql.should == "SELECT * FROM zzz WHERE ((x > 1) AND (y > 2))"
1699
+
1700
+ q = @d.from(:zzz).query do
1701
+ where :x => 33
1702
+ end
1703
+ q.class.should == @d.class
1704
+ q.sql.should == "SELECT * FROM zzz WHERE (x = 33)"
1705
+ end
1706
+
1707
+ specify "should support #group_by and #having" do
1708
+ q = @d.query do
1709
+ from :abc
1710
+ group_by :id
1711
+ having {:x >= 2}
1712
+ end
1713
+ q.class.should == @d.class
1714
+ q.sql.should == "SELECT * FROM abc GROUP BY id HAVING (x >= 2)"
1715
+ end
1716
+
1717
+ specify "should support #order, #order_by" do
1718
+ q = @d.query do
1719
+ from :xyz
1720
+ order_by :stamp
1721
+ end
1722
+ q.class.should == @d.class
1723
+ q.sql.should == "SELECT * FROM xyz ORDER BY stamp"
1724
+ end
1725
+
1726
+ specify "should raise on non-chainable method calls" do
1727
+ proc {@d.query {count}}.should raise_error(SequelError)
1728
+ end
1729
+
1730
+ specify "should raise on each, insert, update, delete" do
1731
+ proc {@d.query {each}}.should raise_error(SequelError)
1732
+ proc {@d.query {insert(:x => 1)}}.should raise_error(SequelError)
1733
+ proc {@d.query {update(:x => 1)}}.should raise_error(SequelError)
1734
+ proc {@d.query {delete}}.should raise_error(SequelError)
1735
+ end
1736
+ end
1737
+
1738
+ context "Dataset" do
1739
+ setup do
1740
+ @d = Sequel::Dataset.new(nil).from(:x)
1741
+ end
1742
+
1743
+ specify "should support self-changing select!" do
1744
+ @d.select!(:y)
1745
+ @d.sql.should == "SELECT y FROM x"
1746
+ end
1747
+
1748
+ specify "should support self-changing from!" do
1749
+ @d.from!(:y)
1750
+ @d.sql.should == "SELECT * FROM y"
1751
+ end
1752
+
1753
+ specify "should support self-changing order!" do
1754
+ @d.order!(:y)
1755
+ @d.sql.should == "SELECT * FROM x ORDER BY y"
1756
+ end
1757
+
1758
+ specify "should support self-changing filter!" do
1759
+ @d.filter!(:y => 1)
1760
+ @d.sql.should == "SELECT * FROM x WHERE (y = 1)"
1761
+ end
1762
+
1763
+ specify "should support self-changing filter! with block" do
1764
+ @d.filter! {:y == 2}
1765
+ @d.sql.should == "SELECT * FROM x WHERE (y = 2)"
1766
+ end
1767
+
1768
+ specify "should raise for ! methods that don't return a dataset" do
1769
+ proc {@d.opts!}.should raise_error(NameError)
1770
+ end
1771
+
1772
+ specify "should raise for missing methods" do
1773
+ proc {@d.xuyz}.should raise_error(NameError)
1774
+ proc {@d.xyz!}.should raise_error(NameError)
1775
+ proc {@d.xyz?}.should raise_error(NameError)
1776
+ end
1777
+
1778
+ specify "should support chaining of bang methods" do
1779
+ @d.order!(:y)
1780
+ @d.filter!(:y => 1)
1781
+ @d.sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
1782
+ end
1783
+ end
1784
+
1785
+ context "Dataset#transform" do
1786
+ setup do
1787
+ @c = Class.new(Sequel::Dataset) do
1788
+ attr_accessor :raw
1789
+ attr_accessor :sql
1790
+
1791
+ def fetch_rows(sql, &block)
1792
+ block[@raw]
1793
+ end
1794
+
1795
+ def insert(v)
1796
+ @sql = insert_sql(v)
1797
+ end
1798
+
1799
+ def update(v)
1800
+ @sql = update_sql(v)
1801
+ end
1802
+ end
1803
+
1804
+ @ds = @c.new(nil).from(:items)
1805
+ @ds.transform(:x => [
1806
+ proc {|v| Marshal.load(v)},
1807
+ proc {|v| Marshal.dump(v)}
1808
+ ])
1809
+ end
1810
+
1811
+ specify "should change the dataset to transform values loaded from the database" do
1812
+ @ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
1813
+ @ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
1814
+ end
1815
+
1816
+ specify "should change the dataset to transform values saved to the database" do
1817
+ @ds.insert(:x => :toast)
1818
+ @ds.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');"
1819
+
1820
+ @ds.insert(:y => 'butter')
1821
+ @ds.sql.should == "INSERT INTO items (y) VALUES ('butter');"
1822
+
1823
+ @ds.update(:x => ['dream'])
1824
+ @ds.sql.should == "UPDATE items SET x = '#{Marshal.dump(['dream'])}'"
1825
+ end
1826
+
1827
+ specify "should be transferred to cloned datasets" do
1828
+ @ds2 = @ds.filter(:a => 1)
1829
+
1830
+ @ds2.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
1831
+ @ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
1832
+
1833
+ @ds2.insert(:x => :toast)
1834
+ @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');"
1835
+ end
1836
+
1837
+ specify "should work correctly together with set_row_proc" do
1838
+ @ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
1839
+ @ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
1840
+ @ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
1841
+
1842
+ f = nil
1843
+ @ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
1844
+ @ds.each(:naked => true) {|r| f = r}
1845
+ f.should == {:x => "wow", :y => 'hello'}
1846
+ end
1573
1847
  end
data/spec/model_spec.rb CHANGED
@@ -1,6 +1,132 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
- Sequel::Model.db = SchemaDummyDatabase.new
3
+ Sequel::Model.db = MODEL_DB = MockDatabase.new
4
+
5
+ context "A model class" do
6
+ specify "should be associated with a dataset" do
7
+ @m = Class.new(Sequel::Model) do
8
+ set_dataset MODEL_DB[:items]
9
+ end
10
+
11
+ @m.dataset.should be_a_kind_of(MockDataset)
12
+ @m.dataset.opts[:from].should == [:items]
13
+
14
+ @m2 = Class.new(Sequel::Model) do
15
+ set_dataset MODEL_DB[:zzz]
16
+ end
17
+
18
+ @m2.dataset.should be_a_kind_of(MockDataset)
19
+ @m2.dataset.opts[:from].should == [:zzz]
20
+ @m.dataset.opts[:from].should == [:items]
21
+ end
22
+ end
23
+
24
+ context "A model's primary key" do
25
+ specify "should default to id" do
26
+ @m = Class.new(Sequel::Model) do
27
+ end
28
+
29
+ @m.primary_key.should == :id
30
+ end
31
+
32
+ specify "should be changeable through Model.set_primary_key" do
33
+ @m = Class.new(Sequel::Model) do
34
+ set_primary_key :xxx
35
+ end
36
+
37
+ @m.primary_key.should == :xxx
38
+ end
39
+
40
+ specify "should support composite primary keys" do
41
+ @m = Class.new(Sequel::Model) do
42
+ set_primary_key [:node_id, :session_id]
43
+ end
44
+ @m.primary_key.should == [:node_id, :session_id]
45
+ end
46
+ end
47
+
48
+ context "A model without a primary key" do
49
+ setup do
50
+ @m = Class.new(Sequel::Model) do
51
+ no_primary_key
52
+ end
53
+ end
54
+
55
+ specify "should return nil for primary_key" do
56
+ @m.primary_key.should be_nil
57
+ end
58
+
59
+ specify "should raise on #this" do
60
+ o = @m.new
61
+ proc {o.this}.should raise_error(SequelError)
62
+ end
63
+ end
64
+
65
+ context "Model#this" do
66
+ setup do
67
+ @m = Class.new(Sequel::Model(:items)) do
68
+ end
69
+ end
70
+
71
+ specify "should return a dataset identifying the record" do
72
+ o = @m.new(:id => 3)
73
+ o.this.sql.should == "SELECT * FROM items WHERE (id = 3)"
74
+ end
75
+
76
+ specify "should support arbitrary primary keys" do
77
+ @m.set_primary_key(:xxx)
78
+
79
+ o = @m.new(:xxx => 3)
80
+ o.this.sql.should == "SELECT * FROM items WHERE (xxx = 3)"
81
+ end
82
+
83
+ specify "should support composite primary keys" do
84
+ @m.set_primary_key [:x, :y]
85
+ o = @m.new(:x => 4, :y => 5)
86
+
87
+ o.this.sql.should =~ /^SELECT \* FROM items WHERE (\(x = 4\) AND \(y = 5\))|(\(y = 5\) AND \(x = 4\))$/
88
+ end
89
+ end
90
+
91
+ context "A new model instance" do
92
+ setup do
93
+ @m = Class.new(Sequel::Model) do
94
+ set_dataset MODEL_DB[:items]
95
+ end
96
+ end
97
+
98
+ specify "should be marked as new?" do
99
+ o = @m.new
100
+ o.should be_new
101
+ end
102
+
103
+ specify "should not be marked as new? once it is saved" do
104
+ o = @m.new(:x => 1)
105
+ o.should be_new
106
+ o.save
107
+ o.should_not be_new
108
+ end
109
+
110
+ specify "should use the last inserted id as primary key if not in values" do
111
+ d = @m.dataset
112
+ def d.insert(*args)
113
+ super
114
+ 1234
115
+ end
116
+
117
+ def d.first
118
+ {:x => 1, :id => 1234}
119
+ end
120
+
121
+ o = @m.new(:x => 1)
122
+ o.save
123
+ o.id.should == 1234
124
+
125
+ o = @m.new(:x => 1, :id => 333)
126
+ o.save
127
+ o.id.should == 333
128
+ end
129
+ end
4
130
 
5
131
  describe Sequel::Model do
6
132
  before do
@@ -20,8 +146,8 @@ describe Sequel::Model do
20
146
  @model.primary_key.should == :ssn
21
147
  end
22
148
 
23
- it "allows table name change" do
24
- @model.set_table_name :foo
149
+ it "allows dataset change" do
150
+ @model.set_dataset(MODEL_DB[:foo])
25
151
  @model.table_name.should == :foo
26
152
  end
27
153
 
@@ -55,4 +181,161 @@ context "Sequel::Model()" do
55
181
  eval "class DummyModelBased < Sequel::Model(:blog); end"
56
182
  end.should_not raise_error
57
183
  end
184
+ end
185
+
186
+ context "A model class" do
187
+ setup do
188
+ MODEL_DB.reset
189
+ @c = Class.new(Sequel::Model(:items))
190
+ end
191
+
192
+ specify "should be able to create rows in the associated table" do
193
+ o = @c.create(:x => 1)
194
+ o.class.should == @c
195
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1);']
196
+ end
197
+
198
+ specify "should be able to create rows without any values specified" do
199
+ o = @c.create
200
+ o.class.should == @c
201
+ MODEL_DB.sqls.should == ['INSERT INTO items DEFAULT VALUES;']
202
+ end
203
+ end
204
+
205
+ context "A model class without a primary key" do
206
+ setup do
207
+ MODEL_DB.reset
208
+ @c = Class.new(Sequel::Model(:items)) do
209
+ no_primary_key
210
+ end
211
+ end
212
+
213
+ specify "should be able to insert records without selecting them back" do
214
+ i = nil
215
+ proc {i = @c.create(:x => 1)}.should_not raise_error
216
+ i.class.should be(@c)
217
+ i.values.should == {:x => 1}
218
+
219
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1);']
220
+ end
221
+
222
+ specify "should raise when deleting" do
223
+ o = @c.new
224
+ proc {o.delete}.should raise_error
225
+ end
226
+
227
+ specify "should insert a record when saving" do
228
+ o = @c.new(:x => 2)
229
+ o.should be_new
230
+ o.save
231
+ MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (2);']
232
+ end
233
+ end
234
+
235
+ context "Model#serialize" do
236
+ setup do
237
+ MODEL_DB.reset
238
+ end
239
+
240
+ specify "should translate values to YAML when creating records" do
241
+ @c = Class.new(Sequel::Model(:items)) do
242
+ no_primary_key
243
+ serialize :abc
244
+ end
245
+
246
+ @c.create(:abc => 1)
247
+ @c.create(:abc => "hello")
248
+
249
+ MODEL_DB.sqls.should == [ \
250
+ "INSERT INTO items (abc) VALUES ('--- 1\n');", \
251
+ "INSERT INTO items (abc) VALUES ('--- hello\n');", \
252
+ ]
253
+ end
254
+
255
+ specify "should translate values to and from YAML using accessor methods" do
256
+ @c = Class.new(Sequel::Model(:items)) do
257
+ serialize :abc, :def
258
+ end
259
+
260
+ ds = @c.dataset
261
+ ds.extend(Module.new {
262
+ attr_accessor :raw
263
+
264
+ def fetch_rows(sql, &block)
265
+ block.call(@raw)
266
+ end
267
+
268
+ @@sqls = nil
269
+
270
+ def insert(*args)
271
+ @@sqls = insert_sql(*args)
272
+ end
273
+
274
+ def update(*args)
275
+ @@sqls = update_sql(*args)
276
+ end
277
+
278
+ def sqls
279
+ @@sqls
280
+ end
281
+
282
+ def columns
283
+ [:id, :abc, :def]
284
+ end
285
+ })
286
+
287
+ ds.raw = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
288
+ o = @c.first
289
+ o.id.should == 1
290
+ o.abc.should == 1
291
+ o.def.should == "hello"
292
+
293
+ o.set(:abc => 23)
294
+ ds.sqls.should == "UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)"
295
+
296
+ o = @c.create(:abc => [1, 2, 3])
297
+ ds.sqls.should == "INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}');"
298
+ end
299
+ end
300
+
301
+ context "Model attribute accessors" do
302
+ setup do
303
+ MODEL_DB.reset
304
+
305
+ @c = Class.new(Sequel::Model(:items)) do
306
+ def columns
307
+ [:id, :x, :y]
308
+ end
309
+ end
310
+
311
+ ds = @c.dataset
312
+ ds.extend(Module.new {
313
+ def columns
314
+ [:id, :x, :y]
315
+ end
316
+ })
317
+ end
318
+
319
+ specify "should be created dynamically" do
320
+ o = @c.new
321
+
322
+ o.should_not be_respond_to(:x)
323
+ o.x.should be_nil
324
+ o.should be_respond_to(:x)
325
+
326
+ o.should_not be_respond_to(:x=)
327
+ o.x = 34
328
+ o.x.should == 34
329
+ o.should be_respond_to(:x=)
330
+ end
331
+
332
+ specify "should raise for a column that doesn't exist in the dataset" do
333
+ o = @c.new
334
+
335
+ proc {o.x}.should_not raise_error
336
+ proc {o.xx}.should raise_error(SequelError)
337
+
338
+ proc {o.x = 3}.should_not raise_error
339
+ proc {o.yy = 4}.should raise_error
340
+ end
58
341
  end