sequel 1.5.1 → 2.0.0

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.
data/spec/base_spec.rb CHANGED
@@ -240,10 +240,10 @@ end
240
240
 
241
241
  describe "Model.db=" do
242
242
  setup do
243
- $db1 = Sequel::Database.new
244
- $db2 = Sequel::Database.new
243
+ $db1 = MockDatabase.new
244
+ $db2 = MockDatabase.new
245
245
 
246
- class BlueBlue < Sequel::Model
246
+ class BlueBlue < Sequel::Model(:items)
247
247
  set_dataset $db1[:blue]
248
248
  end
249
249
  end
@@ -9,12 +9,25 @@ describe Sequel::Model, "#eager" do
9
9
  many_to_one :band, :class=>'EagerBand', :key=>:band_id
10
10
  one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id
11
11
  many_to_many :genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
12
+ one_to_many :good_tracks, :class=>'EagerTrack', :key=>:album_id do |ds|
13
+ ds.filter(:name=>'Good')
14
+ end
15
+ many_to_one :band_name, :class=>'EagerBand', :key=>:band_id, :select=>[:id, :name]
16
+ one_to_many :track_names, :class=>'EagerTrack', :key=>:album_id, :select=>[:id, :name]
17
+ many_to_many :genre_names, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :select=>[:id]
12
18
  end
13
19
 
14
20
  class EagerBand < Sequel::Model(:bands)
15
21
  columns :id
16
22
  one_to_many :albums, :class=>'EagerAlbum', :key=>:band_id, :eager=>:tracks
17
23
  many_to_many :members, :class=>'EagerBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm
24
+ one_to_many :good_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_block=>proc{|ds| ds.filter(:name=>'good')} do |ds|
25
+ ds.filter(:name=>'Good')
26
+ end
27
+ one_to_many :self_titled_albums, :class=>'EagerAlbum', :key=>:band_id, :allow_eager=>false do |ds|
28
+ ds.filter(:name=>name)
29
+ end
30
+ one_to_many :albums_by_name, :class=>'EagerAlbum', :key=>:band_id, :order=>:name, :allow_eager=>false
18
31
  end
19
32
 
20
33
  class EagerTrack < Sequel::Model(:tracks)
@@ -34,7 +47,11 @@ describe Sequel::Model, "#eager" do
34
47
 
35
48
  EagerAlbum.dataset.extend(Module.new {
36
49
  def fetch_rows(sql)
37
- h = {:id => 1, :band_id=> 2}
50
+ h = if sql =~ /101/
51
+ {:id => 101, :band_id=> 101}
52
+ else
53
+ {:id => 1, :band_id=> 2}
54
+ end
38
55
  h.merge!(:x_foreign_key_x=>4) if sql =~ /ag\.genre_id/
39
56
  @db << sql
40
57
  yield h
@@ -46,7 +63,14 @@ describe Sequel::Model, "#eager" do
46
63
  h = {:id => 2}
47
64
  h.merge!(:x_foreign_key_x=>5) if sql =~ /bm\.member_id/
48
65
  @db << sql
49
- yield h
66
+ case sql
67
+ when /id IN (101)/
68
+ when /id > 100/
69
+ yield({:id => 101})
70
+ yield({:id => 102})
71
+ else
72
+ yield h
73
+ end
50
74
  end
51
75
  })
52
76
 
@@ -82,7 +106,7 @@ describe Sequel::Model, "#eager" do
82
106
  a.size.should == 1
83
107
  a.first.should be_a_kind_of(EagerAlbum)
84
108
  a.first.values.should == {:id => 1, :band_id => 2}
85
- MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (id IN (2))']
109
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT bands.* FROM bands WHERE (id IN (2))']
86
110
  a = a.first
87
111
  a.band.should be_a_kind_of(EagerBand)
88
112
  a.band.values.should == {:id => 2}
@@ -95,7 +119,7 @@ describe Sequel::Model, "#eager" do
95
119
  a.size.should == 1
96
120
  a.first.should be_a_kind_of(EagerAlbum)
97
121
  a.first.values.should == {:id => 1, :band_id => 2}
