sequel 0.2.0.2 → 0.2.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.
@@ -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