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/{READHIM → README} +0 -0
- data/lib/m4dbi.rb +16 -4
- data/lib/m4dbi/collection.rb +7 -8
- data/lib/m4dbi/database.rb +73 -0
- data/lib/m4dbi/error.rb +4 -0
- data/lib/m4dbi/model.rb +55 -41
- data/spec/database.rb +84 -0
- data/spec/hash.rb +5 -5
- data/spec/helper.rb +18 -6
- data/spec/model.rb +211 -96
- metadata +25 -35
- data/HIM +0 -50
- data/lib/m4dbi/database-handle.rb +0 -117
- data/lib/m4dbi/row.rb +0 -35
- data/lib/m4dbi/timestamp.rb +0 -20
- data/spec/dbi.rb +0 -142
data/spec/hash.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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' ] || "
|
21
|
-
puts "
|
22
|
-
|
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
|
29
|
-
dbh.
|
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
|
-
|
1
|
+
require_relative 'helper'
|
2
2
|
|
3
|
-
describe '
|
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 =
|
6
|
-
if dbh
|
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
|
-
|
10
|
-
|
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 <
|
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
|
33
|
+
describe 'A M4DBI::Model subclass' do
|
29
34
|
before do
|
30
|
-
# Here we subclass
|
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(
|
34
|
-
@m_post = Class.new(
|
35
|
-
@m_empty = Class.new(
|
36
|
-
@m_mc = Class.new(
|
37
|
-
@m_nipk = Class.new(
|
38
|
-
@m_mcpk = Class.new(
|
39
|
-
class Author <
|
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 <
|
50
|
-
class Author <
|
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 <
|
60
|
+
class Author < M4DBI::Model( :authors )
|
56
61
|
def method1; 1; end
|
57
62
|
end
|
58
|
-
class Author <
|
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 =
|
75
|
+
original_handle = M4DBI.last_dbh
|
71
76
|
|
72
|
-
class Author <
|
77
|
+
class Author < M4DBI::Model( :authors ); end
|
73
78
|
|
74
79
|
dbh = connect_to_spec_database
|
75
|
-
new_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 <
|
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(
|
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
|
132
|
+
dbh1.should.equal M4DBI.last_dbh
|
115
133
|
dbh2 = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
|
116
|
-
dbh2.should.equal
|
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 <
|
122
|
-
class Author2 <
|
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(
|
156
|
+
should.raise( M4DBI::Error ) do
|
139
157
|
@m_author.new nil
|
140
158
|
end
|
141
|
-
should.raise(
|
159
|
+
should.raise( M4DBI::Error ) do
|
142
160
|
@m_author.new 2
|
143
161
|
end
|
144
|
-
should.raise(
|
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(
|
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
|
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
|
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
|
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
|
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
|
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
|
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 ]
|
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 ]
|
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 =>
|
365
|
-
:name => '
|
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
|
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 '
|
392
|
+
a.name.should.equal 'author99'
|
373
393
|
|
374
|
-
a_ = @m_author[
|
394
|
+
a_ = @m_author[ 99 ]
|
375
395
|
a_.should.not.be.nil
|
376
396
|
a_.should.equal a
|
377
|
-
a_.name.should.equal '
|
378
|
-
|
379
|
-
#
|
380
|
-
|
381
|
-
|
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
|
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
|
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
|
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
|
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
|
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_
|
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
|
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
|
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 ]
|
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 ]
|
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
|
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
|
654
|
+
describe 'A created M4DBI::Model subclass instance' do
|
623
655
|
before do
|
624
|
-
@m_mc = Class.new(
|
625
|
-
@m_author = Class.new(
|
626
|
-
@m_post = Class.new(
|
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
|
669
|
+
mc[ 'id' ]
|
637
670
|
mc.c1
|
638
671
|
mc.c2
|
639
672
|
mc.c5
|
640
673
|
end
|
641
|
-
mc
|
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
|
691
|
+
id_ = mc[ 'id' ]
|
659
692
|
id_.should.not.be.nil
|
660
693
|
|
661
694
|
mc_ = @m_mc[ id_ ]
|
662
|
-
mc_
|
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
|
794
|
+
describe 'A found M4DBI::Model subclass instance' do
|
701
795
|
before do
|
702
|
-
@m_author = Class.new(
|
703
|
-
@m_post = Class.new(
|
704
|
-
@m_mc = Class.new(
|
705
|
-
@m_nipk = Class.new(
|
706
|
-
@m_mcpk = Class.new(
|
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
|
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
|
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 '
|
928
|
+
describe 'M4DBI::Model (relationships)' do
|
835
929
|
before do
|
836
|
-
@m_author = Class.new(
|
837
|
-
@m_post = Class.new(
|
838
|
-
@m_fan = Class.new(
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
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
|
-
|
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 '
|
1005
|
+
describe 'M4DBI::Collection' do
|
912
1006
|
before do
|
913
|
-
@m_author = Class.new(
|
914
|
-
@m_post = Class.new(
|
1007
|
+
@m_author = Class.new( M4DBI::Model( :authors ) )
|
1008
|
+
@m_post = Class.new( M4DBI::Model( :posts ) )
|
915
1009
|
|
916
|
-
|
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
|
-
|
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
|