98
- MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (album_id IN (1))']
122
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT tracks.* FROM tracks WHERE (album_id IN (1))']
99
123
  a = a.first
100
124
  a.tracks.should be_a_kind_of(Array)
101
125
  a.tracks.size.should == 1
@@ -110,11 +134,7 @@ describe Sequel::Model, "#eager" do
110
134
  a.size.should == 1
111
135
  a.first.should be_a_kind_of(EagerAlbum)
112
136
  a.first.values.should == {:id => 1, :band_id => 2}
113
- MODEL_DB.sqls.length.should == 2
114
- MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
115
- ["SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id) AND (ag.album_id IN (1))",
116
- "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.album_id IN (1)) AND (ag.genre_id = genres.id)"
117
- ].should(include(MODEL_DB.sqls[1]))
137
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
118
138
  a = a.first
119
139
  a.genres.should be_a_kind_of(Array)
120
140
  a.genres.size.should == 1
@@ -131,12 +151,9 @@ describe Sequel::Model, "#eager" do
131
151
  a.first.values.should == {:id => 1, :band_id => 2}
132
152
  MODEL_DB.sqls.length.should == 4
133
153
  MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
134
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (id IN (2))'))
135
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (album_id IN (1))'))
136
- sqls = MODEL_DB.sqls[1..-1] - ['SELECT * FROM bands WHERE (id IN (2))', 'SELECT * FROM tracks WHERE (album_id IN (1))']
137
- ["SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id) AND (ag.album_id IN (1))",
138
- "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.album_id IN (1)) AND (ag.genre_id = genres.id)"
139
- ].should(include(sqls[0]))
154
+ MODEL_DB.sqls[1..-1].should(include('SELECT bands.* FROM bands WHERE (id IN (2))'))
155
+ MODEL_DB.sqls[1..-1].should(include('SELECT tracks.* FROM tracks WHERE (album_id IN (1))'))
156
+ MODEL_DB.sqls[1..-1].should(include('SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))'))
140
157
  a = a.first
141
158
  a.band.should be_a_kind_of(EagerBand)
142
159
  a.band.values.should == {:id => 2}
@@ -158,12 +175,10 @@ describe Sequel::Model, "#eager" do
158
175
  a.first.should be_a_kind_of(EagerTrack)
159
176
  a.first.values.should == {:id => 3, :album_id => 1}
160
177
  MODEL_DB.sqls.length.should == 4
161
- MODEL_DB.sqls[0...-1].should == ['SELECT * FROM tracks',
162
- 'SELECT * FROM albums WHERE (id IN (1))',
163
- 'SELECT * FROM bands WHERE (id IN (2))']
164
- ["SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON (bm.member_id = members.id) AND (bm.band_id IN (2))",
165
- "SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON (bm.band_id IN (2)) AND (bm.member_id = members.id)"
166
- ].should(include(MODEL_DB.sqls[-1]))
178
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
179
+ 'SELECT albums.* FROM albums WHERE (id IN (1))',
180
+ 'SELECT bands.* FROM bands WHERE (id IN (2))',
181
+ "SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
167
182
  a = a.first
168
183
  a.album.should be_a_kind_of(EagerAlbum)
169
184
  a.album.values.should == {:id => 1, :band_id => 2}
@@ -183,8 +198,8 @@ describe Sequel::Model, "#eager" do
183
198
  a.first.should be_a_kind_of(EagerBand)
184
199
  a.first.values.should == {:id => 2}
185
200
  MODEL_DB.sqls.should == ['SELECT * FROM bands',
186
- 'SELECT * FROM albums WHERE (band_id IN (2))',
187
- 'SELECT * FROM tracks WHERE (album_id IN (1))']
201
+ 'SELECT albums.* FROM albums WHERE (band_id IN (2))',
202
+ 'SELECT tracks.* FROM tracks WHERE (album_id IN (1))']
188
203
  a = a.first
189
204
  a.albums.should be_a_kind_of(Array)
190
205
  a.albums.size.should == 1
@@ -208,8 +223,8 @@ describe Sequel::Model, "#eager" do
208
223
  a = a.first
