m4dbi 0.7.3 → 0.8.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/README.rdoc +39 -0
- data/lib/m4dbi/collection.rb +13 -9
- data/lib/m4dbi/database.rb +4 -0
- data/lib/m4dbi/model.rb +52 -45
- data/lib/m4dbi/statement.rb +39 -0
- data/lib/m4dbi/version.rb +1 -1
- data/lib/m4dbi.rb +1 -0
- data/spec/helper.rb +1 -1
- data/spec/model.rb +1 -0
- metadata +5 -4
- data/README +0 -1
data/README.rdoc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
== M4DBI - Models (and more) For DBI
|
2
|
+
|
3
|
+
https://github.com/Pistos/m4dbi
|
4
|
+
|
5
|
+
M4DBI is a Ruby library that provides ORM modelling to RDBI
|
6
|
+
( https://github.com/RDBI/rdbi ).
|
7
|
+
|
8
|
+
=== Dependencies
|
9
|
+
|
10
|
+
- rdbi
|
11
|
+
- rdbi-driver-* for your choice of DBs
|
12
|
+
- metaid (gem install metaid)
|
13
|
+
- bacon (optional, for running tests; gem install bacon)
|
14
|
+
|
15
|
+
=== Installation
|
16
|
+
|
17
|
+
==== Repository
|
18
|
+
|
19
|
+
To use the repository version, you need git ( http://git-scm.com/ ).
|
20
|
+
Chances are, there is a git package for your Linux/UNIX flavour.
|
21
|
+
|
22
|
+
cd /where/you/want/m4dbi
|
23
|
+
git clone git://github.com/Pistos/m4dbi.git
|
24
|
+
|
25
|
+
=== Usage
|
26
|
+
|
27
|
+
See the spec/ dir.
|
28
|
+
|
29
|
+
(Somewhat out of date -- 2011-08-14)
|
30
|
+
|
31
|
+
=== Source Code
|
32
|
+
|
33
|
+
Browse source at http://github.com/Pistos/m4dbi .
|
34
|
+
Very limited rdocs at http://rdoc.info/github/Pistos/m4dbi/master/frames .
|
35
|
+
|
36
|
+
=== Feedback and Support
|
37
|
+
|
38
|
+
On IRC: irc.freenode.net ##mathetes .
|
39
|
+
Use http://webchat.freenode.net/?channels=mathetes if you don't have an IRC client.
|
data/lib/m4dbi/collection.rb
CHANGED
@@ -25,13 +25,15 @@ module M4DBI
|
|
25
25
|
def delete( arg )
|
26
26
|
case arg
|
27
27
|
when @the_many_model
|
28
|
-
@the_many_model.dbh.
|
28
|
+
stm = @the_many_model.dbh.prepare(
|
29
29
|
%{
|
30
30
|
DELETE FROM #{@the_many_model.table}
|
31
31
|
WHERE
|
32
32
|
#{@the_one_fk} = ?
|
33
33
|
AND #{@the_many_model.pk_clause}
|
34
|
-
}
|
34
|
+
}
|
35
|
+
)
|
36
|
+
stm.execute(
|
35
37
|
@the_one.pk,
|
36
38
|
arg.pk
|
37
39
|
).affected_count > 0
|
@@ -41,13 +43,15 @@ module M4DBI
|
|
41
43
|
where_subclause = keys.map { |k|
|
42
44
|
"#{k} = ?"
|
43
45
|
}.join( " AND " )
|
44
|
-
@the_many_model.dbh.
|
46
|
+
stm = @the_many_model.dbh.prepare(
|
45
47
|
%{
|
46
48
|
DELETE FROM #{@the_many_model.table}
|
47
49
|
WHERE
|
48
50
|
#{@the_one_fk} = ?
|
49
51
|
AND #{where_subclause}
|
50
|
-
}
|
52
|
+
}
|
53
|
+
)
|
54
|
+
stm.execute(
|
51
55
|
@the_one.pk,
|
52
56
|
*( keys.map { |k| hash[ k ] } )
|
53
57
|
).affected_count
|
@@ -56,13 +60,13 @@ module M4DBI
|
|
56
60
|
|
57
61
|
# Returns the number of records deleted
|
58
62
|
def clear
|
59
|
-
@the_many_model.dbh.
|
63
|
+
stm = @the_many_model.dbh.prepare(
|
60
64
|
%{
|
61
65
|
DELETE FROM #{@the_many_model.table}
|
62
66
|
WHERE #{@the_one_fk} = ?
|
63
|
-
}
|
64
|
-
|
65
|
-
).affected_count
|
67
|
+
}
|
68
|
+
)
|
69
|
+
stm.execute( @the_one.pk ).affected_count
|
66
70
|
end
|
67
71
|
end
|
68
|
-
end
|
72
|
+
end
|
data/lib/m4dbi/database.rb
CHANGED
data/lib/m4dbi/model.rb
CHANGED
@@ -2,12 +2,16 @@ module M4DBI
|
|
2
2
|
class Model
|
3
3
|
#attr_reader :row
|
4
4
|
ancestral_trait_reader :dbh, :table
|
5
|
-
ancestral_trait_class_reader :dbh, :table, :pk, :columns
|
5
|
+
ancestral_trait_class_reader :dbh, :table, :pk, :columns, :st
|
6
6
|
|
7
7
|
M4DBI_UNASSIGNED = '__m4dbi_unassigned__'
|
8
8
|
|
9
9
|
extend Enumerable
|
10
10
|
|
11
|
+
def self.prepare( sql )
|
12
|
+
st[sql] ||= dbh.prepare(sql)
|
13
|
+
end
|
14
|
+
|
11
15
|
def self.[]( first_arg, *args )
|
12
16
|
if args.size == 0
|
13
17
|
case first_arg
|
@@ -25,10 +29,9 @@ module M4DBI
|
|
25
29
|
values = [ first_arg ] + args
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
)
|
32
|
+
sql = "SELECT * FROM #{table} WHERE #{clause}"
|
33
|
+
stm = prepare(sql)
|
34
|
+
row = stm.select_one(*values)
|
32
35
|
|
33
36
|
if row
|
34
37
|
self.new( row )
|
@@ -36,9 +39,9 @@ module M4DBI
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def self.pk_clause
|
39
|
-
pk.
|
40
|
-
"#{col} = ?"
|
41
|
-
|
42
|
+
pk.
|
43
|
+
map { |col| "#{col} = ?" }.
|
44
|
+
join( ' AND ' )
|
42
45
|
end
|
43
46
|
|
44
47
|
def self.from_rows( rows )
|
@@ -55,8 +58,9 @@ module M4DBI
|
|
55
58
|
sql = "SELECT * FROM #{table} WHERE #{cond}"
|
56
59
|
end
|
57
60
|
|
61
|
+
stm = prepare(sql)
|
58
62
|
self.from_rows(
|
59
|
-
|
63
|
+
stm.select_all(*params)
|
60
64
|
)
|
61
65
|
end
|
62
66
|
|
@@ -70,16 +74,16 @@ module M4DBI
|
|
70
74
|
sql = "SELECT * FROM #{table} WHERE #{cond} LIMIT 1"
|
71
75
|
end
|
72
76
|
|
73
|
-
|
77
|
+
stm = prepare(sql)
|
78
|
+
row = stm.select_one( *params )
|
74
79
|
if row
|
75
80
|
self.new( row )
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
79
84
|
def self.all
|
80
|
-
|
81
|
-
|
82
|
-
)
|
85
|
+
stm = prepare("SELECT * FROM #{table}")
|
86
|
+
self.from_rows( stm.select_all )
|
83
87
|
end
|
84
88
|
|
85
89
|
# TODO: Perhaps we'll use cursors for Model#each.
|
@@ -88,14 +92,16 @@ module M4DBI
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def self.one
|
91
|
-
|
95
|
+
stm = prepare("SELECT * FROM #{table} LIMIT 1")
|
96
|
+
row = stm.select_one
|
92
97
|
if row
|
93
98
|
self.new( row )
|
94
99
|
end
|
95
100
|
end
|
96
101
|
|
97
102
|
def self.count
|
98
|
-
|
103
|
+
stm = prepare("SELECT COUNT(*) FROM #{table}")
|
104
|
+
stm.select_column.to_i
|
99
105
|
end
|
100
106
|
|
101
107
|
def self.create( hash = {} )
|
@@ -123,7 +129,8 @@ module M4DBI
|
|
123
129
|
else
|
124
130
|
sql = "INSERT INTO #{table} ( #{cols} ) VALUES ( #{value_placeholders} )"
|
125
131
|
end
|
126
|
-
|
132
|
+
stm = prepare(sql)
|
133
|
+
num_inserted = stm.execute(*values).affected_count
|
127
134
|
if num_inserted > 0
|
128
135
|
pk_hash = hash.slice( *(
|
129
136
|
self.pk.map { |pk_col| pk_col.to_sym }
|
@@ -133,7 +140,7 @@ module M4DBI
|
|
133
140
|
self.pk.map { |pk_col| pk_col.to_s }
|
134
141
|
) )
|
135
142
|
end
|
136
|
-
if
|
143
|
+
if ! pk_hash.empty?
|
137
144
|
rec = self.one_where( pk_hash )
|
138
145
|
else
|
139
146
|
begin
|
@@ -168,14 +175,16 @@ module M4DBI
|
|
168
175
|
end
|
169
176
|
end
|
170
177
|
|
171
|
-
def self.select_all( *
|
178
|
+
def self.select_all( sql, *binds )
|
179
|
+
stm = prepare(sql)
|
172
180
|
self.from_rows(
|
173
|
-
|
181
|
+
stm.select_all( *binds )
|
174
182
|
)
|
175
183
|
end
|
176
184
|
|
177
|
-
def self.select_one( *
|
178
|
-
|
185
|
+
def self.select_one( sql, *binds )
|
186
|
+
stm = prepare(sql)
|
187
|
+
row = stm.select_one( *binds )
|
179
188
|
if row
|
180
189
|
self.new( row )
|
181
190
|
end
|
@@ -200,20 +209,16 @@ module M4DBI
|
|
200
209
|
|
201
210
|
set_clause, set_params = set_hash.to_set_clause
|
202
211
|
params = set_params + where_params
|
203
|
-
|
204
|
-
|
205
|
-
*params
|
206
|
-
)
|
212
|
+
stm = prepare("UPDATE #{table} SET #{set_clause} WHERE #{where_clause}")
|
213
|
+
stm.execute( *params )
|
207
214
|
end
|
208
215
|
|
209
216
|
def self.update_one( *args )
|
210
217
|
set_clause, set_params = args[ -1 ].to_set_clause
|
211
218
|
pk_values = args[ 0..-2 ]
|
212
219
|
params = set_params + pk_values
|
213
|
-
|
214
|
-
|
215
|
-
*params
|
216
|
-
)
|
220
|
+
stm = prepare("UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}")
|
221
|
+
stm.execute( *params )
|
217
222
|
end
|
218
223
|
|
219
224
|
# Example:
|
@@ -278,12 +283,17 @@ module M4DBI
|
|
278
283
|
|
279
284
|
def initialize( row )
|
280
285
|
if ! row.respond_to?( "[]".to_sym ) || ! row.respond_to?( "[]=".to_sym )
|
281
|
-
raise M4DBI::Error.new( "Attempted to instantiate M4DBI::Model with an invalid argument (#{row.inspect}). (Expecting something accessible [] and []= .)" )
|
286
|
+
raise M4DBI::Error.new( "Attempted to instantiate M4DBI::Model with an invalid argument (#{row.inspect}). (Expecting something accessible with [] and []= .)" )
|
282
287
|
end
|
283
288
|
if caller[ 1 ] !~ %r{/m4dbi/model\.rb:}
|
284
289
|
warn "Do not call M4DBI::Model#new directly; use M4DBI::Model#create instead."
|
285
290
|
end
|
286
291
|
@row = row
|
292
|
+
@st = Hash.new
|
293
|
+
end
|
294
|
+
|
295
|
+
def prepare( sql )
|
296
|
+
@st[sql] ||= dbh.prepare(sql)
|
287
297
|
end
|
288
298
|
|
289
299
|
def method_missing( method, *args )
|
@@ -353,10 +363,8 @@ module M4DBI
|
|
353
363
|
def set( hash )
|
354
364
|
set_clause, set_params = hash.to_set_clause
|
355
365
|
set_params << pk
|
356
|
-
|
357
|
-
|
358
|
-
*set_params
|
359
|
-
).affected_count
|
366
|
+
st = prepare("UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}")
|
367
|
+
num_updated = st.execute( *set_params ).affected_count
|
360
368
|
if num_updated > 0
|
361
369
|
hash.each do |key,value|
|
362
370
|
@row[ key ] = value
|
@@ -367,10 +375,8 @@ module M4DBI
|
|
367
375
|
|
368
376
|
# Returns true iff the record and only the record was successfully deleted.
|
369
377
|
def delete
|
370
|
-
|
371
|
-
|
372
|
-
*pk_values
|
373
|
-
).affected_count
|
378
|
+
st = prepare("DELETE FROM #{table} WHERE #{pk_clause}")
|
379
|
+
num_deleted = st.execute( *pk_values ).affected_count
|
374
380
|
num_deleted == 1
|
375
381
|
end
|
376
382
|
|
@@ -404,10 +410,11 @@ module M4DBI
|
|
404
410
|
@models ||= Hash.new
|
405
411
|
@models[ model_key ] ||= Class.new( M4DBI::Model ) do |klass|
|
406
412
|
klass.trait( {
|
407
|
-
:dbh
|
408
|
-
:table
|
409
|
-
:pk
|
413
|
+
:dbh => h,
|
414
|
+
:table => table,
|
415
|
+
:pk => pk_,
|
410
416
|
:columns => h.table_schema( table.to_sym ).columns,
|
417
|
+
:st => Hash.new, # prepared statements for all queries
|
411
418
|
} )
|
412
419
|
|
413
420
|
meta_def( 'pk_str'.to_sym ) do
|
@@ -450,8 +457,8 @@ module M4DBI
|
|
450
457
|
# Column writers
|
451
458
|
|
452
459
|
class_def( "#{method}=".to_sym ) do |new_value|
|
453
|
-
|
454
|
-
|
460
|
+
stm = prepare("UPDATE #{table} SET #{colname} = ? WHERE #{pk_clause}")
|
461
|
+
num_changed = stm.execute(
|
455
462
|
new_value,
|
456
463
|
*pk_values
|
457
464
|
).affected_count
|
@@ -461,8 +468,8 @@ module M4DBI
|
|
461
468
|
end
|
462
469
|
|
463
470
|
class_def( '[]='.to_sym ) do |colname, new_value|
|
464
|
-
|
465
|
-
|
471
|
+
stm = prepare("UPDATE #{table} SET #{colname} = ? WHERE #{pk_clause}")
|
472
|
+
num_changed = stm.execute(
|
466
473
|
new_value,
|
467
474
|
*pk_values
|
468
475
|
).affected_count
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module M4DBI
|
2
|
+
class Statement
|
3
|
+
def initialize( rdbi_statement )
|
4
|
+
@st = rdbi_statement
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute( *args )
|
8
|
+
@st.execute *args
|
9
|
+
end
|
10
|
+
|
11
|
+
def select( *bindvars )
|
12
|
+
@st.execute( *bindvars ).fetch( :all, RDBI::Result::Driver::Struct )
|
13
|
+
end
|
14
|
+
|
15
|
+
def select_one( *bindvars )
|
16
|
+
select( *bindvars )[0]
|
17
|
+
end
|
18
|
+
|
19
|
+
def select_column( *bindvars )
|
20
|
+
rows = @st.execute( *bindvars ).fetch( 1, RDBI::Result::Driver::Array )
|
21
|
+
if rows.any?
|
22
|
+
rows[0][0]
|
23
|
+
else
|
24
|
+
raise RDBI::Error.new( "Query returned no rows. SQL: #{@dbh.last_query}" )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
alias select_all select
|
29
|
+
alias s select
|
30
|
+
alias s1 select_one
|
31
|
+
alias sc select_column
|
32
|
+
alias update execute
|
33
|
+
alias u execute
|
34
|
+
alias insert execute
|
35
|
+
alias i execute
|
36
|
+
alias delete execute
|
37
|
+
alias d execute
|
38
|
+
end
|
39
|
+
end
|
data/lib/m4dbi/version.rb
CHANGED
data/lib/m4dbi.rb
CHANGED
@@ -11,6 +11,7 @@ require "#{__DIR__}/m4dbi/traits"
|
|
11
11
|
require "#{__DIR__}/m4dbi/hash"
|
12
12
|
require "#{__DIR__}/m4dbi/array"
|
13
13
|
require "#{__DIR__}/m4dbi/database"
|
14
|
+
require "#{__DIR__}/m4dbi/statement"
|
14
15
|
require "#{__DIR__}/m4dbi/model"
|
15
16
|
require "#{__DIR__}/m4dbi/collection"
|
16
17
|
|
data/spec/helper.rb
CHANGED
@@ -17,7 +17,7 @@ puts "M4DBI version: #{M4DBI::VERSION}"
|
|
17
17
|
# See test-schema*.sql and test-data.sql
|
18
18
|
def connect_to_spec_database( database = ( ENV[ 'M4DBI_DATABASE' ] || 'm4dbi' ) )
|
19
19
|
driver = ENV[ 'M4DBI_DRIVER' ] || "PostgreSQL"
|
20
|
-
|
20
|
+
puts "\nUsing RDBI driver: '#{driver}'"
|
21
21
|
case driver.downcase
|
22
22
|
when 'postgresql', 'sqlite3', 'mysql'
|
23
23
|
require "rdbi/driver/#{driver.downcase}"
|
data/spec/model.rb
CHANGED
@@ -478,6 +478,7 @@ describe 'A M4DBI::Model subclass' do
|
|
478
478
|
r.should.respond_to :time_created
|
479
479
|
r.should.not.respond_to :no_column_by_this_name
|
480
480
|
r.time_created.should.not.be.nil
|
481
|
+
r.time_created.should.be.kind_of DateTime
|
481
482
|
end
|
482
483
|
|
483
484
|
it 'returns a record via #find_or_create( Hash )' do
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: m4dbi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.8.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Pistos
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-09-12 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: metaid
|
@@ -41,13 +41,14 @@ executables: []
|
|
41
41
|
extensions: []
|
42
42
|
|
43
43
|
extra_rdoc_files:
|
44
|
-
- README
|
44
|
+
- README.rdoc
|
45
45
|
- CHANGELOG
|
46
46
|
- LICENCE
|
47
47
|
files:
|
48
|
-
- README
|
48
|
+
- README.rdoc
|
49
49
|
- CHANGELOG
|
50
50
|
- LICENCE
|
51
|
+
- lib/m4dbi/statement.rb
|
51
52
|
- lib/m4dbi/database.rb
|
52
53
|
- lib/m4dbi/model.rb
|
53
54
|
- lib/m4dbi/version.rb
|
data/README
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Don't read ME; read HIM!
|