m4dbi 0.6.2 → 0.7.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/hash.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'spec/helper'
1
+ require_relative 'helper'
2
2
 
3
3
  describe 'Hash' do
4
4
  it 'is convertible to an SQL subclause and matching value Array' do
@@ -11,12 +11,12 @@ describe 'Hash' do
11
11
  }
12
12
  clause, values = h.to_clause( " AND " )
13
13
  where_clause, where_values = h.to_where_clause
14
-
14
+
15
15
  str = "a = ? AND b = ? AND the_nil = ? AND abc = ? AND xyz = ?"
16
16
  where_str = "a = ? AND b = ? AND the_nil IS NULL AND abc = ? AND xyz = ?"
17
17
  clause.length.should.equal str.length
18
18
  where_clause.length.should.equal where_str.length
19
-
19
+
20
20
  h.each_key do |key|
21
21
  clause.should.match /#{key} = ?/
22
22
  if h[ key ].nil?
@@ -25,11 +25,11 @@ describe 'Hash' do
25
25
  where_clause.should.match /#{key} = ?/
26
26
  end
27
27
  end
28
-
28
+
29
29
  values.should.equal h.values
30
30
  where_values.should.equal h.values.compact
31
31
  end
32
-
32
+
33
33
  it 'offers a means to get subhashes via #slice' do
34
34
  h = { :a => 1, :b => 2, :c => 3, :d => 4, :e => 5, :f => 6 }