209
224
  a.albums
210
225
  MODEL_DB.sqls.should == ['SELECT * FROM bands',
211
- 'SELECT * FROM albums WHERE (band_id = 2)',
212
- 'SELECT * FROM tracks WHERE (album_id IN (1))']
226
+ 'SELECT albums.* FROM albums WHERE (band_id = 2)',
227
+ 'SELECT tracks.* FROM tracks WHERE (album_id IN (1))']
213
228
  a.albums.should be_a_kind_of(Array)
214
229
  a.albums.size.should == 1
215
230
  a.albums.first.should be_a_kind_of(EagerAlbum)
@@ -228,11 +243,7 @@ describe Sequel::Model, "#eager" do
228
243
  a.size.should == 1
229
244
  a.first.should be_a_kind_of(EagerBandMember)
230
245
  a.first.values.should == {:id => 5}
231
- MODEL_DB.sqls.length.should == 2
232
- MODEL_DB.sqls[0].should == 'SELECT * FROM members'
233
- ['SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON (bm.band_id = bands.id) AND (bm.member_id IN (5)) ORDER BY id',
234
- 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON (bm.member_id IN (5)) AND (bm.band_id = bands.id) ORDER BY id'
235
- ].should(include(MODEL_DB.sqls[1]))
246
+ MODEL_DB.sqls.should == ['SELECT * FROM members', 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON ((bm.band_id = bands.id) AND (bm.member_id IN (5))) ORDER BY id']
236
247
  a = a.first
237
248
  a.bands.should be_a_kind_of(Array)
238
249
  a.bands.size.should == 1
@@ -247,7 +258,7 @@ describe Sequel::Model, "#eager" do
247
258
  a.size.should == 1
248
259
  a.first.should be_a_kind_of(EagerAlbum)
249
260
  a.first.values.should == {:id => 1, :band_id => 2}
250
- MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (album_id IN (1))']
261
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT tracks.* FROM tracks WHERE (album_id IN (1))']
251
262
  a = a.first
252
263
  a.tracks.should be_a_kind_of(Array)
253
264
  a.tracks.size.should == 1
@@ -257,6 +268,64 @@ describe Sequel::Model, "#eager" do
257
268
  a.tracks.first.album.should == a
258
269
  MODEL_DB.sqls.length.should == 2
259
270
  end
271
+
272
+ it "should cache the negative lookup when eagerly loading a many_to_one association" do
273
+ a = EagerAlbum.eager(:band).filter(:id=>101).all
274
+ a.should be_a_kind_of(Array)
275
+ a.size.should == 1
276
+ a.first.should be_a_kind_of(EagerAlbum)
277
+ a.first.values.should == {:id => 101, :band_id => 101}
278
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (id = 101)', 'SELECT bands.* FROM bands WHERE (id IN (101))']
279
+ a = a.first
280
+ a.instance_variable_get(:@band).should == :null
281
+ a.band.should == nil
282
+ MODEL_DB.sqls.length.should == 2
283
+ end
284
+
285
+ it "should cache the negative lookup when eagerly loading a *_to_many associations" do
286
+ a = EagerBand.eager(:albums).filter('id > 100').all
287
+ a.should be_a_kind_of(Array)
288
+ a.size.should == 2
289
+ a.first.should be_a_kind_of(EagerBand)
290
+ a.first.values.should == {:id => 101}
291
+ a.last.values.should == {:id => 102}
292
+ MODEL_DB.sqls.should == ['SELECT * FROM bands WHERE (id > 100)', 'SELECT albums.* FROM albums WHERE (band_id IN (101, 102))', "SELECT tracks.* FROM tracks WHERE (album_id IN (101))"]
293
+ a.first.instance_variable_get(:@albums).should be_a_kind_of(Array)
294
+ a.first.albums.length.should == 1
295
+ a.first.albums.first.should be_a_kind_of(EagerAlbum)
296
+ a.last.instance_variable_get(:@albums).should == []
297
+ a.last.albums.should == []
298
+ MODEL_DB.sqls.length.should == 3
299
+ end
300
+
301
+ it "should use the association's block when eager loading by default" do
302
+ EagerAlbum.eager(:good_tracks).all
303
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT tracks.* FROM tracks WHERE ((album_id IN (1)) AND (name = 'Good'))"]
304
+ end
305
+
306
+ it "should use the eager_block option when eager loading if given" do
307
+ EagerBand.eager(:good_albums).all
308
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT albums.* FROM albums WHERE ((band_id IN (2)) AND (name = 'good'))"]
309
+ MODEL_DB.sqls.clear
310
+ EagerBand.eager(:good_albums=>:good_tracks).all
311
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT albums.* FROM albums WHERE ((band_id IN (2)) AND (name = 'good'))", "SELECT tracks.* FROM tracks WHERE ((album_id IN (1)) AND (name = 'Good'))"]
312
+ end
313
+
314
+ it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
315
+ proc{EagerBand.eager(:self_titled_albums).all}.should raise_error(Sequel::Error)
316
+ proc{EagerBand.eager(:albums_by_name).all}.should raise_error(Sequel::Error)
317
+ end
318
+
319
+ it "should respect the association's :select option" do
320
+ EagerAlbum.eager(:band_name).all
321
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM bands WHERE (id IN (2))"]
322
+ MODEL_DB.sqls.clear
323
+ EagerAlbum.eager(:track_names).all
324
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM tracks WHERE (album_id IN (1))"]
325
+ MODEL_DB.sqls.clear
326
+ EagerAlbum.eager(:genre_names).all
327
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
328
+ end
260
329
  end
261
330
 
262
331
  describe Sequel::Model, "#eager_graph" do
@@ -540,10 +609,8 @@ describe Sequel::Model, "#eager_graph" do
540
609
  a.genres.size.should == 1
541
610
  a.genres.first.should be_a_kind_of(GraphGenre)
542
611
  a.genres.first.values.should == {:id=>6}
543
- MODEL_DB.sqls[0].should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
544
- ["SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id) AND (ag.album_id IN (1))",
545
- "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.album_id IN (1)) AND (ag.genre_id = genres.id)"
546
- ].should(include(MODEL_DB.sqls[1]))
612
+ MODEL_DB.sqls.should == ['SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)',
613
+ "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
547
614
  end
548
615
 
549
616
  it "should handle no associated records for a single many_to_one association" do
@@ -645,4 +712,34 @@ describe Sequel::Model, "#eager_graph" do
645
712
  a[3].album.band.members.last.should be_a_kind_of(GraphBandMember)
646
713
  a[3].album.band.members.last.values.should == {:id => 6}
647
714
  end
715
+
716
+ it "should respect the association's :graph_join_type option" do
717
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_join_type=>:inner
718
+ GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums INNER JOIN bands inner_band ON (inner_band.id = albums.band_id)'
719
+
720
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_join_type=>:right_outer
721
+ GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums RIGHT OUTER JOIN tracks right_tracks ON (right_tracks.album_id = albums.id)'
722
+
723
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_type=>:inner
724
+ GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres inner_genres ON (inner_genres.id = ag.genre_id)'
725
+ end
726
+
727
+ it "should respect the association's :graph_conditions option" do
728
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:active=>true}
729
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
730
+
731
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}
732
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
733
+
734
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:id=>(0..100)}
735
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands active_band ON ((active_band.id = albums.band_id) AND ((active_band.id >= 0) AND (active_band.id <= 100)))"
736
+ end
737
+
738
+ it "should respect the association's :graph_join_table_conditions option" do
739
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_conditions=>{:active=>true}
740
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND (ag.active = 't')) LEFT OUTER JOIN genres active_genres ON (active_genres.id = ag.genre_id)"
741
+
742
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}, :graph_join_table_conditions=>{true=>:active}
743
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND ('t' = albums.active)) LEFT OUTER JOIN genres active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
744
+ end
648
745
  end
