m4dbi 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.6.1
2
+
3
+ - Fixed major Hash#slice bug that only surfaced in Ruby 1.8.6 and earier.
4
+ - Minor rdoc and spec additions or adjustments.
5
+
1
6
  ## 0.6.0
2
7
 
3
8
  - Compatibility with DBI 0.4.0 ensured.
@@ -5,23 +5,23 @@ module DBI
5
5
  @the_many_model = the_many_model
6
6
  @the_one_fk = the_one_fk
7
7
  end
8
-
8
+
9
9
  def elements
10
10
  @the_many_model.where( @the_one_fk => @the_one.pk )
11
11
  end
12
12
  alias copy elements
13
-
13
+
14
14
  def method_missing( method, *args, &blk )
15
15
  elements.send( method, *args, &blk )
16
16
  end
17
-
17
+
18
18
  def push( new_item_hash )
19
19
  new_item_hash[ @the_one_fk ] = @the_one.pk
20
20
  @the_many_model.create( new_item_hash )
21
21
  end
22
22
  alias << push
23
23
  alias add push
24
-
24
+
25
25
  def delete( arg )
26
26
  case arg
27
27
  when @the_many_model
@@ -30,7 +30,7 @@ module DBI
30
30
  DELETE FROM #{@the_many_model.table}
31
31
  WHERE
32
32
  #{@the_one_fk} = ?
33
- AND #{@the_many_model.pk} = ?
33
+ AND #{@the_many_model.pk_clause}
34
34
  },
35
35
  @the_one.pk,
36
36
  arg.pk
@@ -54,7 +54,7 @@ module DBI
54
54
  )
55
55
  end
56
56
  end
57
-
57
+
58
58
  # Returns the number of records deleted
59
59
  def clear
60
60
  @the_many_model.dbh.do(
data/lib/m4dbi/model.rb CHANGED
@@ -103,15 +103,13 @@ module DBI
103
103
 
104
104
  def self.create( hash = {} )
105
105
  if block_given?
106
- row = DBI::Row.new(
107
- columns.collect { |c| c[ 'name' ] },
108
- [ M4DBI_UNASSIGNED ] * columns.size
109
- )
106
+ struct = Struct.new( *( columns.collect { |c| c[ 'name' ].to_sym } ) )
107
+ row = struct.new( *( [ M4DBI_UNASSIGNED ] * columns.size ) )
110
108
  yield row
111
- hash = row.to_h
112
- hash.to_a.each do |key,value|
113
- if value == M4DBI_UNASSIGNED
114
- hash.delete( key )
109
+ hash = {}
110
+ row.members.each do |k|
111
+ if row[ k ] != M4DBI_UNASSIGNED
112
+ hash[ k ] = row[ k ]
115
113
  end
116
114
  end
117
115
  end
@@ -154,13 +152,13 @@ module DBI
154
152
 
155
153
  def self.find_or_create( hash = nil )
156
154
  item = nil
157
- error = nil
155
+ error = DBI::Error.new( "Failed to find_or_create( #{hash.inspect} )" )
158
156
  item = self.one_where( hash )
159
157
  if item.nil?
160
158
  item =
161
159
  begin
162
160
  self.create( hash )
163
- rescue => error
161
+ rescue Exception => error
164
162
  self.one_where( hash )
165
163
  end
166
164
  end
@@ -293,8 +291,21 @@ module DBI
293
291
  begin
294
292
  @row.send( method, *args )
295
293
  rescue NoMethodError => e
294
+ if e.backtrace.grep /method_missing/
295
+ # Prevent infinite recursion
296
+ self_str = 'model object'
297
+ elsif self.respond_to? :to_s
298
+ self_str = self.to_s
299
+ elsif self.respond_to? :inspect
300
+ self_str = self.inspect
301
+ elsif self.respond_to? :class
302
+ self_str = "#{self.class} object"
303
+ else
304
+ self_str = "instance of unknown model"
305
+ end
306
+
296
307
  raise NoMethodError.new(
297
- "undefined method '#{method}' for #{self}",
308
+ "undefined method '#{method}' for #{self_str}",
298
309
  method,
299
310
  args
300
311
  )
@@ -372,13 +383,14 @@ module DBI
372
383
 
373
384
  # Define a new DBI::Model like this:
374
385
  # class Post < DBI::Model( :posts ); end
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' ] )
378
- h = DBI::DatabaseHandle.last_handle
386
+ # You can specify the primary key column(s) using an option, like so:
387
+ # class Author < DBI::Model( :authors, pk: [ 'auth_num' ] ); end
388
+ def self.Model( table, options = Hash.new )
389
+ h = options[ :dbh ] || DBI::DatabaseHandle.last_handle
379
390
  if h.nil? or not h.connected?
380
391
  raise DBI::Error.new( "Attempted to create a Model class without first connecting to a database." )
381
392
  end
393
+ pk_ = options[ :pk ] || [ 'id' ]
382
394
  if not pk_.respond_to? :each
383
395
  raise DBI::Error.new( "Primary key must be enumerable (was given #{pk_.inspect})" )
384
396
  end
@@ -400,18 +412,26 @@ module DBI
400
412
  :columns => h.columns( table.to_s ),
401
413
  } )
