m4dbi 0.5.0 → 0.6.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/CHANGELOG +17 -1
- data/HIM +6 -6
- data/LICENCE +21 -0
- data/lib/m4dbi.rb +12 -6
- data/lib/m4dbi/array.rb +5 -0
- data/lib/m4dbi/database-handle.rb +33 -2
- data/lib/m4dbi/hash.rb +33 -5
- data/lib/m4dbi/model.rb +105 -35
- data/lib/m4dbi/timestamp.rb +20 -0
- data/spec/dbi.rb +13 -3
- data/spec/hash.rb +29 -9
- data/spec/helper.rb +19 -0
- data/spec/model.rb +143 -36
- metadata +28 -14
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 0.6.0
|
2
|
+
|
3
|
+
- Compatibility with DBI 0.4.0 ensured.
|
4
|
+
- Support for multi-column primary keys added.
|
5
|
+
- MySQL support improved and spec'ed.
|
6
|
+
- SQLite support improved and spec'ed.
|
7
|
+
- MIT licence adopted.
|
8
|
+
|
9
|
+
## 0.5.5
|
10
|
+
|
11
|
+
- Improved and added numerous specs.
|
12
|
+
- More precise NoMethodError? thrown on Model instances. (r2601)
|
13
|
+
- DBI::Timestamp now behaves like Time. (r2602)
|
14
|
+
- Model instance writers now properly modify object values (not just database).
|
15
|
+
- nil values in condition clauses ([] and "where") no longer generate wrong SQL.
|
16
|
+
|
1
17
|
## 0.5.0
|
2
18
|
|
3
|
-
First official public release.
|
19
|
+
- First official public release.
|
data/HIM
CHANGED
@@ -21,17 +21,17 @@ M4DBI is a Ruby library that provides ORM modelling to the Ruby DBI package
|
|
21
21
|
|
22
22
|
==== Repository
|
23
23
|
|
24
|
-
To use the repository version, you need
|
25
|
-
Chances are, there is a
|
24
|
+
To use the repository version, you need git ( http://git.or.cz ).
|
25
|
+
Chances are, there is a git package for your Linux/UNIX flavour.
|
26
26
|
|
27
27
|
cd /where/you/want/m4dbi
|
28
|
-
|
28
|
+
git clone git://github.com/Pistos/m4dbi.git
|
29
29
|
|
30
30
|
Change the following to whatever the equivalent paths are for your system:
|
31
31
|
|
32
32
|
cd /usr/lib/ruby/site_ruby/1.8
|
33
|
-
ln -s /path/to/
|
34
|
-
ln -s /path/to/
|
33
|
+
ln -s /path/to/cloned/m4dbi/lib/m4dbi
|
34
|
+
ln -s /path/to/cloned/m4dbi/lib/m4dbi.rb
|
35
35
|
|
36
36
|
=== Usage
|
37
37
|
|
@@ -40,7 +40,7 @@ generated from the spec files under the spec/ dir.
|
|
40
40
|
|
41
41
|
=== Source Code
|
42
42
|
|
43
|
-
Browse source at http://
|
43
|
+
Browse source at http://github.com/Pistos/m4dbi/tree/master .
|
44
44
|
See coverage at http://rome.purepistos.net/m4dbi/rcov .
|
45
45
|
Very limited rdocs at http://rome.purepistos.net/m4dbi/rdoc .
|
46
46
|
|
data/LICENCE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2008-2009 Pistos
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/lib/m4dbi.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require
|
8
|
-
require
|
3
|
+
M4DBI_VERSION = '0.6.0'
|
4
|
+
|
5
|
+
__DIR__ = File.expand_path( File.dirname( __FILE__ ) )
|
6
|
+
|
7
|
+
require "#{__DIR__}/m4dbi/traits"
|
8
|
+
require "#{__DIR__}/m4dbi/hash"
|
9
|
+
require "#{__DIR__}/m4dbi/array"
|
10
|
+
require "#{__DIR__}/m4dbi/database-handle"
|
11
|
+
require "#{__DIR__}/m4dbi/row"
|
12
|
+
require "#{__DIR__}/m4dbi/timestamp"
|
13
|
+
require "#{__DIR__}/m4dbi/model"
|
14
|
+
require "#{__DIR__}/m4dbi/collection"
|
data/lib/m4dbi/array.rb
ADDED
@@ -20,17 +20,40 @@ module DBI
|
|
20
20
|
end
|
21
21
|
end; end
|
22
22
|
|
23
|
+
module DBD; module Mysql
|
24
|
+
module DatabaseNameAccessor
|
25
|
+
def dbname
|
26
|
+
select_column( "SELECT DATABASE()" )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end; end
|
30
|
+
|
31
|
+
module DBD; module SQLite3
|
32
|
+
module DatabaseNameAccessor
|
33
|
+
def dbname
|
34
|
+
select_one( "PRAGMA database_list" )[ 2 ]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end; end
|
38
|
+
|
23
39
|
class DatabaseHandle
|
40
|
+
attr_reader :transactions
|
41
|
+
|
24
42
|
alias old_initialize initialize
|
25
|
-
def initialize(
|
43
|
+
def initialize( *args )
|
26
44
|
DBI::DatabaseHandle.last_handle = self
|
27
|
-
handle = old_initialize(
|
45
|
+
handle = old_initialize( *args )
|
28
46
|
@mutex = Mutex.new
|
47
|
+
@transactions = Array.new
|
29
48
|
|
30
49
|
# Hackery to expose dbname.
|
31
50
|
if defined?( DBI::DBD::Pg::Database ) and ( DBI::DBD::Pg::Database === @handle )
|
32
51
|
@handle.extend DBI::DBD::Pg::ConnectionDatabaseNameAccessor
|
33
52
|
extend DBI::DBD::Pg::DatabaseNameAccessor
|
53
|
+
elsif defined?( DBI::DBD::Mysql::Database ) and ( DBI::DBD::Mysql::Database === @handle )
|
54
|
+
extend DBI::DBD::Mysql::DatabaseNameAccessor
|
55
|
+
elsif defined?( DBI::DBD::SQLite3::Database ) and ( DBI::DBD::SQLite3::Database === @handle )
|
56
|
+
extend DBI::DBD::SQLite3::DatabaseNameAccessor
|
34
57
|
end
|
35
58
|
# TODO: more DBDs
|
36
59
|
|
@@ -44,12 +67,18 @@ module DBI
|
|
44
67
|
# database handle.
|
45
68
|
def one_transaction
|
46
69
|
@mutex.synchronize do
|
70
|
+
# Keep track of transactions for debugging purposes
|
71
|
+
transaction = { :time => ::Time.now, :stack => caller }
|
72
|
+
@transactions << transaction
|
73
|
+
|
47
74
|
auto_commit = self[ 'AutoCommit' ]
|
48
75
|
self[ 'AutoCommit' ] = false
|
49
76
|
result = transaction do
|
50
77
|
yield self
|
51
78
|
end
|
52
79
|
self[ 'AutoCommit' ] = auto_commit
|
80
|
+
|
81
|
+
@transactions.delete transaction
|
53
82
|
result
|
54
83
|
end
|
55
84
|
end
|
@@ -58,6 +87,8 @@ module DBI
|
|
58
87
|
row = select_one( statement, *bindvars )
|
59
88
|
if row
|
60
89
|
row[ 0 ]
|
90
|
+
else
|
91
|
+
raise DBI::DataError.new( "Query returned no rows." )
|
61
92
|
end
|
62
93
|
end
|
63
94
|
|
data/lib/m4dbi/hash.rb
CHANGED
@@ -1,21 +1,49 @@
|
|
1
1
|
class Hash
|
2
|
-
|
2
|
+
COMPACT_NILS = true
|
3
|
+
DONT_COMPACT_NILS = false
|
4
|
+
|
5
|
+
# Takes an optional block to provide a single "field = ?" type subclause
|
6
|
+
# for each key-value pair.
|
7
|
+
def to_clause( join_string, compact_nils = DONT_COMPACT_NILS )
|
3
8
|
# The clause items and the values have to be in the same order.
|
4
9
|
keys_ = keys
|
5
|
-
|
6
|
-
|
7
|
-
|
10
|
+
if block_given?
|
11
|
+
mapping = keys_.map { |field| yield field }
|
12
|
+
else
|
13
|
+
mapping = keys_.map { |field| "#{field} = ?" }
|
14
|
+
end
|
15
|
+
clause = mapping.join( join_string )
|
8
16
|
values_ = keys_.map { |key|
|
9
17
|
self[ key ]
|
10
18
|
}
|
19
|
+
if compact_nils
|
20
|
+
values_.compact!
|
21
|
+
end
|
11
22
|
[ clause, values_ ]
|
12
23
|
end
|
13
24
|
|
14
25
|
def to_where_clause
|
15
|
-
to_clause( " AND " )
|
26
|
+
to_clause( " AND ", COMPACT_NILS ) { |field|
|
27
|
+
if self[ field ].nil?
|
28
|
+
"#{field} IS NULL"
|
29
|
+
else
|
30
|
+
"#{field} = ?"
|
31
|
+
end
|
32
|
+
}
|
16
33
|
end
|
17
34
|
|
18
35
|
def to_set_clause
|
19
36
|
to_clause( ", " )
|
20
37
|
end
|
38
|
+
|
39
|
+
if method_defined? :slice
|
40
|
+
warn "Hash#slice already defined; redefining."
|
41
|
+
end
|
42
|
+
def slice( *desired_keys )
|
43
|
+
Hash[
|
44
|
+
select { |key,value|
|
45
|
+
desired_keys.include? key
|
46
|
+
}
|
47
|
+
]
|
48
|
+
end
|
21
49
|
end
|
data/lib/m4dbi/model.rb
CHANGED
@@ -11,26 +11,39 @@ module DBI
|
|
11
11
|
|
12
12
|
extend Enumerable
|
13
13
|
|
14
|
-
def self.[](
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
14
|
+
def self.[]( first_arg, *args )
|
15
|
+
if args.size == 0
|
16
|
+
case first_arg
|
17
|
+
when Hash
|
18
|
+
clause, values = first_arg.to_where_clause
|
19
|
+
when NilClass
|
20
|
+
clause = pk_clause
|
21
|
+
values = [ first_arg ]
|
22
|
+
else # single value
|
23
|
+
clause = pk_clause
|
24
|
+
values = Array( first_arg )
|
25
|
+
end
|
26
|
+
else
|
27
|
+
clause = pk_clause
|
28
|
+
values = [ first_arg ] + args
|
27
29
|
end
|
28
30
|
|
31
|
+
row = dbh.select_one(
|
32
|
+
"SELECT * FROM #{table} WHERE #{clause}",
|
33
|
+
*values
|
34
|
+
)
|
35
|
+
|
29
36
|
if row
|
30
37
|
self.new( row )
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
41
|
+
def self.pk_clause
|
42
|
+
pk.map { |col|
|
43
|
+
"#{col} = ?"
|
44
|
+
}.join( ' AND ' )
|
45
|
+
end
|
46
|
+
|
34
47
|
def self.from_rows( rows )
|
35
48
|
rows.map { |r| self.new( r ) }
|
36
49
|
end
|
@@ -85,7 +98,7 @@ module DBI
|
|
85
98
|
end
|
86
99
|
|
87
100
|
def self.count
|
88
|
-
dbh.select_column( "SELECT COUNT(*) FROM #{table}" )
|
101
|
+
dbh.select_column( "SELECT COUNT(*) FROM #{table}" ).to_i
|
89
102
|
end
|
90
103
|
|
91
104
|
def self.create( hash = {} )
|
@@ -115,9 +128,16 @@ module DBI
|
|
115
128
|
*values
|
116
129
|
)
|
117
130
|
if num_inserted > 0
|
118
|
-
|
119
|
-
|
120
|
-
|
131
|
+
pk_hash = hash.slice( *(
|
132
|
+
self.pk.map { |pk_col| pk_col.to_sym }
|
133
|
+
) )
|
134
|
+
if pk_hash.empty?
|
135
|
+
pk_hash = hash.slice( *(
|
136
|
+
self.pk.map { |pk_col| pk_col.to_s }
|
137
|
+
) )
|
138
|
+
end
|
139
|
+
if not pk_hash.empty?
|
140
|
+
rec = self.one_where( pk_hash )
|
121
141
|
else
|
122
142
|
begin
|
123
143
|
rec = last_record( dbh_ )
|
@@ -189,11 +209,12 @@ module DBI
|
|
189
209
|
)
|
190
210
|
end
|
191
211
|
|
192
|
-
def self.update_one(
|
193
|
-
set_clause, set_params =
|
194
|
-
|
212
|
+
def self.update_one( *args )
|
213
|
+
set_clause, set_params = args[ -1 ].to_set_clause
|
214
|
+
pk_values = args[ 0..-2 ]
|
215
|
+
params = set_params + pk_values
|
195
216
|
dbh.do(
|
196
|
-
"UPDATE #{table} SET #{set_clause} WHERE #{
|
217
|
+
"UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}",
|
197
218
|
*params
|
198
219
|
)
|
199
220
|
end
|
@@ -233,6 +254,7 @@ module DBI
|
|
233
254
|
j.#{m1_fk} = ?
|
234
255
|
AND m2.id = j.#{m2_fk}
|
235
256
|
},
|
257
|
+
# TODO: m2.id? Should be m2.pk or something
|
236
258
|
pk
|
237
259
|
)
|
238
260
|
end
|
@@ -249,6 +271,7 @@ module DBI
|
|
249
271
|
j.#{m2_fk} = ?
|
250
272
|
AND m1.id = j.#{m1_fk}
|
251
273
|
},
|
274
|
+
# TODO: Should be m1.pk not m1.id
|
252
275
|
pk
|
253
276
|
)
|
254
277
|
end
|
@@ -267,17 +290,44 @@ module DBI
|
|
267
290
|
end
|
268
291
|
|
269
292
|
def method_missing( method, *args )
|
270
|
-
|
293
|
+
begin
|
294
|
+
@row.send( method, *args )
|
295
|
+
rescue NoMethodError => e
|
296
|
+
raise NoMethodError.new(
|
297
|
+
"undefined method '#{method}' for #{self}",
|
298
|
+
method,
|
299
|
+
args
|
300
|
+
)
|
301
|
+
end
|
271
302
|
end
|
272
303
|
|
304
|
+
# Returns a single value for single-column primary keys,
|
305
|
+
# returns an Array for multi-column primary keys.
|
273
306
|
def pk
|
274
|
-
|
307
|
+
if pk_columns.size == 1
|
308
|
+
@row[ pk_columns[ 0 ] ]
|
309
|
+
else
|
310
|
+
pk_values
|
311
|
+
end
|
275
312
|
end
|
276
313
|
|
277
|
-
|
314
|
+
# Always returns an Array of values, even for single-column primary keys.
|
315
|
+
def pk_values
|
316
|
+
pk_columns.map { |col|
|
317
|
+
@row[ col ]
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
def pk_columns
|
278
322
|
self.class.pk
|
279
323
|
end
|
280
324
|
|
325
|
+
def pk_clause
|
326
|
+
pk_columns.map { |col|
|
327
|
+
"#{col} = ?"
|
328
|
+
}.join( ' AND ' )
|
329
|
+
end
|
330
|
+
|
281
331
|
def ==( other )
|
282
332
|
other and ( pk == other.pk )
|
283
333
|
end
|
@@ -293,17 +343,23 @@ module DBI
|
|
293
343
|
def set( hash )
|
294
344
|
set_clause, set_params = hash.to_set_clause
|
295
345
|
set_params << pk
|
296
|
-
dbh.do(
|
297
|
-
"UPDATE #{table} SET #{set_clause} WHERE #{
|
346
|
+
num_updated = dbh.do(
|
347
|
+
"UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}",
|
298
348
|
*set_params
|
299
349
|
)
|
350
|
+
if num_updated > 0
|
351
|
+
hash.each do |key,value|
|
352
|
+
@row[ key ] = value
|
353
|
+
end
|
354
|
+
end
|
355
|
+
num_updated
|
300
356
|
end
|
301
357
|
|
302
358
|
# Returns true iff the record and only the record was successfully deleted.
|
303
359
|
def delete
|
304
360
|
num_deleted = dbh.do(
|
305
|
-
"DELETE FROM #{table} WHERE #{
|
306
|
-
|
361
|
+
"DELETE FROM #{table} WHERE #{pk_clause}",
|
362
|
+
*pk_values
|
307
363
|
)
|
308
364
|
num_deleted == 1
|
309
365
|
end
|
@@ -316,18 +372,21 @@ module DBI
|
|
316
372
|
|
317
373
|
# Define a new DBI::Model like this:
|
318
374
|
# class Post < DBI::Model( :posts ); end
|
319
|
-
# You can specify the primary key column like so:
|
320
|
-
# class Author < DBI::Model( :authors, '
|
321
|
-
def self.Model( table, pk_ = 'id' )
|
375
|
+
# You can specify the primary key column(s) using an array, like so:
|
376
|
+
# class Author < DBI::Model( :authors, [ 'auth_num' ] ); end
|
377
|
+
def self.Model( table, pk_ = [ 'id' ] )
|
322
378
|
h = DBI::DatabaseHandle.last_handle
|
323
379
|
if h.nil? or not h.connected?
|
324
380
|
raise DBI::Error.new( "Attempted to create a Model class without first connecting to a database." )
|
325
381
|
end
|
382
|
+
if not pk_.respond_to? :each
|
383
|
+
raise DBI::Error.new( "Primary key must be enumerable (was given #{pk_.inspect})" )
|
384
|
+
end
|
326
385
|
|
327
386
|
model_key =
|
328
|
-
|
387
|
+
# DBD-dependent. Not all DBDs have dbname implemented by M4DBI.
|
388
|
+
if h.respond_to? :dbname
|
329
389
|
"#{h.dbname}::#{table}"
|
330
|
-
# TODO: more DBDs
|
331
390
|
else
|
332
391
|
table
|
333
392
|
end
|
@@ -342,24 +401,35 @@ module DBI
|
|
342
401
|
} )
|
343
402
|
|
344
403
|
if defined?( DBI::DBD::Pg::Database ) and DBI::DBD::Pg::Database === h.handle
|
404
|
+
# TODO: This is broken for non-SERIAL or multi-column primary keys
|
345
405
|
meta_def( "last_record".to_sym ) do |dbh_|
|
346
406
|
self.s1 "SELECT * FROM #{table} WHERE #{pk} = currval( '#{table}_#{pk}_seq' );"
|
347
407
|
end
|
408
|
+
elsif defined?( DBI::DBD::Mysql::Database ) and DBI::DBD::Mysql::Database === h.handle
|
409
|
+
meta_def( "last_record".to_sym ) do |dbh_|
|
410
|
+
self.s1 "SELECT * FROM #{table} WHERE #{pk} = LAST_INSERT_ID();"
|
411
|
+
end
|
412
|
+
elsif defined?( DBI::DBD::SQLite3::Database ) and DBI::DBD::SQLite3::Database === h.handle
|
413
|
+
meta_def( "last_record".to_sym ) do |dbh_|
|
414
|
+
self.s1 "SELECT * FROM #{table} WHERE #{pk} = last_insert_rowid();"
|
415
|
+
end
|
348
416
|
# TODO: more DBDs
|
349
417
|
end
|
350
418
|
|
351
419
|
klass.trait[ :columns ].each do |col|
|
352
420
|
colname = col[ 'name' ]
|
353
421
|
|
422
|
+
# Column readers
|
354
423
|
class_def( colname.to_sym ) do
|
355
424
|
@row[ colname ]
|
356
425
|
end
|
357
426
|
|
427
|
+
# Column writers
|
358
428
|
class_def( "#{colname}=".to_sym ) do |new_value|
|
359
429
|
num_changed = dbh.do(
|
360
|
-
"UPDATE #{table} SET #{colname} = ? WHERE #{
|
430
|
+
"UPDATE #{table} SET #{colname} = ? WHERE #{pk_clause}",
|
361
431
|
new_value,
|
362
|
-
|
432
|
+
*pk_values
|
363
433
|
)
|
364
434
|
if num_changed > 0
|
365
435
|
@row[ colname ] = new_value
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module DBI
|
2
|
+
class Timestamp
|
3
|
+
def method_missing( method, *args )
|
4
|
+
t = to_time
|
5
|
+
begin
|
6
|
+
t.send( method, *args )
|
7
|
+
rescue NoMethodError => e
|
8
|
+
raise NoMethodError.new(
|
9
|
+
"undefined method '#{method}' for #{self}",
|
10
|
+
method,
|
11
|
+
args
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def <=>( other )
|
17
|
+
to_time <=> other.to_time
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/dbi.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec/helper'
|
2
2
|
|
3
|
-
$dbh =
|
4
|
-
|
3
|
+
$dbh = connect_to_spec_database
|
4
|
+
reset_data
|
5
5
|
|
6
6
|
describe 'DBI::DatabaseHandle#select_column' do
|
7
7
|
|
@@ -11,6 +11,15 @@ describe 'DBI::DatabaseHandle#select_column' do
|
|
11
11
|
)
|
12
12
|
name.class.should.not.equal Array
|
13
13
|
name.should.equal 'author1'
|
14
|
+
|
15
|
+
null = $dbh.select_column(
|
16
|
+
"SELECT c4 FROM many_col_table WHERE c3 = 40"
|
17
|
+
)
|
18
|
+
null.should.be.nil
|
19
|
+
|
20
|
+
should.raise( DBI::DataError ) do
|
21
|
+
$dbh.select_column( "SELECT name FROM authors WHERE 1+1 = 3" )
|
22
|
+
end
|
14
23
|
end
|
15
24
|
|
16
25
|
it 'selects one column of first row' do
|
@@ -26,7 +35,6 @@ describe 'DBI::DatabaseHandle#select_column' do
|
|
26
35
|
)
|
27
36
|
name.should.equal 'author3'
|
28
37
|
end
|
29
|
-
|
30
38
|
end
|
31
39
|
|
32
40
|
describe 'DBI::DatabaseHandle#one_transaction' do
|
@@ -87,6 +95,8 @@ describe 'DBI::DatabaseHandle#one_transaction' do
|
|
87
95
|
|
88
96
|
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
89
97
|
value.should.equal( 11 + 1 + 1 )
|
98
|
+
|
99
|
+
reset_data
|
90
100
|
end
|
91
101
|
|
92
102
|
end
|
data/spec/hash.rb
CHANGED
@@ -5,23 +5,43 @@ describe 'Hash' do
|
|
5
5
|
h = {
|
6
6
|
:a => 2,
|
7
7
|
:b => 'foo',
|
8
|
+
:the_nil => nil,
|
8
9
|
:abc => Time.now,
|
9
10
|
:xyz => 9.02,
|
10
11
|
}
|
11
12
|
clause, values = h.to_clause( " AND " )
|
12
13
|
where_clause, where_values = h.to_where_clause
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
str = "a = ? AND b = ? AND the_nil = ? AND abc = ? AND xyz = ?"
|
16
|
+
where_str = "a = ? AND b = ? AND the_nil IS NULL AND abc = ? AND xyz = ?"
|
17
|
+
clause.length.should.equal str.length
|
18
|
+
where_clause.length.should.equal where_str.length
|
17
19
|
|
18
|
-
h.
|
20
|
+
h.each_key do |key|
|
19
21
|
clause.should.match /#{key} = ?/
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
if h[ key ].nil?
|
23
|
+
where_clause.should.match /#{key} IS NULL/
|
24
|
+
else
|
25
|
+
where_clause.should.match /#{key} = ?/
|
26
|
+
end
|
23
27
|
end
|
24
|
-
|
25
|
-
|
28
|
+
|
29
|
+
values.should.equal h.values
|
30
|
+
where_values.should.equal h.values.compact
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'offers a means to get subhashes via #slice' do
|
34
|
+
h = { :a => 1, :b => 2, :c => 3, :d => 4, :e => 5, :f => 6 }
|
35
|
+
h.slice( :b, :c ).should.equal(
|
36
|
+
{ :b => 2, :c => 3 }
|
37
|
+
)
|
38
|
+
h.slice( :a, :b, :f ).should.equal(
|
39
|
+
{ :a => 1, :b => 2, :f => 6 }
|
40
|
+
)
|
41
|
+
h.slice( :c, :e, :q ).should.equal(
|
42
|
+
{ :c => 3, :e => 5 }
|
43
|
+
)
|
44
|
+
h.slice.should.equal Hash.new
|
45
|
+
h.slice( :g, :h ).should.equal Hash.new
|
26
46
|
end
|
27
47
|
end
|
data/spec/helper.rb
CHANGED
@@ -11,3 +11,22 @@ $LOAD_PATH.unshift(
|
|
11
11
|
)
|
12
12
|
|
13
13
|
require 'm4dbi'
|
14
|
+
|
15
|
+
puts "DBI version: #{DBI::VERSION}"
|
16
|
+
puts "M4DBI version: #{M4DBI_VERSION}"
|
17
|
+
|
18
|
+
# See test-schema*.sql and test-data.sql
|
19
|
+
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" )
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset_data( dbh = $dbh, datafile = "test-data.sql" )
|
26
|
+
dir = File.dirname( __FILE__ )
|
27
|
+
File.read( "#{dir}/#{datafile}" ).split( /;/ ).each do |command|
|
28
|
+
if not command.strip.empty?
|
29
|
+
dbh.do command
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/model.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
require 'spec/helper'
|
2
2
|
|
3
|
-
# See test-schema.sql and test-data.sql
|
4
|
-
|
5
|
-
def reset_data( dbh = $dbh, datafile = "test-data.sql" )
|
6
|
-
dir = File.dirname( __FILE__ )
|
7
|
-
File.read( "#{dir}/#{datafile}" ).split( /;/ ).each do |command|
|
8
|
-
dbh.do( command )
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
3
|
describe 'DBI::Model' do
|
13
4
|
it 'raises an exception when trying to define a model before connecting to a database' do
|
14
5
|
dbh = DBI::DatabaseHandle.last_handle
|
@@ -21,7 +12,7 @@ describe 'DBI::Model' do
|
|
21
12
|
end
|
22
13
|
end
|
23
14
|
|
24
|
-
$dbh =
|
15
|
+
$dbh = connect_to_spec_database
|
25
16
|
reset_data
|
26
17
|
|
27
18
|
class ManyCol < DBI::Model( :many_col_table )
|
@@ -43,6 +34,7 @@ describe 'A DBI::Model subclass' do
|
|
43
34
|
@m_post = Class.new( DBI::Model( :posts ) )
|
44
35
|
@m_empty = Class.new( DBI::Model( :empty_table ) )
|
45
36
|
@m_mc = Class.new( DBI::Model( :many_col_table ) )
|
37
|
+
@m_mcpk = Class.new( DBI::Model( :mcpk, [ :kc1, :kc2 ] ) )
|
46
38
|
class Author < DBI::Model( :authors ); end
|
47
39
|
end
|
48
40
|
|
@@ -78,7 +70,7 @@ describe 'A DBI::Model subclass' do
|
|
78
70
|
|
79
71
|
class Author < DBI::Model( :authors ); end
|
80
72
|
|
81
|
-
dbh =
|
73
|
+
dbh = connect_to_spec_database
|
82
74
|
new_handle = DBI::DatabaseHandle.last_handle
|
83
75
|
new_handle.should.equal dbh
|
84
76
|
new_handle.should.not.equal original_handle
|
@@ -93,7 +85,7 @@ describe 'A DBI::Model subclass' do
|
|
93
85
|
a1.should.not.be.nil
|
94
86
|
a1.name.should.equal 'author1'
|
95
87
|
|
96
|
-
dbh =
|
88
|
+
dbh = connect_to_spec_database( 'm4dbi2' )
|
97
89
|
reset_data( dbh, "test-data2.sql" )
|
98
90
|
|
99
91
|
@m_author2 = Class.new( DBI::Model( :authors ) )
|
@@ -109,7 +101,7 @@ describe 'A DBI::Model subclass' do
|
|
109
101
|
ensure
|
110
102
|
# Clean up handles for later specs
|
111
103
|
dbh.disconnect if dbh and dbh.connected?
|
112
|
-
|
104
|
+
connect_to_spec_database
|
113
105
|
end
|
114
106
|
end
|
115
107
|
|
@@ -135,6 +127,26 @@ describe 'A DBI::Model subclass' do
|
|
135
127
|
o.should.not.be.nil
|
136
128
|
o.class.should.equal @m_author
|
137
129
|
o.name.should.equal 'author2'
|
130
|
+
|
131
|
+
o = @m_mcpk[ [ 2, 2 ] ]
|
132
|
+
o.should.not.be.nil
|
133
|
+
o.class.should.equal @m_mcpk
|
134
|
+
o.val.should.equal 'two two'
|
135
|
+
|
136
|
+
o = @m_mcpk[ 1, 1 ]
|
137
|
+
o.should.not.be.nil
|
138
|
+
o.class.should.equal @m_mcpk
|
139
|
+
o.val.should.equal 'one one'
|
140
|
+
|
141
|
+
o = @m_mcpk[ { :kc1 => 5, :kc2 => 6 } ]
|
142
|
+
o.should.not.be.nil
|
143
|
+
o.class.should.equal @m_mcpk
|
144
|
+
o.val.should.equal 'five six'
|
145
|
+
|
146
|
+
should.not.raise( DBI::Error ) do
|
147
|
+
o = @m_author[ nil ]
|
148
|
+
o.should.be.nil
|
149
|
+
end
|
138
150
|
end
|
139
151
|
|
140
152
|
it 'provides hash-like single-record access via #[ field_hash ]' do
|
@@ -147,6 +159,16 @@ describe 'A DBI::Model subclass' do
|
|
147
159
|
o.should.not.be.nil
|
148
160
|
o.class.should.equal @m_post
|
149
161
|
o.text.should.equal 'First post.'
|
162
|
+
|
163
|
+
o = @m_mc[ :c1 => 100, :c2 => 50 ]
|
164
|
+
o.should.not.be.nil
|
165
|
+
o.class.should.equal @m_mc
|
166
|
+
o.c3.should.equal 20
|
167
|
+
|
168
|
+
o = @m_mc[ :c1 => 100, :c2 => nil ]
|
169
|
+
o.should.not.be.nil
|
170
|
+
o.class.should.equal @m_mc
|
171
|
+
o.c3.should.equal 40
|
150
172
|
end
|
151
173
|
|
152
174
|
it 'returns nil from #[] when no record is found' do
|
@@ -158,9 +180,7 @@ describe 'A DBI::Model subclass' do
|
|
158
180
|
end
|
159
181
|
|
160
182
|
it 'provides multi-record access via #where( Hash )' do
|
161
|
-
posts = @m_post.where(
|
162
|
-
:author_id => 1
|
163
|
-
)
|
183
|
+
posts = @m_post.where( :author_id => 1 )
|
164
184
|
posts.should.not.be.nil
|
165
185
|
posts.should.not.be.empty
|
166
186
|
posts.size.should.equal 2
|
@@ -171,6 +191,24 @@ describe 'A DBI::Model subclass' do
|
|
171
191
|
}
|
172
192
|
p = sorted_posts.first
|
173
193
|
p.text.should.equal 'First post.'
|
194
|
+
|
195
|
+
rows = @m_mc.where( :c1 => 100, :c2 => 50 )
|
196
|
+
rows.should.not.be.nil
|
197
|
+
rows.should.not.be.empty
|
198
|
+
rows.size.should.equal 1
|
199
|
+
row = rows[ 0 ]
|
200
|
+
row.class.should.equal @m_mc
|
201
|
+
row.c1.should.equal 100
|
202
|
+
row.c3.should.equal 20
|
203
|
+
|
204
|
+
rows = @m_mc.where( :c1 => 100, :c2 => nil )
|
205
|
+
rows.should.not.be.nil
|
206
|
+
rows.should.not.be.empty
|
207
|
+
rows.size.should.equal 1
|
208
|
+
row = rows[ 0 ]
|
209
|
+
row.class.should.equal @m_mc
|
210
|
+
row.c1.should.equal 100
|
211
|
+
row.c3.should.equal 40
|
174
212
|
end
|
175
213
|
|
176
214
|
it 'provides multi-record access via #where( String )' do
|
@@ -187,6 +225,20 @@ describe 'A DBI::Model subclass' do
|
|
187
225
|
p.text.should.equal 'Second post.'
|
188
226
|
end
|
189
227
|
|
228
|
+
it 'provides multi-record access via #where( String, param, param... )' do
|
229
|
+
posts = @m_post.where( "id < ?", 3 )
|
230
|
+
posts.should.not.be.nil
|
231
|
+
posts.should.not.be.empty
|
232
|
+
posts.size.should.equal 2
|
233
|
+
posts[ 0 ].class.should.equal @m_post
|
234
|
+
|
235
|
+
sorted_posts = posts.sort { |p1,p2|
|
236
|
+
p2._id <=> p1._id
|
237
|
+
}
|
238
|
+
p = sorted_posts.first
|
239
|
+
p.text.should.equal 'Second post.'
|
240
|
+
end
|
241
|
+
|
190
242
|
it 'returns an empty array from #where when no records are found' do
|
191
243
|
a = @m_author.where( :id => 999 )
|
192
244
|
a.should.be.empty
|
@@ -200,6 +252,12 @@ describe 'A DBI::Model subclass' do
|
|
200
252
|
post.should.not.be.nil
|
201
253
|
post.class.should.equal @m_post
|
202
254
|
post.text.should.equal 'Second post.'
|
255
|
+
|
256
|
+
row = @m_mc.one_where( :c1 => 100, :c2 => nil )
|
257
|
+
row.should.not.be.nil
|
258
|
+
row.class.should.equal @m_mc
|
259
|
+
row.c1.should.equal 100
|
260
|
+
row.c3.should.equal 40
|
203
261
|
end
|
204
262
|
|
205
263
|
it 'provides single-record access via #one_where( String )' do
|
@@ -209,6 +267,13 @@ describe 'A DBI::Model subclass' do
|
|
209
267
|
post.id.should.equal 3
|
210
268
|
end
|
211
269
|
|
270
|
+
it 'provides single-record access via #one_where( String, param, param... )' do
|
271
|
+
post = @m_post.one_where( "text LIKE ?", '%Third%' )
|
272
|
+
post.should.not.be.nil
|
273
|
+
post.class.should.equal @m_post
|
274
|
+
post.id.should.equal 3
|
275
|
+
end
|
276
|
+
|
212
277
|
it 'returns nil from #one_where when no record is found' do
|
213
278
|
a = @m_author.one_where( :id => 999 )
|
214
279
|
a.should.be.nil
|
@@ -217,7 +282,6 @@ describe 'A DBI::Model subclass' do
|
|
217
282
|
p.should.be.nil
|
218
283
|
end
|
219
284
|
|
220
|
-
|
221
285
|
it 'returns all table records via #all' do
|
222
286
|
rows = @m_author.all
|
223
287
|
rows.should.not.be.nil
|
@@ -225,8 +289,10 @@ describe 'A DBI::Model subclass' do
|
|
225
289
|
rows.size.should.equal 3
|
226
290
|
|
227
291
|
rows[ 0 ].id.should.equal 1
|
292
|
+
rows[ 0 ].class.should.equal @m_author
|
228
293
|
rows[ 0 ].name.should.equal 'author1'
|
229
294
|
rows[ 1 ].id.should.equal 2
|
295
|
+
rows[ 1 ].class.should.equal @m_author
|
230
296
|
rows[ 1 ].name.should.equal 'author2'
|
231
297
|
end
|
232
298
|
|
@@ -236,7 +302,7 @@ describe 'A DBI::Model subclass' do
|
|
236
302
|
rows.should.be.empty
|
237
303
|
end
|
238
304
|
|
239
|
-
it 'returns
|
305
|
+
it 'returns a random single record from #one' do
|
240
306
|
one = @m_author.one
|
241
307
|
one.should.not.be.nil
|
242
308
|
one.class.should.equal @m_author
|
@@ -324,6 +390,14 @@ describe 'A DBI::Model subclass' do
|
|
324
390
|
a.should.not.respond_to :no_column_by_this_name
|
325
391
|
a.name.should.equal 'author1'
|
326
392
|
@m_author.count.should.equal n
|
393
|
+
|
394
|
+
n = @m_mc.count
|
395
|
+
row = @m_mc.find_or_create( :c1 => 100, :c2 => nil )
|
396
|
+
row.should.not.be.nil
|
397
|
+
row.class.should.equal @m_mc
|
398
|
+
row.c1.should.equal 100
|
399
|
+
row.c3.should.equal 40
|
400
|
+
@m_mc.count.should.equal n
|
327
401
|
end
|
328
402
|
|
329
403
|
it 'creates a record via #find_or_create( Hash )' do
|
@@ -373,7 +447,7 @@ describe 'A DBI::Model subclass' do
|
|
373
447
|
posts[ 1 ].text.should.equal 'Third post.'
|
374
448
|
posts[ 1 ].class.should.equal @m_post
|
375
449
|
|
376
|
-
no_posts = @m_post.s( "SELECT * FROM posts WHERE
|
450
|
+
no_posts = @m_post.s( "SELECT * FROM posts WHERE 1+1 = 3" )
|
377
451
|
no_posts.should.not.be.nil
|
378
452
|
no_posts.should.be.empty
|
379
453
|
end
|
@@ -402,7 +476,7 @@ describe 'A DBI::Model subclass' do
|
|
402
476
|
post.author_id.should.equal 1
|
403
477
|
post.text.should.equal 'Third post.'
|
404
478
|
|
405
|
-
no_post = @m_post.s1( "SELECT * FROM posts WHERE
|
479
|
+
no_post = @m_post.s1( "SELECT * FROM posts WHERE 1+1 = 3" )
|
406
480
|
no_post.should.be.nil
|
407
481
|
end
|
408
482
|
|
@@ -426,16 +500,26 @@ describe 'A DBI::Model subclass' do
|
|
426
500
|
end
|
427
501
|
|
428
502
|
it 'provides a means to update records referred to by primary key value' do
|
429
|
-
new_text = '
|
503
|
+
new_text = 'Some new text.'
|
430
504
|
|
431
505
|
p2 = @m_post[ 2 ]
|
432
506
|
p2.text.should.not.equal new_text
|
433
|
-
|
434
507
|
@m_post.update_one( 2, { :text => new_text } )
|
435
|
-
|
436
508
|
p2_ = @m_post[ 2 ]
|
437
509
|
p2_.text.should.equal new_text
|
438
510
|
|
511
|
+
row = @m_mcpk[ 1, 1 ]
|
512
|
+
row.val.should.not.equal new_text
|
513
|
+
@m_mcpk.update_one( 1, 1, { :val => new_text } )
|
514
|
+
row = @m_mcpk[ 1, 1 ]
|
515
|
+
row.val.should.equal new_text
|
516
|
+
|
517
|
+
row = @m_mcpk[ 3, 4 ]
|
518
|
+
row.val.should.not.equal new_text
|
519
|
+
@m_mcpk.update_one( 3, 4, { :val => new_text } )
|
520
|
+
row = @m_mcpk[ 3, 4 ]
|
521
|
+
row.val.should.equal new_text
|
522
|
+
|
439
523
|
reset_data
|
440
524
|
end
|
441
525
|
|
@@ -560,6 +644,8 @@ describe 'A found DBI::Model subclass instance' do
|
|
560
644
|
before do
|
561
645
|
@m_author = Class.new( DBI::Model( :authors ) )
|
562
646
|
@m_post = Class.new( DBI::Model( :posts ) )
|
647
|
+
@m_mc = Class.new( DBI::Model( :many_col_table ) )
|
648
|
+
@m_mcpk = Class.new( DBI::Model( :mcpk, [ :kc1, :kc2 ] ) )
|
563
649
|
end
|
564
650
|
|
565
651
|
it 'provides access to primary key value' do
|
@@ -568,6 +654,12 @@ describe 'A found DBI::Model subclass instance' do
|
|
568
654
|
|
569
655
|
p = @m_post[ 3 ]
|
570
656
|
p.pk.should.equal 3
|
657
|
+
|
658
|
+
r = @m_mcpk[ 1, 1 ]
|
659
|
+
r.pk.should.equal [ 1, 1 ]
|
660
|
+
|
661
|
+
r = @m_mcpk[ { :kc1 => 3, :kc2 => 4 } ]
|
662
|
+
r.pk.should.equal [ 3, 4 ]
|
571
663
|
end
|
572
664
|
|
573
665
|
it 'provides read access to fields via identically-named readers' do
|
@@ -595,6 +687,7 @@ describe 'A found DBI::Model subclass instance' do
|
|
595
687
|
|
596
688
|
p3 = @m_post[ 3 ]
|
597
689
|
p3.text = the_new_text
|
690
|
+
p3.text.should.equal the_new_text
|
598
691
|
|
599
692
|
p3_ = @m_post[ 3 ]
|
600
693
|
p3_.text.should.equal the_new_text
|
@@ -603,6 +696,16 @@ describe 'A found DBI::Model subclass instance' do
|
|
603
696
|
p2_ = @m_post[ 2 ]
|
604
697
|
p2_.text.should.equal p2.text
|
605
698
|
|
699
|
+
mc1 = @m_mc.create(
|
700
|
+
:id => 1,
|
701
|
+
:c1 => 2
|
702
|
+
)
|
703
|
+
mc1.c1.should.equal 2
|
704
|
+
mc1.c1 = nil
|
705
|
+
mc1.c1.should.be.nil
|
706
|
+
mc1_ = @m_mc[ 1 ]
|
707
|
+
mc1_.c1.should.be.nil
|
708
|
+
|
606
709
|
reset_data
|
607
710
|
end
|
608
711
|
|
@@ -620,11 +723,28 @@ describe 'A found DBI::Model subclass instance' do
|
|
620
723
|
:author_id => 2,
|
621
724
|
:text => the_new_text
|
622
725
|
)
|
726
|
+
p.author_id.should.equal 2
|
727
|
+
p.text.should.equal the_new_text
|
623
728
|
|
624
729
|
p_ = @m_post[ 1 ]
|
625
730
|
p_.author_id.should.equal 2
|
626
731
|
p_.text.should.equal the_new_text
|
627
732
|
|
733
|
+
mc1 = @m_mc.create(
|
734
|
+
:id => 1,
|
735
|
+
:c1 => 2,
|
736
|
+
:c2 => 3
|
737
|
+
)
|
738
|
+
mc1.set(
|
739
|
+
:c1 => nil,
|
740
|
+
:c2 => 4
|
741
|
+
)
|
742
|
+
mc1.c1.should.be.nil
|
743
|
+
mc1.c2.should.equal 4
|
744
|
+
mc1_ = @m_mc[ 1 ]
|
745
|
+
mc1_.c1.should.be.nil
|
746
|
+
mc1_.c2.should.equal 4
|
747
|
+
|
628
748
|
reset_data
|
629
749
|
end
|
630
750
|
|
@@ -645,18 +765,6 @@ describe 'A found DBI::Model subclass instance' do
|
|
645
765
|
end
|
646
766
|
end
|
647
767
|
|
648
|
-
it 'allows a field to be incremented' do
|
649
|
-
mc = ManyCol.create( :c1 => 50 )
|
650
|
-
should.not.raise do
|
651
|
-
mc.inc
|
652
|
-
end
|
653
|
-
end
|
654
|
-
it 'allows a field to be decremented' do
|
655
|
-
mc = ManyCol.create( :c1 => 50 )
|
656
|
-
should.not.raise do
|
657
|
-
mc.dec
|
658
|
-
end
|
659
|
-
end
|
660
768
|
end
|
661
769
|
|
662
770
|
describe 'DBI::Model (relationships)' do
|
@@ -740,7 +848,6 @@ describe 'DBI::Collection' do
|
|
740
848
|
before do
|
741
849
|
@m_author = Class.new( DBI::Model( :authors ) )
|
742
850
|
@m_post = Class.new( DBI::Model( :posts ) )
|
743
|
-
@m_fan = Class.new( DBI::Model( :fans ) )
|
744
851
|
|
745
852
|
DBI::Model.one_to_many(
|
746
853
|
@m_author, @m_post, :posts, :author, :author_id
|
metadata
CHANGED
@@ -1,19 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: m4dbi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
platform:
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pistos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-11-10 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: metaid
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dbi
|
27
|
+
type: :runtime
|
17
28
|
version_requirement:
|
18
29
|
version_requirements: !ruby/object:Gem::Requirement
|
19
30
|
requirements:
|
@@ -31,21 +42,25 @@ extra_rdoc_files:
|
|
31
42
|
- HIM
|
32
43
|
- READHIM
|
33
44
|
- CHANGELOG
|
45
|
+
- LICENCE
|
34
46
|
files:
|
35
47
|
- HIM
|
36
48
|
- READHIM
|
37
|
-
-
|
49
|
+
- CHANGELOG
|
50
|
+
- LICENCE
|
51
|
+
- lib/m4dbi/array.rb
|
52
|
+
- lib/m4dbi/collection.rb
|
38
53
|
- lib/m4dbi/row.rb
|
39
|
-
- lib/m4dbi/
|
54
|
+
- lib/m4dbi/timestamp.rb
|
40
55
|
- lib/m4dbi/traits.rb
|
41
|
-
- lib/m4dbi/
|
56
|
+
- lib/m4dbi/database-handle.rb
|
42
57
|
- lib/m4dbi/hash.rb
|
58
|
+
- lib/m4dbi/model.rb
|
43
59
|
- lib/m4dbi.rb
|
44
|
-
- spec/helper.rb
|
45
|
-
- spec/dbi.rb
|
46
60
|
- spec/model.rb
|
61
|
+
- spec/dbi.rb
|
47
62
|
- spec/hash.rb
|
48
|
-
-
|
63
|
+
- spec/helper.rb
|
49
64
|
has_rdoc: false
|
50
65
|
homepage: http://purepistos.net/m4dbi
|
51
66
|
post_install_message:
|
@@ -66,15 +81,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
81
|
version: "0"
|
67
82
|
version:
|
68
83
|
requirements:
|
69
|
-
- dbi
|
70
84
|
- bacon (optional)
|
71
|
-
rubyforge_project:
|
72
|
-
rubygems_version:
|
85
|
+
rubyforge_project: m4dbi
|
86
|
+
rubygems_version: 1.2.0
|
73
87
|
signing_key:
|
74
88
|
specification_version: 2
|
75
89
|
summary: Models (and More) for DBI
|
76
90
|
test_files:
|
77
|
-
- spec/helper.rb
|
78
|
-
- spec/dbi.rb
|
79
91
|
- spec/model.rb
|
92
|
+
- spec/dbi.rb
|
80
93
|
- spec/hash.rb
|
94
|
+
- spec/helper.rb
|