data/spec/hooks_spec.rb CHANGED
@@ -3,20 +3,6 @@ require File.join(File.dirname(__FILE__), "spec_helper")
3
3
  describe "Model hooks" do
4
4
  before do
5
5
  MODEL_DB.reset
6
-
7
- @hooks = [
8
- :after_initialize,
9
- :before_create,
10
- :after_create,
11
- :before_update,
12
- :after_update,
13
- :before_save,
14
- :after_save,
15
- :before_destroy,
16
- :after_destroy
17
- ]
18
-
19
- # @hooks.each {|h| Sequel::Model.class_def(h) {}}
20
6
  end
21
7
 
22
8
  specify "should be definable using def <hook name>" do
@@ -60,6 +46,47 @@ describe "Model hooks" do
60
46
  c.new.before_save
61
47
  $adds.should == ['hyiyie', 'byiyie']
62
48
  end
49
+
50
+ specify "should not be additive if the method or tag already exists" do
51
+ $adds = []
52
+ c = Class.new(Sequel::Model) do
53
+ def bye; $adds << 'bye'; end
54
+ before_save :bye
55
+ before_save :bye
56
+ end
57
+
58
+ c.new.before_save
59
+ $adds.should == ['bye']
60
+
61
+ $adds = []
62
+ d = Class.new(Sequel::Model) do
63
+ before_save(:bye){$adds << 'hyiyie'}
64
+ before_save(:bye){$adds << 'byiyie'}
65
+ end
66
+
67
+ d.new.before_save
68
+ $adds.should == ['byiyie']
69
+
70
+ $adds = []
71
+ e = Class.new(Sequel::Model) do
72
+ def bye; $adds << 'bye'; end
73
+ before_save :bye
74
+ before_save(:bye){$adds << 'byiyie'}
75
+ end
76
+
77
+ e.new.before_save
78
+ $adds.should == ['byiyie']
79
+
80
+ $adds = []
81
+ e = Class.new(Sequel::Model) do
82
+ def bye; $adds << 'bye'; end
83
+ before_save(:bye){$adds << 'byiyie'}
84
+ before_save :bye
85
+ end
86
+
87
+ e.new.before_save
88
+ $adds.should == ['bye']
89
+ end
63
90
 