402
414
 
415
+ meta_def( 'pk_str'.to_sym ) do
416
+ if pk.size == 1
417
+ pk[ 0 ].to_s
418
+ else
419
+ pk.to_s
420
+ end
421
+ end
422
+
403
423
  if defined?( DBI::DBD::Pg::Database ) and DBI::DBD::Pg::Database === h.handle
404
424
  # TODO: This is broken for non-SERIAL or multi-column primary keys
405
425
  meta_def( "last_record".to_sym ) do |dbh_|
406
- self.s1 "SELECT * FROM #{table} WHERE #{pk} = currval( '#{table}_#{pk}_seq' );"
426
+ self.s1 "SELECT * FROM #{table} WHERE #{pk_str} = currval( '#{table}_#{pk_str}_seq' );"
407
427
  end
408
428
  elsif defined?( DBI::DBD::Mysql::Database ) and DBI::DBD::Mysql::Database === h.handle
409
429
  meta_def( "last_record".to_sym ) do |dbh_|
410
- self.s1 "SELECT * FROM #{table} WHERE #{pk} = LAST_INSERT_ID();"
430
+ self.s1 "SELECT * FROM #{table} WHERE #{pk_str} = LAST_INSERT_ID();"
411
431
  end
412
432
  elsif defined?( DBI::DBD::SQLite3::Database ) and DBI::DBD::SQLite3::Database === h.handle
413
433
  meta_def( "last_record".to_sym ) do |dbh_|
414
- self.s1 "SELECT * FROM #{table} WHERE #{pk} = last_insert_rowid();"
434
+ self.s1 "SELECT * FROM #{table} WHERE #{pk_str} = last_insert_rowid();"
415
435
  end
416
436
  # TODO: more DBDs
417
437
  end
data/lib/m4dbi/row.rb CHANGED
@@ -9,7 +9,7 @@ module DBI
9
9
  field = convert_alternate_fieldname( field )
10
10
  end
11
11
  if @column_names.include?( field )
12
- self[ field ] = *args
12
+ self[ field ] = args[ 0 ]
13
13
  else
14
14
  super
15
15
  end
@@ -27,7 +27,7 @@ module DBI
27
27
  end
28
28
  end
29
29
  end
30
-
30
+
31
31
  def convert_alternate_fieldname( field )
32
32
  field.gsub( /(^_)|(_$)/ , '' )
33
33
  end
data/lib/m4dbi.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
 
3
- M4DBI_VERSION = '0.6.1'
3
+ M4DBI_VERSION = '0.6.2'
4
4
 
5
5
  __DIR__ = File.expand_path( File.dirname( __FILE__ ) )
