sequel 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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