64
91
  specify "should be inheritable" do
65
92
  # pending
@@ -155,12 +182,12 @@ describe "Model#before_create && Model#after_create" do
155
182
  columns :x
156
183
  no_primary_key
157
184
 
158
- before_create {MODEL_DB << "BLAH before"}
159
185
  after_create {MODEL_DB << "BLAH after"}
160
186
  end
161
187
  end
162
188
 
163
189
  specify "should be called around new record creation" do
190
+ @c.before_create {MODEL_DB << "BLAH before"}
164
191
  @c.create(:x => 2)
165
192
  MODEL_DB.sqls.should == [
166
193
  'BLAH before',
@@ -168,6 +195,12 @@ describe "Model#before_create && Model#after_create" do
168
195
  'BLAH after'
169
196
  ]
170
197
  end
198
+
199
+ specify "should cancel the save if before_create returns false" do
200
+ @c.before_create{false}
201
+ @c.create(:x => 2).should == false
202
+ MODEL_DB.sqls.should == []
203
+ end
171
204
  end
172
205
 
173
206
  describe "Model#before_update && Model#after_update" do
@@ -175,12 +208,12 @@ describe "Model#before_update && Model#after_update" do
175
208
  MODEL_DB.reset
176
209
 
177
210
  @c = Class.new(Sequel::Model(:items)) do
178
- before_update {MODEL_DB << "BLAH before"}
179
211
  after_update {MODEL_DB << "BLAH after"}
180
212
  end
181
213
  end
182
214
 
183
215
  specify "should be called around record update" do
216
+ @c.before_update {MODEL_DB << "BLAH before"}
184
217
  m = @c.load(:id => 2233)
185
218
  m.save
186
219
  MODEL_DB.sqls.should == [
@@ -189,6 +222,12 @@ describe "Model#before_update && Model#after_update" do
189
222
  'BLAH after'
190
223
  ]
191
224
  end
225
+
226
+ specify "should cancel the save if before_update returns false" do
227
+ @c.before_update{false}
228
+ @c.load(:id => 2233).save.should == false
229
+ MODEL_DB.sqls.should == []
230
+ end
192
231
  end