6
6
 
data/spec/dbi.rb CHANGED
@@ -4,31 +4,31 @@ $dbh = connect_to_spec_database
4
4
  reset_data
5
5
 
6
6
  describe 'DBI::DatabaseHandle#select_column' do
7
-
7
+
8
8
  it 'selects one column' do
9
9
  name = $dbh.select_column(
10
10
  "SELECT name FROM authors LIMIT 1"
11
11
  )
12
12
  name.class.should.not.equal Array
13
13
  name.should.equal 'author1'
14
-
14
+
15
15
  null = $dbh.select_column(
16
16
  "SELECT c4 FROM many_col_table WHERE c3 = 40"
17
17
  )
18
18
  null.should.be.nil
19
-
19
+
20
20
  should.raise( DBI::DataError ) do
21
21
  $dbh.select_column( "SELECT name FROM authors WHERE 1+1 = 3" )
22
22
  end
23
23
  end
24
-
24
+
25
25
  it 'selects one column of first row' do
26
26
  name = $dbh.select_column(
27
27
  "SELECT name FROM authors ORDER BY name DESC"
28
28
  )
29
29
  name.should.equal 'author3'
30
30
  end
31
-
31
+
32
32
  it 'selects first column of first row' do
33
33
  name = $dbh.select_column(
34
34
  "SELECT name, id FROM authors ORDER BY name DESC"
@@ -38,50 +38,54 @@ describe 'DBI::DatabaseHandle#select_column' do
38
38
  end
39
39
 
40
40
  describe 'DBI::DatabaseHandle#one_transaction' do
41
-
41
+
42
42
  it 'turns off autocommit for the duration of a single transaction' do
43
43
  $dbh.d( "DELETE FROM many_col_table;" )
44
44
  $dbh.i( "INSERT INTO many_col_table ( id, c1 ) VALUES ( 1, 10 );" )
45
-
45
+
46
46
  # Here we will attempt to increment a value two times in parallel.
47
47
  # If each multi-operation transaction is truly atomic, we expect that
48
48
  # the final value will reflect two increments.
49
49
  # If atomicity is not respected, the value should only reflect one
50
50
  # increment.
51
-
51
+
52
52
  # First, we test the non-transactional case, to show failure.
53
-
53
+
54
54
  thread1 = Thread.new do
55
55
  value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
56
56
  value.should.equal 10
57
- sleep 2 # seconds
57
+ sleep 1 # seconds
58
58
  $dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
59
59
  end
60
-
60
+
61
+ sleep 0.5
62
+
61
63
  thread2 = Thread.new do
62
64
  value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
63
65
  value.should.equal 10
64
66
  # Update right away
65
67
  $dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
66
68
  end
67
-
69
+
68
70
  thread2.join
69
71
  thread1.join
70
-
72
+
71
73
  value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
72
74
  # Failure; two increments should give a final value of 12.
73
75
  value.should.equal( 10 + 1 )
74
-
76
+
75
77
  # Now, we show that transactions keep things sane.
76
-
78
+
77
79
  thread1 = Thread.new do
78
80
  $dbh.one_transaction do |dbh|
79
81
  value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
80
- sleep 2 # seconds
82
+ sleep 1 # seconds
81
83
  dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
82
84
  end
83
85
  end
84
-
86
+
87
+ sleep 0.5
88
+
85
89
  thread2 = Thread.new do
86
90
  $dbh.one_transaction do |dbh|
87
91
  value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
@@ -89,50 +93,50 @@ describe 'DBI::DatabaseHandle#one_transaction' do
89
93
  dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
90
94
  end
91
95
  end
92
-
96
+
93
97
  thread2.join
94
98
  thread1.join
95
-
99
+
96
100
  value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
97
101
  value.should.equal( 11 + 1 + 1 )
98
-
102
+
99
103
  reset_data
100
104
  end
101
-
105
+
102
106
  end
103
107
 
104
108
  describe 'DBI::Row accessors' do
105
-
109
+
106
110
  it 'provide read access via #fieldname' do
107
111
  row = $dbh.select_one(
108
112
  "SELECT * FROM posts ORDER BY author_id DESC LIMIT 1"
109
113
  )
110
114
  row.should.not.equal nil
111
-
115
+
112
116
  row._id.should.be.same_as row[ 'id' ]
113
117
  row.id_.should.be.same_as row[ 'id' ]
114
118
  row.author_id.should.be.same_as row[ 'author_id' ]
115
119
  row.text.should.be.same_as row[ 'text' ]
116
-
120
+
117
121
  row.text.should.equal 'Second post.'
118
122
  end
119
-
123
+
120
124
  it 'provide in-memory (non-syncing) write access via #fieldname=' do
121
125
  row = $dbh.select_one(
122
126
  "SELECT * FROM posts ORDER BY author_id DESC LIMIT 1"
123
127
  )
124
128
  row.should.not.equal nil
125
-
129
+
126
130
  old_id = row._id
127
131
  row.id = old_id + 1
128
132
  row._id.should.not.equal old_id
129
133
  row._id.should.equal( old_id + 1 )
130
-
134
+
131
135
  old_text = row.text
132
136
  new_text = 'This is the new post text.'
133
137
  row.text = new_text
134
138
  row.text.should.not.equal old_text
135
139
  row.text.should.equal new_text
136
140
  end
137
-
141
+
138
142
  end
data/spec/model.rb CHANGED
@@ -34,8 +34,8 @@ describe 'A DBI::Model subclass' do
34
34
  @m_post = Class.new( DBI::Model( :posts ) )
35
35
  @m_empty = Class.new( DBI::Model( :empty_table ) )
36
36
  @m_mc = Class.new( DBI::Model( :many_col_table ) )
37
- @m_nipk = Class.new( DBI::Model( :non_id_pk, [ :str ] ) )
38
- @m_mcpk = Class.new( DBI::Model( :mcpk, [ :kc1, :kc2 ] ) )
37
+ @m_nipk = Class.new( DBI::Model( :non_id_pk, :pk => [ :str ] ) )
38
+ @m_mcpk = Class.new( DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
39
39
  class Author < DBI::Model( :authors ); end
40
40
  end
41
41
 
@@ -90,6 +90,7 @@ describe 'A DBI::Model subclass' do
90
90
  reset_data( dbh, "test-data2.sql" )
91
91
 
92
92
  @m_author2 = Class.new( DBI::Model( :authors ) )
93
+ @m_author2.dbh.should.equal dbh
93
94
 
94
95
  @m_author2[ 1 ].should.be.nil
95
96
  a11 = @m_author2[ 11 ]
@@ -101,7 +102,34 @@ describe 'A DBI::Model subclass' do
101
102
  a2.name.should.equal 'author2'
102
103
  ensure
103
104
  # Clean up handles for later specs
104
- dbh.disconnect if dbh and dbh.connected?
105
+ # puts dbh.object_id
106
+ # dbh.disconnect if dbh and dbh.connected?
107
+ connect_to_spec_database
108
+ end
109
+ end
110
+
111
+ it 'can use a specific database handle' do
112
+ begin
113
+ dbh1 = connect_to_spec_database
114
+ dbh1.should.equal DBI::DatabaseHandle.last_handle
115
+ dbh2 = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
116
+ dbh2.should.equal DBI::DatabaseHandle.last_handle
117
+ reset_data( dbh2, "test-data2.sql" )
118
+
119
+ dbh1.should.not.equal dbh2
120
+
121
+ class Author1 < DBI::Model( :authors, :dbh => dbh1 ); end
122
+ class Author2 < DBI::Model( :authors, :dbh => dbh2 ); end
123
+
124
+ a1 = Author1[ 1 ]
125
+ a1.should.not.be.nil
126
+ a1.name.should.equal 'author1'
127
+
128
+ a11 = Author2[ 11 ]
129
+ a11.should.not.be.nil
130
+ a11.name.should.equal 'author11'
131
+ ensure
132
+ # Clean up handles for later specs
105
133
  connect_to_spec_database
106
134
  end
107
135
  end
@@ -348,6 +376,22 @@ describe 'A DBI::Model subclass' do
348
376
  a_.should.equal a
349
377
  a_.name.should.equal 'author9'
350
378
 
379
+ # No value given for auto-incrementing primary key
380
+ a = @m_author.create(
381
+ :name => 'author10'
382
+ )
383
+ a.should.not.be.nil
384
+ a.class.should.equal @m_author
385
+ a.id.should.not.equal 9
386
+ a.should.respond_to :name
387
+ a.should.not.respond_to :no_column_by_this_name
388
+ a.name.should.equal 'author10'
389
+
390
+ a_ = @m_author[ a.id ]
391
+ a_.should.not.be.nil
392
+ a_.should.equal a
393
+ a_.name.should.equal 'author10'
394
+
351
395
  reset_data
352
396
  end
353
397
 
@@ -658,8 +702,8 @@ describe 'A found DBI::Model subclass instance' do
658
702
  @m_author = Class.new( DBI::Model( :authors ) )
659
703
  @m_post = Class.new( DBI::Model( :posts ) )
660
704
  @m_mc = Class.new( DBI::Model( :many_col_table ) )
661
- @m_nipk = Class.new( DBI::Model( :non_id_pk, [ :str ] ) )
662
- @m_mcpk = Class.new( DBI::Model( :mcpk, [ :kc1, :kc2 ] ) )
705
+ @m_nipk = Class.new( DBI::Model( :non_id_pk, :pk => [ :str ] ) )
706
+ @m_mcpk = Class.new( DBI::Model( :mcpk, :pk => [ :kc1, :kc2 ] ) )
663
707
  end
664
708
 
665
709
  it 'provides access to primary key value' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: m4dbi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pistos
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-08 00:00:00 -05:00
12
+ date: 2009-07-10 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -48,21 +48,23 @@ files:
48
48
  - READHIM
49
49
  - CHANGELOG
50
50
  - LICENCE
51
- - lib/m4dbi/array.rb
52
- - lib/m4dbi/collection.rb
51
+ - lib/m4dbi.rb
53
52
  - lib/m4dbi/row.rb
54
- - lib/m4dbi/timestamp.rb
53
+ - lib/m4dbi/array.rb
55
54
  - lib/m4dbi/traits.rb
56
- - lib/m4dbi/database-handle.rb
55
+ - lib/m4dbi/collection.rb
57
56
  - lib/m4dbi/hash.rb
57
+ - lib/m4dbi/database-handle.rb
58
58
  - lib/m4dbi/model.rb
59
- - lib/m4dbi.rb
60
- - spec/model.rb
59
+ - lib/m4dbi/timestamp.rb
61
60
  - spec/dbi.rb
62
61
  - spec/hash.rb
62
+ - spec/model.rb
63
63
  - spec/helper.rb
64
- has_rdoc: false
64
+ has_rdoc: true
65
65
  homepage: http://purepistos.net/m4dbi
66
+ licenses: []
67
+
66
68
  post_install_message:
67
69
  rdoc_options: []
68
70
 
@@ -83,12 +85,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
85
  requirements:
84
86
  - bacon (optional)
85
87
  rubyforge_project: m4dbi
86
- rubygems_version: 1.3.1
88
+ rubygems_version: 1.3.4
87
89
  signing_key:
88
- specification_version: 2
90
+ specification_version: 3
89
91
  summary: Models (and More) for DBI
90
92
  test_files:
91
- - spec/model.rb
92
93
  - spec/dbi.rb
93
94
  - spec/hash.rb
95
+ - spec/model.rb
94
96
  - spec/helper.rb