m4dbi 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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.execute(
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.execute(
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.execute(
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
- @the_one.pk
65
- ).affected_count
67
+ }
68
+ )
69
+ stm.execute( @the_one.pk ).affected_count
66
70
  end
67
71
  end
68
- end
72
+ end
@@ -6,6 +6,10 @@ module M4DBI
6
6
  @dbh = rdbi_dbh
7
7
  end
8
8
 
9
+ def prepare( *args )
10
+ Statement.new( @dbh.prepare(*args) )
11
+ end
12
+
9
13
  def execute( *args )
10
14
  @dbh.execute *args
11
15
  end
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
- row = dbh.select_one(
29
- "SELECT * FROM #{table} WHERE #{clause}",
30
- *values
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.map { |col|
40
- "#{col} = ?"
41
- }.join( ' AND ' )
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
- dbh.select_all( sql, *params )
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
- row = dbh.select_one( sql, *params )
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
- self.from_rows(
81
- dbh.select_all( "SELECT * FROM #{table}" )
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
- row = dbh.select_one( "SELECT * FROM #{table} LIMIT 1" )
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
- dbh.select_column( "SELECT COUNT(*) FROM #{table}" ).to_i
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
- num_inserted = dbh_.execute( sql, *values ).affected_count
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 not pk_hash.empty?
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( *args )
178
+ def self.select_all( sql, *binds )
179
+ stm = prepare(sql)
172
180
  self.from_rows(
173
- dbh.select_all( *args )
181
+ stm.select_all( *binds )
174
182
  )
175
183
  end
176
184
 
177
- def self.select_one( *args )
178
- row = dbh.select_one( *args )
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
- dbh.execute(
204
- "UPDATE #{table} SET #{set_clause} WHERE #{where_clause}",
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
- dbh.execute(
214
- "UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}",
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
- num_updated = dbh.execute(
357
- "UPDATE #{table} SET #{set_clause} WHERE #{pk_clause}",
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
- num_deleted = dbh.execute(
371
- "DELETE FROM #{table} WHERE #{pk_clause}",
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 => h,
408
- :table => table,
409
- :pk => 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
- num_changed = dbh.execute(
454
- "UPDATE #{table} SET #{colname} = ? WHERE #{pk_clause}",
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
- num_changed = dbh.execute(
465
- "UPDATE #{table} SET #{colname} = ? WHERE #{pk_clause}",
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
@@ -1,3 +1,3 @@
1
1
  module M4DBI
2
- VERSION = '0.7.3'
2
+ VERSION = '0.8.0'
3
3
  end
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
- # puts "\nUsing RDBI driver: '#{driver}'"
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.7.3
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-08-14 00:00:00 Z
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!