193
232
 
194
233
  describe "Model#before_save && Model#after_save" do
@@ -221,6 +260,12 @@ describe "Model#before_save && Model#after_save" do
221
260
  'BLAH after'
222
261
  ]
223
262
  end
263
+
264
+ specify "should cancel the save if before_save returns false" do
265
+ @c.before_save{false}
266
+ @c.load(:id => 2233).save.should == false
267
+ MODEL_DB.sqls.should == ["BLAH before"]
268
+ end
224
269
  end
225
270
 
226
271
  describe "Model#before_destroy && Model#after_destroy" do
@@ -228,7 +273,6 @@ describe "Model#before_destroy && Model#after_destroy" do
228
273
  MODEL_DB.reset
229
274
 
230
275
  @c = Class.new(Sequel::Model(:items)) do
231
- before_destroy {MODEL_DB << "BLAH before"}
232
276
  after_destroy {MODEL_DB << "BLAH after"}
233
277
 
234
278
  def delete
@@ -237,7 +281,8 @@ describe "Model#before_destroy && Model#after_destroy" do
237
281
  end
238
282
  end
239
283
 
240
- specify "should be called around record update" do
284
+ specify "should be called around record destruction" do
285
+ @c.before_destroy {MODEL_DB << "BLAH before"}
241
286
  m = @c.new(:id => 2233)
242
287
  m.destroy
243
288
  MODEL_DB.sqls.should == [
@@ -246,11 +291,66 @@ describe "Model#before_destroy && Model#after_destroy" do
246
291
  'BLAH after'
247
292
  ]
248
293
  end
294
+
295
+ specify "should cancel the destroy if before_destroy returns false" do
296
+ @c.before_destroy{false}
297
+ @c.load(:id => 2233).destroy.should == false
298
+ MODEL_DB.sqls.should == []
299
+ end
300
+ end
301
+
302
+ describe "Model#before_validation && Model#after_validation" do
303
+ setup do
304
+ MODEL_DB.reset
305
+
306
+ @c = Class.new(Sequel::Model(:items)) do
307
+ before_validation{MODEL_DB << "BLAH before"}
308
+ after_validation{MODEL_DB << "BLAH after"}
309
+
310
+ def self.validate(o)
311
+ o.errors[:id] << 'not valid' unless o[:id] == 2233
312
+ end
313
+
314
+ def save!(*columns)
315
+ MODEL_DB << "CREATE BLAH"
316
+ self
317
+ end
318
+ columns :id
319
+ end
320
+ end
321
+
322
+ specify "should be called around validation" do
323
+ m = @c.new(:id => 2233)
324
+ m.should be_valid
325
+ MODEL_DB.sqls.should == ['BLAH before', 'BLAH after']
326
+
327
+ MODEL_DB.sqls.clear
328
+ m = @c.new(:id => 22)
329
+ m.should_not be_valid
330
+ MODEL_DB.sqls.should == ['BLAH before', 'BLAH after']
331
+ end
332
+
333
+ specify "should be called when calling save" do
334
+ m = @c.new(:id => 2233)
335
+ m.save.should == m
336
+ MODEL_DB.sqls.should == ['BLAH before', 'BLAH after', 'CREATE BLAH']
337
+
338
+ MODEL_DB.sqls.clear
339
+ m = @c.new(:id => 22)
340
+ m.save.should == false
341
+ MODEL_DB.sqls.should == ['BLAH before', 'BLAH after']
342
+ end
343
+
344
+ specify "should cancel the save if before_validation returns false" do
345
+ @c.before_validation{false}
346
+ @c.load(:id => 2233).save.should == false
347
+ MODEL_DB.sqls.should == ["BLAH before"]
348
+ end
249
349
  end
250
350
 
251
- describe "Model#has_hooks?" do
351
+ describe "Model.has_hooks?" do
252
352
  setup do
253
- @c = Class.new(Sequel::Model)
353
+ @c = Class.new(Sequel::Model(:items))
254
354
  end
255
355
 
256
356
  specify "should return false if no hooks are defined" do