35
35
  h.slice( :b, :c ).should.equal(
data/spec/helper.rb CHANGED
@@ -12,21 +12,33 @@ $LOAD_PATH.unshift(
12
12
 
13
13
  require 'm4dbi'
14
14
 
15
- puts "DBI version: #{DBI::VERSION}"
16
15
  puts "M4DBI version: #{M4DBI_VERSION}"
17
16
 
18
17
  # See test-schema*.sql and test-data.sql
19
18
  def connect_to_spec_database( database = ( ENV[ 'M4DBI_DATABASE' ] || 'm4dbi' ) )
20
- driver = ENV[ 'M4DBI_DRIVER' ] || "DBI:Pg"
21
- puts "Using DBI driver: '#{driver}'"
22
- DBI.connect( "#{driver}:#{database}", "m4dbi", "m4dbi" )
19
+ driver = ENV[ 'M4DBI_DRIVER' ] || "PostgreSQL"
20
+ # puts "\nUsing RDBI driver: '#{driver}'"
21
+ case driver.downcase
22
+ when 'postgresql', 'sqlite3', 'mysql'
23
+ require "rdbi/driver/#{driver.downcase}"
24
+ else
25
+ raise "Unrecognized RDBI driver: #{driver}"
26
+ end
27
+
28
+ M4DBI.connect(
29
+ driver,
30
+ :database => database,
31
+ :username => 'm4dbi',
32
+ :hostname => 'localhost',
33
+ :password => 'm4dbi'
34
+ )
23
35
  end
24
36
 
25
37
  def reset_data( dbh = $dbh, datafile = "test-data.sql" )
26
38
  dir = File.dirname( __FILE__ )
27
39
  File.read( "#{dir}/#{datafile}" ).split( /;/ ).each do |command|
28
- if not command.strip.empty?
29
- dbh.do command
40
+ if ! command.strip.empty?
41
+ dbh.execute command
30
42
  end
31
43
  end
32
44
  end
data/spec/model.rb CHANGED
@@ -1,13 +1,18 @@
1
- require 'spec/helper'
1
+ require_relative 'helper'
2
2
 
3
- describe 'DBI::Model' do
3
+ describe 'M4DBI::Model' do
4
4
  it 'raises an exception when trying to define a model before connecting to a database' do
5
- dbh = DBI::DatabaseHandle.last_handle
6
- if dbh and dbh.respond_to? :disconnect
5
+ dbh = M4DBI.last_dbh
6
+ if dbh && dbh.respond_to?( :disconnect )
7
7
  dbh.disconnect
8
+ dbh.should.not.be.connected
8
9
  end
9
- should.raise do
10
- @m_author = Class.new( DBI::Model( :authors ) )
10
+ dbh = M4DBI.last_dbh
11
+ if dbh
12
+ dbh.should.not.be.connected
13
+ end
14
+ should.raise( M4DBI::Error ) do
15
+ @m_author = Class.new( M4DBI::Model( :authors ) )
11
16
  end
12
17
  end
13
18
  end
@@ -15,7 +20,7 @@ end
15
20
  $dbh = connect_to_spec_database
16
21
  reset_data
17
22
 
18
- class ManyCol < DBI::Model( :many_col_table )
23
+ class ManyCol < M4DBI::Model( :many_col_table )
19
24
  def inc
20
25
  self.c1 = c1 + 10
21
26
  end
@@ -25,18 +30,18 @@ class ManyCol < DBI::Model( :many_col_table )
25
30
  end
26
31
  end
27
32
 
28
- describe 'A DBI::Model subclass' do
33
+ describe 'A M4DBI::Model subclass' do
29
34
  before do
30
- # Here we subclass DBI::Model.
35
+ # Here we subclass M4DBI::Model.
31
36
  # This is nearly equivalent to the typical "ChildClassName < ParentClassName"
32
37
  # syntax, but allows us to refer to the class in our specs.
33
- @m_author = Class.new( DBI::Model( :authors ) )
34
- @m_post = Class.new( DBI::Model( :posts ) )
35
- @m_empty = Class.new( DBI::Model( :empty_table ) )
36
- @m_mc = Class.new( DBI::Model( :many_col_table ) )
37
- @m_nipk = Class.new( DBI::Model( :non_id_pk, :pk => [ :str ] ) )
38
- @m_mcpk = Class.new( DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
39
- class Author < DBI::Model( :authors ); end
38
+ @m_author = Class.new( M4DBI::Model( :authors ) )
39
+ @m_post = Class.new( M4DBI::Model( :posts ) )
40
+ @m_empty = Class.new( M4DBI::Model( :empty_table ) )
41
+ @m_mc = Class.new( M4DBI::Model( :many_col_table ) )
42
+ @m_nipk = Class.new( M4DBI::Model( :non_id_pk, :pk => [ :str ] ) )
43
+ @m_mcpk = Class.new( M4DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
44
+ class Author < M4DBI::Model( :authors ); end
40
45
  end
41
46
 
42
47
  it 'can be defined' do
@@ -46,16 +51,16 @@ describe 'A DBI::Model subclass' do
46
51
 
47
52
  it 'maintains identity across different inheritances' do
48
53
  should.not.raise do
49
- class Author < DBI::Model( :authors ); end
50
- class Author < DBI::Model( :authors ); end
54
+ class Author < M4DBI::Model( :authors ); end
55
+ class Author < M4DBI::Model( :authors ); end
51
56
  end
52
57
  end
53
58
 
54
59
  it 'maintains member methods across redefinitions' do
55
- class Author < DBI::Model( :authors )
60
+ class Author < M4DBI::Model( :authors )
56
61
  def method1; 1; end
57
62
  end
58
- class Author < DBI::Model( :authors )
63
+ class Author < M4DBI::Model( :authors )
59
64
  def method2; 2; end
60
65
  end
61
66
  a = Author[ 3 ]
@@ -67,16 +72,29 @@ describe 'A DBI::Model subclass' do
67
72
  # If you try to subclass a class a second time with a different parent class,
68
73
  # Ruby raises an exception.
69
74
  should.not.raise do
70
- original_handle = DBI::DatabaseHandle.last_handle
75
+ original_handle = M4DBI.last_dbh
71
76
 
72
- class Author < DBI::Model( :authors ); end
77
+ class Author < M4DBI::Model( :authors ); end
73
78
 
74
79
  dbh = connect_to_spec_database
75
- new_handle = DBI::DatabaseHandle.last_handle
80
+ new_handle = M4DBI.last_dbh
76
81
  new_handle.should.equal dbh
77
82
  new_handle.should.not.equal original_handle
78
83
 
79
- class Author < DBI::Model( :authors ); end
84
+ class Author < M4DBI::Model( :authors ); end
85
+ end
86
+ end
87
+
88
+ it 'provides the database handle it is using' do
89
+ begin
90
+ @m_author.dbh.should.equal $dbh
91
+
92
+ dbh = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
93
+ @m_author2 = Class.new( M4DBI::Model( :authors ) )
94
+ @m_author2.dbh.should.equal dbh
95
+ ensure
96
+ # Clean up handles for later specs
97
+ connect_to_spec_database
80
98
  end
81
99
  end
82
100
 
@@ -89,8 +107,8 @@ describe 'A DBI::Model subclass' do
89
107
  dbh = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
90
108
  reset_data( dbh, "test-data2.sql" )
91
109
 
92
- @m_author2 = Class.new( DBI::Model( :authors ) )
93
- @m_author2.dbh.should.equal dbh
110
+ @m_author2 = Class.new( M4DBI::Model( :authors ) )
111
+ @m_author2.dbh.should.not.equal @m_author.dbh
94
112
 
95
113
  @m_author2[ 1 ].should.be.nil
96
114
  a11 = @m_author2[ 11 ]
@@ -111,15 +129,15 @@ describe 'A DBI::Model subclass' do
111
129
  it 'can use a specific database handle' do
112
130
  begin
113
131
  dbh1 = connect_to_spec_database
114
- dbh1.should.equal DBI::DatabaseHandle.last_handle
132
+ dbh1.should.equal M4DBI.last_dbh
115
133
  dbh2 = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
116
- dbh2.should.equal DBI::DatabaseHandle.last_handle
134
+ dbh2.should.equal M4DBI.last_dbh
117
135
  reset_data( dbh2, "test-data2.sql" )
118
136
 
119
137
  dbh1.should.not.equal dbh2
120
138
 
121
- class Author1 < DBI::Model( :authors, :dbh => dbh1 ); end
122
- class Author2 < DBI::Model( :authors, :dbh => dbh2 ); end
139
+ class Author1 < M4DBI::Model( :authors, :dbh => dbh1 ); end
140
+ class Author2 < M4DBI::Model( :authors, :dbh => dbh2 ); end
123
141
 
124
142
  a1 = Author1[ 1 ]
125
143
  a1.should.not.be.nil
@@ -135,13 +153,13 @@ describe 'A DBI::Model subclass' do
135
153
  end
136
154
 
137
155
  it 'raises an exception when creating with invalid arguments' do
138
- should.raise( DBI::Error ) do
156
+ should.raise( M4DBI::Error ) do
139
157
  @m_author.new nil
140
158
  end
141
- should.raise( DBI::Error ) do
159
+ should.raise( M4DBI::Error ) do
142
160
  @m_author.new 2
143
161
  end
144
- should.raise( DBI::Error ) do
162
+ should.raise( M4DBI::Error ) do
145
163
  @m_author.new Object.new
146
164
  end
147
165
  end
@@ -184,7 +202,7 @@ describe 'A DBI::Model subclass' do
184
202
  o.class.should.equal @m_mcpk
185
203
  o.val.should.equal 'five six'
186
204
 
187
- should.not.raise( DBI::Error ) do
205
+ should.not.raise( RDBI::Error ) do
188
206
  o = @m_author[ nil ]
189
207
  o.should.be.nil
190
208
  end
@@ -194,7 +212,7 @@ describe 'A DBI::Model subclass' do
194
212
  o = @m_author[ :name => 'author1' ]
195
213
  o.should.not.be.nil
196
214
  o.class.should.equal @m_author
197
- o.id.should.equal 1
215
+ o[ 'id' ].should.equal 1
198
216
 
199
217
  o = @m_post[ :author_id => 1 ]
200
218
  o.should.not.be.nil
@@ -228,7 +246,7 @@ describe 'A DBI::Model subclass' do
228
246
  posts[ 0 ].class.should.equal @m_post
229
247
 
230
248
  sorted_posts = posts.sort { |p1,p2|
231
- p1._id <=> p2._id
249
+ p1[ 'id' ] <=> p2[ 'id' ]
232
250
  }
233
251
  p = sorted_posts.first
234
252
  p.text.should.equal 'First post.'
@@ -260,7 +278,7 @@ describe 'A DBI::Model subclass' do
260
278
  posts[ 0 ].class.should.equal @m_post
261
279
 
262
280
  sorted_posts = posts.sort { |p1,p2|
263
- p2._id <=> p1._id
281
+ p2[ 'id' ] <=> p1[ 'id' ]
264
282
  }
265
283
  p = sorted_posts.first
266
284
  p.text.should.equal 'Second post.'
@@ -274,7 +292,7 @@ describe 'A DBI::Model subclass' do
274
292
  posts[ 0 ].class.should.equal @m_post
275
293
 
276
294
  sorted_posts = posts.sort { |p1,p2|
277
- p2._id <=> p1._id
295
+ p2[ 'id' ] <=> p1[ 'id' ]
278
296
  }
279
297
  p = sorted_posts.first
280
298
  p.text.should.equal 'Second post.'
@@ -305,14 +323,14 @@ describe 'A DBI::Model subclass' do
305
323
  post = @m_post.one_where( "text LIKE '%Third%'" )
306
324
  post.should.not.be.nil
307
325
  post.class.should.equal @m_post
308
- post.id.should.equal 3
326
+ post[ 'id' ].should.equal 3
309
327
  end
310
328
 
311
329
  it 'provides single-record access via #one_where( String, param, param... )' do
312
330
  post = @m_post.one_where( "text LIKE ?", '%Third%' )
313
331
  post.should.not.be.nil
314
332
  post.class.should.equal @m_post
315
- post.id.should.equal 3
333
+ post[ 'id' ].should.equal 3
316
334
  end
317
335
 
318
336
  it 'returns nil from #one_where when no record is found' do
@@ -329,10 +347,10 @@ describe 'A DBI::Model subclass' do
329
347
  rows.should.not.be.empty
330
348
  rows.size.should.equal 3
331
349
 
332
- rows[ 0 ].id.should.equal 1
350
+ rows[ 0 ][ 'id' ].should.equal 1
333
351
  rows[ 0 ].class.should.equal @m_author
334
352
  rows[ 0 ].name.should.equal 'author1'
335
- rows[ 1 ].id.should.equal 2
353
+ rows[ 1 ][ 'id' ].should.equal 2
336
354
  rows[ 1 ].class.should.equal @m_author
337
355
  rows[ 1 ].name.should.equal 'author2'
338
356
  end
@@ -360,34 +378,48 @@ describe 'A DBI::Model subclass' do
360
378
  end
361
379
 
362
380
  it 'provides a means to create new records via #create( Hash )' do
381
+ num_authors = @m_author.count
382
+
363
383
  a = @m_author.create(
364
- :id => 9,
365
- :name => 'author9'
384
+ :id => 99,
385
+ :name => 'author99'
366
386
  )
367
387
  a.should.not.be.nil
368
388
  a.class.should.equal @m_author
369
- a.id.should.equal 9
389
+ a[ 'id' ].should.equal 99
370
390
  a.should.respond_to :name
371
391
  a.should.not.respond_to :no_column_by_this_name
372
- a.name.should.equal 'author9'
392
+ a.name.should.equal 'author99'
373
393
 
374
- a_ = @m_author[ 9 ]
394
+ a_ = @m_author[ 99 ]
375
395
  a_.should.not.be.nil
376
396
  a_.should.equal a
377
- a_.name.should.equal 'author9'
378
-
379
- # No value given for auto-incrementing primary key
380
- a = @m_author.create(
381
- :name => 'author10'
382
- )
397
+ a_.name.should.equal 'author99'
398
+
399
+ # Insert without auto-incrementing primary key specified
400
+ # Try at least as many times as there were records in the DB,
401
+ # because the sequence used for the IDs is independent of
402
+ # the actual ID values in the DB for some RDBMSes.
403
+ num_authors.times do
404
+ begin
405
+ a = @m_author.create(
406
+ :name => 'author10'
407
+ )
408
+ break # Stop on success
409
+ rescue Exception => e
410
+ if e.message !~ /duplicate/
411
+ raise e
412
+ end
413
+ end
414
+ end
383
415
  a.should.not.be.nil
384
416
  a.class.should.equal @m_author
385
- a.id.should.not.equal 9
417
+ a[ 'id' ].should.not.be.nil
386
418
  a.should.respond_to :name
387
419
  a.should.not.respond_to :no_column_by_this_name
388
420
  a.name.should.equal 'author10'
389
421
 
390
- a_ = @m_author[ a.id ]
422
+ a_ = @m_author[ a[ 'id' ] ]
391
423
  a_.should.not.be.nil
392
424
  a_.should.equal a
393
425
  a_.name.should.equal 'author10'
@@ -403,12 +435,12 @@ describe 'A DBI::Model subclass' do
403
435
  end
404
436
 
405
437
  a = @m_author.create { |rec|
406
- rec.id = 9
438
+ rec[ 'id' ] = 9
407
439
  rec.name = 'author9'
408
440
  }
409
441
  a.should.not.be.nil
410
442
  a.class.should.equal @m_author
411
- a.id.should.equal 9
443
+ a[ 'id' ].should.equal 9
412
444
  a.name.should.equal 'author9'
413
445
 
414
446
  a_ = @m_author[ 9 ]
@@ -418,13 +450,13 @@ describe 'A DBI::Model subclass' do
418
450
  m = nil
419
451
  should.not.raise do
420
452
  m = @m_mc.create { |rec|
421
- rec.id = 1
453
+ rec[ 'id' ] = 1
422
454
  rec.c2 = 7
423
455
  rec.c3 = 8
424
456
  }
425
457
  end
426
458
  m_ = @m_mc[ 1 ]
427
- m_.id.should.equal 1
459
+ m_[ 'id' ].should.equal 1
428
460
  m_.c1.should.be.nil
429
461
  m_.c2.should.equal 7
430
462
  m_.c3.should.equal 8
@@ -442,7 +474,7 @@ describe 'A DBI::Model subclass' do
442
474
  )
443
475
  a.should.not.be.nil
444
476
  a.class.should.equal @m_author
445
- a.id.should.equal 1
477
+ a[ 'id' ].should.equal 1
446
478
  a.should.respond_to :name
447
479
  a.should.not.respond_to :no_column_by_this_name
448
480
  a.name.should.equal 'author1'
@@ -465,7 +497,7 @@ describe 'A DBI::Model subclass' do
465
497
  )
466
498
  a.should.not.be.nil
467
499
  a.class.should.equal @m_author
468
- a.id.should.equal 9
500
+ a[ 'id' ].should.equal 9
469
501
  a.should.respond_to :name
470
502
  a.should.not.respond_to :no_column_by_this_name
471
503
  a.name.should.equal 'author9'
@@ -497,10 +529,10 @@ describe 'A DBI::Model subclass' do
497
529
  posts.should.not.be.empty
498
530
  posts.size.should.equal 2
499
531
 
500
- posts[ 0 ].id.should.equal 1
532
+ posts[ 0 ][ 'id' ].should.equal 1
501
533
  posts[ 0 ].text.should.equal 'First post.'
502
534
  posts[ 0 ].class.should.equal @m_post
503
- posts[ 1 ].id.should.equal 3
535
+ posts[ 1 ][ 'id' ].should.equal 3
504
536
  posts[ 1 ].text.should.equal 'Third post.'
505
537
  posts[ 1 ].class.should.equal @m_post
506
538
 
@@ -529,7 +561,7 @@ describe 'A DBI::Model subclass' do
529
561
  post.should.not.be.nil
530
562
  post.class.should.equal @m_post
531
563
 
532
- post.id.should.equal 3
564
+ post[ 'id' ].should.equal 3
533
565
  post.author_id.should.equal 1
534
566
  post.text.should.equal 'Third post.'
535
567
 
@@ -619,11 +651,12 @@ describe 'A DBI::Model subclass' do
619
651
  end
620
652
  end
621
653
 
622
- describe 'A created DBI::Model subclass instance' do
654
+ describe 'A created M4DBI::Model subclass instance' do
623
655
  before do
624
- @m_mc = Class.new( DBI::Model( :many_col_table ) )
625
- @m_author = Class.new( DBI::Model( :authors ) )
626
- @m_post = Class.new( DBI::Model( :posts ) )
656
+ @m_mc = Class.new( M4DBI::Model( :many_col_table ) )
657
+ @m_author = Class.new( M4DBI::Model( :authors ) )
658
+ @m_post = Class.new( M4DBI::Model( :posts ) )
659
+ @m_conflict = Class.new( M4DBI::Model( :conflicting_cols ) )
627
660
  end
628
661
 
629
662
  it 'provides read access to fields via identically-named readers' do
@@ -633,12 +666,12 @@ describe 'A created DBI::Model subclass instance' do
633
666
  )
634
667
  mc.should.not.be.nil
635
668
  should.not.raise do
636
- mc.id
669
+ mc[ 'id' ]
637
670
  mc.c1
638
671
  mc.c2
639
672
  mc.c5
640
673
  end
641
- mc.id.should.not.be.nil
674
+ mc[ 'id' ].should.not.be.nil
642
675
  mc.c3.should.equal 3
643
676
  mc.c4.should.equal 4
644
677
  end
@@ -655,26 +688,86 @@ describe 'A created DBI::Model subclass instance' do
655
688
  mc.c2.should.equal 20
656
689
  mc.c3.should.equal 30
657
690
  mc.c4.should.equal 40
658
- id_ = mc.id
691
+ id_ = mc[ 'id' ]
659
692
  id_.should.not.be.nil
660
693
 
661
694
  mc_ = @m_mc[ id_ ]
662
- mc_.id.should.equal id_
695
+ mc_[ 'id' ].should.equal id_
663
696
  mc_.c1.should.equal 10
664
697
  mc_.c2.should.equal 20
665
698
  mc_.c3.should.equal 30
666
699
  mc_.c4.should.equal 40
667
700
  end
668
701
 
702
+ it 'provides read access to fields via Hash-like syntax' do
703
+ mc = @m_mc.create(
704
+ :c3 => 3,
705
+ :c4 => 4
706
+ )
707
+ mc.should.not.be.nil
708
+ mc[ 'id' ].should.not.be.nil
709
+ mc[ 'c3' ].should.equal 3
710
+ mc[ 'c4' ].should.equal 4
711
+ mc[ :id ].should.not.be.nil
712
+ mc[ :c3 ].should.equal 3
713
+ mc[ :c4 ].should.equal 4
714
+ end
715
+
716
+ it 'provides write access to fields via Hash-like syntax' do
717
+ mc = @m_mc.create(
718
+ :c3 => 30,
719
+ :c4 => 40
720
+ )
721
+ mc.should.not.be.nil
722
+ mc[ 'c1' ] = 10
723
+ mc[ 'c2' ] = 20
724
+ mc[ 'c1' ].should.equal 10
725
+ mc[ 'c2' ].should.equal 20
726
+ mc[ 'c3' ].should.equal 30
727
+ mc[ 'c4' ].should.equal 40
728
+ id_ = mc[ 'id' ]
729
+ id_.should.not.be.nil
730
+
731
+ mc_ = @m_mc[ id_ ]
732
+ mc_[ 'id' ].should.equal id_
733
+ mc_[ 'c1' ].should.equal 10
734
+ mc_[ 'c2' ].should.equal 20
735
+ mc_[ 'c3' ].should.equal 30
736
+ mc_[ 'c4' ].should.equal 40
737
+ end
738
+
739
+ it 'provides alternative accessors for columns that collide with Object methods' do
740
+ mc = @m_conflict.create(
741
+ :c1 => 123,
742
+ :class => 'Mammalia',
743
+ :dup => false
744
+ )
745
+ mc.should.not.be.nil
746
+ should.not.raise do
747
+ mc[ 'id' ]
748
+ mc.class
749
+ mc.class_
750
+ mc.dup
751
+ mc.dup_
752
+ end
753
+ mc[ 'id' ].should.not.be.nil
754
+ mc.c1.should.equal 123
755
+ mc.class.should.equal @m_conflict
756
+ mc.class_.should.equal 'Mammalia'
757
+ mc.dup.should.equal mc
758
+ end
759
+
669
760
  it 'maintains Hash key equality across different fetches' do
670
761
  h = Hash.new
671
762
  a = @m_author[ 1 ]
672
763
  h[ a ] = 123
673
764
  a_ = @m_author[ 1 ]
674
765
  h[ a_].should.equal 123
766
+ a.should.equal a_
675
767
 
676
768
  a2 = @m_author[ 2 ]
677
769
  h[ a2 ].should.be.nil
770
+ a2.should.not.equal a
678
771
 
679
772
  h[ a2 ] = 456
680
773
  h[ a ].should.equal 123
@@ -682,6 +775,7 @@ describe 'A created DBI::Model subclass instance' do
682
775
 
683
776
  a2_ = @m_author[ 2 ]
684
777
  h[ a2_ ].should.equal 456
778
+ a2.should.equal a2_
685
779
  end
686
780
 
687
781
  it 'maintains Hash key distinction for different Model subclasses' do
@@ -697,13 +791,13 @@ describe 'A created DBI::Model subclass instance' do
697
791
  end
698
792
  end
699
793
 
700
- describe 'A found DBI::Model subclass instance' do
794
+ describe 'A found M4DBI::Model subclass instance' do
701
795
  before do
702
- @m_author = Class.new( DBI::Model( :authors ) )
703
- @m_post = Class.new( DBI::Model( :posts ) )
704
- @m_mc = Class.new( DBI::Model( :many_col_table ) )
705
- @m_nipk = Class.new( DBI::Model( :non_id_pk, :pk => [ :str ] ) )
706
- @m_mcpk = Class.new( DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
796
+ @m_author = Class.new( M4DBI::Model( :authors ) )
797
+ @m_post = Class.new( M4DBI::Model( :posts ) )
798
+ @m_mc = Class.new( M4DBI::Model( :many_col_table ) )
799
+ @m_nipk = Class.new( M4DBI::Model( :non_id_pk, :pk => [ :str ] ) )
800
+ @m_mcpk = Class.new( M4DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
707
801
  end
708
802
 
709
803
  it 'provides access to primary key value' do
@@ -724,7 +818,7 @@ describe 'A found DBI::Model subclass instance' do
724
818
  p = @m_post[ 2 ]
725
819
 
726
820
  should.not.raise( NoMethodError ) do
727
- p.id
821
+ p[ 'id' ]
728
822
  p.author_id
729
823
  p.text
730
824
  end
@@ -733,7 +827,7 @@ describe 'A found DBI::Model subclass instance' do
733
827
  p.foobar
734
828
  end
735
829
 
736
- p.id.should.equal 2
830
+ p[ 'id' ].should.equal 2
737
831
  p.author_id.should.equal 2
738
832
  p.text.should.equal 'Second post.'
739
833
  end
@@ -831,38 +925,38 @@ describe 'A found DBI::Model subclass instance' do
831
925
 
832
926
  end
833
927
 
834
- describe 'DBI::Model (relationships)' do
928
+ describe 'M4DBI::Model (relationships)' do
835
929
  before do
836
- @m_author = Class.new( DBI::Model( :authors ) )
837
- @m_post = Class.new( DBI::Model( :posts ) )
838
- @m_fan = Class.new( DBI::Model( :fans ) )
930
+ @m_author = Class.new( M4DBI::Model( :authors ) )
931
+ @m_post = Class.new( M4DBI::Model( :posts ) )
932
+ @m_fan = Class.new( M4DBI::Model( :fans ) )
839
933
  end
840
934
 
841
935
  it 'facilitates relating one to many, providing read access' do
842
- DBI::Model.one_to_many( @m_author, @m_post, :posts, :author, :author_id )
936
+ M4DBI::Model.one_to_many( @m_author, @m_post, :posts, :author, :author_id )
843
937
  a = @m_author[ 1 ]
844
938
  a.posts.should.not.be.empty
845
939
  p = @m_post[ 3 ]
846
940
  p.author.should.not.be.nil
847
- p.author.id.should.equal 1
941
+ p.author[ 'id' ].should.equal 1
848
942
  end
849
943
 
850
944
  it 'facilitates relating one to many, allowing one of the many to set its one' do
851
- DBI::Model.one_to_many(
945
+ M4DBI::Model.one_to_many(
852
946
  @m_author, @m_post, :posts, :author, :author_id
853
947
  )
854
948
  p = @m_post[ 3 ]
855
949
  p.author.should.not.be.nil
856
- p.author.id.should.equal 1
950
+ p.author[ 'id' ].should.equal 1
857
951
  p.author = @m_author.create( :id => 4, :name => 'author4' )
858
952
  p_ = @m_post[ 3 ]
859
- p_.author.id.should.equal 4
953
+ p_.author[ 'id' ].should.equal 4
860
954
 
861
955
  reset_data
862
956
  end
863
957
 
864
958
  it 'facilitates relating many to many, providing read access' do
865
- DBI::Model.many_to_many(
959
+ M4DBI::Model.many_to_many(
866
960
  @m_author, @m_fan, :authors_liked, :fans, :authors_fans, :author_id, :fan_id
867
961
  )
868
962
  a1 = @m_author[ 1 ]
@@ -908,20 +1002,41 @@ describe 'DBI::Model (relationships)' do
908
1002
  end
909
1003
  end
910
1004
 
911
- describe 'DBI::Collection' do
1005
+ describe 'M4DBI::Collection' do
912
1006
  before do
913
- @m_author = Class.new( DBI::Model( :authors ) )
914
- @m_post = Class.new( DBI::Model( :posts ) )
1007
+ @m_author = Class.new( M4DBI::Model( :authors ) )
1008
+ @m_post = Class.new( M4DBI::Model( :posts ) )
915
1009
 
916
- DBI::Model.one_to_many(
1010
+ M4DBI::Model.one_to_many(
917
1011
  @m_author, @m_post, :posts, :author, :author_id
918
1012
  )
919
1013
  end
920
1014
 
921
1015
  it 'accepts additions' do
1016
+ num_posts = @m_post.count
1017
+
922
1018
  a = @m_author[ 1 ]
923
1019
  the_text = 'A new post.'
924
- a.posts << { :text => the_text }
1020
+
1021
+ num_posts_of_author = a.posts.count
1022
+
1023
+ # Insert without auto-incrementing primary key specified
1024
+ # Try at least as many times as there were records in the DB,
1025
+ # because the sequence used for the IDs is independent of
1026
+ # the actual ID values in the DB for some RDBMSes.
1027
+ num_posts.times do
1028
+ begin
1029
+ a.posts << { :text => the_text }
1030
+ break # Stop on success
1031
+ rescue Exception => e
1032
+ if e.message !~ /duplicate/
1033
+ raise e
1034
+ end
1035
+ end
1036
+ end
1037
+
1038
+ a.posts.count.should.equal num_posts_of_author + 1
1039
+
925
1040
  p = a.posts.find { |p| p.text == the_text }
926
1041
  p.should.not.be.nil
927
1042
  p.author.should.equal a
@@ -965,4 +1080,4 @@ describe 'DBI::Collection' do
965
1080
 
966
1081
  reset_data
967
1082
  end
968
- end
1083
+ end