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