m4dbi 0.6.2 → 0.7.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/{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
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: m4dbi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 0.7.0
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Pistos
|
@@ -9,58 +10,46 @@ autorequire:
|
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2011-08-14 00:00:00 -04:00
|
13
14
|
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: metaid
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
20
21
|
requirements:
|
21
22
|
- - ">="
|
22
23
|
- !ruby/object:Gem::Version
|
23
24
|
version: "0"
|
24
|
-
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: dbi
|
27
25
|
type: :runtime
|
28
|
-
|
29
|
-
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: "0"
|
34
|
-
version:
|
35
|
-
description: M4DBI provides models, associations and some convenient extensions to Ruby DBI.
|
26
|
+
version_requirements: *id001
|
27
|
+
description: M4DBI provides models, associations and some convenient extensions to RDBI.
|
36
28
|
email: pistos at purepistos dot net
|
37
29
|
executables: []
|
38
30
|
|
39
31
|
extensions: []
|
40
32
|
|
41
33
|
extra_rdoc_files:
|
42
|
-
-
|
43
|
-
- READHIM
|
34
|
+
- README
|
44
35
|
- CHANGELOG
|
45
36
|
- LICENCE
|
46
37
|
files:
|
47
|
-
-
|
48
|
-
- READHIM
|
38
|
+
- README
|
49
39
|
- CHANGELOG
|
50
40
|
- LICENCE
|
51
|
-
- lib/m4dbi.rb
|
52
|
-
- lib/m4dbi/
|
53
|
-
- lib/m4dbi/array.rb
|
41
|
+
- lib/m4dbi/database.rb
|
42
|
+
- lib/m4dbi/model.rb
|
54
43
|
- lib/m4dbi/traits.rb
|
55
44
|
- lib/m4dbi/collection.rb
|
56
45
|
- lib/m4dbi/hash.rb
|
57
|
-
- lib/m4dbi/
|
58
|
-
- lib/m4dbi/
|
59
|
-
- lib/m4dbi
|
60
|
-
- spec/
|
61
|
-
- spec/hash.rb
|
46
|
+
- lib/m4dbi/array.rb
|
47
|
+
- lib/m4dbi/error.rb
|
48
|
+
- lib/m4dbi.rb
|
49
|
+
- spec/database.rb
|
62
50
|
- spec/model.rb
|
63
51
|
- spec/helper.rb
|
52
|
+
- spec/hash.rb
|
64
53
|
has_rdoc: true
|
65
54
|
homepage: http://purepistos.net/m4dbi
|
66
55
|
licenses: []
|
@@ -71,26 +60,27 @@ rdoc_options: []
|
|
71
60
|
require_paths:
|
72
61
|
- lib
|
73
62
|
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
74
64
|
requirements:
|
75
65
|
- - ">="
|
76
66
|
- !ruby/object:Gem::Version
|
77
67
|
version: "0"
|
78
|
-
version:
|
79
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
80
70
|
requirements:
|
81
71
|
- - ">="
|
82
72
|
- !ruby/object:Gem::Version
|
83
73
|
version: "0"
|
84
|
-
version:
|
85
74
|
requirements:
|
75
|
+
- rdbi
|
86
76
|
- bacon (optional)
|
87
|
-
rubyforge_project:
|
88
|
-
rubygems_version: 1.
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.6.2
|
89
79
|
signing_key:
|
90
80
|
specification_version: 3
|
91
|
-
summary: Models (and More) for
|
81
|
+
summary: Models (and More) for RDBI
|
92
82
|
test_files:
|
93
|
-
- spec/
|
94
|
-
- spec/hash.rb
|
83
|
+
- spec/database.rb
|
95
84
|
- spec/model.rb
|
96
85
|
- spec/helper.rb
|
86
|
+
- spec/hash.rb
|
data/HIM
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
== M4DBI - Models (and more) For DBI
|
2
|
-
|
3
|
-
http://purepistos.net/m4dbi
|
4
|
-
|
5
|
-
M4DBI is a Ruby library that provides ORM modelling to the Ruby DBI package
|
6
|
-
( http://ruby-dbi.rubyforge.org/ ).
|
7
|
-
|
8
|
-
=== Dependencies
|
9
|
-
|
10
|
-
- dbi (of course) http://ruby-dbi.rubyforge.org/
|
11
|
-
- DBDs for your choice of DBs
|
12
|
-
- metaid (gem install metaid)
|
13
|
-
- bacon (optional, for running tests; gem install bacon)
|
14
|
-
|
15
|
-
=== Installation
|
16
|
-
|
17
|
-
==== Nightly Gem
|
18
|
-
|
19
|
-
wget http://rome.purepistos.net/m4dbi/m4dbi-nightly.gem
|
20
|
-
gem install m4dbi-nightly.gem
|
21
|
-
|
22
|
-
==== Repository
|
23
|
-
|
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
|
-
|
27
|
-
cd /where/you/want/m4dbi
|
28
|
-
git clone git://github.com/Pistos/m4dbi.git
|
29
|
-
|
30
|
-
Change the following to whatever the equivalent paths are for your system:
|
31
|
-
|
32
|
-
cd /usr/lib/ruby/site_ruby/1.8
|
33
|
-
ln -s /path/to/cloned/m4dbi/lib/m4dbi
|
34
|
-
ln -s /path/to/cloned/m4dbi/lib/m4dbi.rb
|
35
|
-
|
36
|
-
=== Usage
|
37
|
-
|
38
|
-
See http://rome.purepistos.net/m4dbi/examples/ . These are automatically
|
39
|
-
generated from the spec files under the spec/ dir.
|
40
|
-
|
41
|
-
=== Source Code
|
42
|
-
|
43
|
-
Browse source at http://github.com/Pistos/m4dbi/tree/master .
|
44
|
-
See coverage at http://rome.purepistos.net/m4dbi/rcov .
|
45
|
-
Very limited rdocs at http://rome.purepistos.net/m4dbi/rdoc .
|
46
|
-
|
47
|
-
=== Feedback and Support
|
48
|
-
|
49
|
-
On IRC: irc.freenode.net ##mathetes or ##ramaze .
|
50
|
-
Use http://mibbit.com if you don't have an IRC client.
|
@@ -1,117 +0,0 @@
|
|
1
|
-
require 'dbi'
|
2
|
-
require 'thread'
|
3
|
-
|
4
|
-
module DBI
|
5
|
-
|
6
|
-
# Here, we engage in some hackery to get database handles to provide us
|
7
|
-
# with the name of the database connected to. For mystical reasons, this
|
8
|
-
# is hidden in normal DBI.
|
9
|
-
# Retrieve the database name with DatabaseHandle#dbname.
|
10
|
-
module DBD; module Pg
|
11
|
-
module ConnectionDatabaseNameAccessor
|
12
|
-
def dbname
|
13
|
-
@connection.db
|
14
|
-
end
|
15
|
-
end
|
16
|
-
module DatabaseNameAccessor
|
17
|
-
def dbname
|
18
|
-
@handle.dbname
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end; end
|
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
|
-
|
39
|
-
class DatabaseHandle
|
40
|
-
attr_reader :transactions
|
41
|
-
|
42
|
-
alias old_initialize initialize
|
43
|
-
def initialize( *args )
|
44
|
-
DBI::DatabaseHandle.last_handle = self
|
45
|
-
handle = old_initialize( *args )
|
46
|
-
@mutex = Mutex.new
|
47
|
-
@transactions = Array.new
|
48
|
-
|
49
|
-
# Hackery to expose dbname.
|
50
|
-
if defined?( DBI::DBD::Pg::Database ) and ( DBI::DBD::Pg::Database === @handle )
|
51
|
-
@handle.extend DBI::DBD::Pg::ConnectionDatabaseNameAccessor
|
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
|
57
|
-
end
|
58
|
-
# TODO: more DBDs
|
59
|
-
|
60
|
-
handle
|
61
|
-
end
|
62
|
-
|
63
|
-
# Atomically disable autocommit, do transaction, and reenable.
|
64
|
-
# Used for a single transaction when autocommit is normally left on.
|
65
|
-
# Only one thread can execute one_transaction at a time,
|
66
|
-
# since we need to thread protect the AutoCommit property of the
|
67
|
-
# database handle.
|
68
|
-
def one_transaction
|
69
|
-
@mutex.synchronize do
|
70
|
-
# Keep track of transactions for debugging purposes
|
71
|
-
transaction = { :time => ::Time.now, :stack => caller }
|
72
|
-
@transactions << transaction
|
73
|
-
|
74
|
-
auto_commit = self[ 'AutoCommit' ]
|
75
|
-
self[ 'AutoCommit' ] = false
|
76
|
-
result = transaction do
|
77
|
-
yield self
|
78
|
-
end
|
79
|
-
self[ 'AutoCommit' ] = auto_commit
|
80
|
-
|
81
|
-
@transactions.delete transaction
|
82
|
-
result
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def select_column( statement, *bindvars )
|
87
|
-
row = select_one( statement, *bindvars )
|
88
|
-
if row
|
89
|
-
row[ 0 ]
|
90
|
-
else
|
91
|
-
raise DBI::DataError.new( "Query returned no rows." )
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
alias s select_all
|
96
|
-
alias s1 select_one
|
97
|
-
alias sc select_column
|
98
|
-
alias u do
|
99
|
-
alias i do
|
100
|
-
alias d do
|
101
|
-
|
102
|
-
class << self
|
103
|
-
def last_handle
|
104
|
-
@handle# ||= create_handle
|
105
|
-
end
|
106
|
-
|
107
|
-
def last_handle=( handle )
|
108
|
-
@handle = handle
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
data/lib/m4dbi/row.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'dbi'
|
2
|
-
|
3
|
-
module DBI
|
4
|
-
class Row
|
5
|
-
def method_missing( method, *args )
|
6
|
-
if method.to_s =~ /^(.+)=$/
|
7
|
-
field = $1
|
8
|
-
if not @column_names.include?( field )
|
9
|
-
field = convert_alternate_fieldname( field )
|
10
|
-
end
|
11
|
-
if @column_names.include?( field )
|
12
|
-
self[ field ] = args[ 0 ]
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
else
|
17
|
-
field = method.to_s
|
18
|
-
# We shouldn't use by_field directly and test for nil,
|
19
|
-
# because nil may be a valid value for the column.
|
20
|
-
if not @column_names.include?( field )
|
21
|
-
field = convert_alternate_fieldname( field )
|
22
|
-
end
|
23
|
-
if @column_names.include?( field )
|
24
|
-
by_field field
|
25
|
-
else
|
26
|
-
super
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def convert_alternate_fieldname( field )
|
32
|
-
field.gsub( /(^_)|(_$)/ , '' )
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
data/lib/m4dbi/timestamp.rb
DELETED
@@ -1,20 +0,0 @@
|
|
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
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
require 'spec/helper'
|
2
|
-
|
3
|
-
$dbh = connect_to_spec_database
|
4
|
-
reset_data
|
5
|
-
|
6
|
-
describe 'DBI::DatabaseHandle#select_column' do
|
7
|
-
|
8
|
-
it 'selects one column' do
|
9
|
-
name = $dbh.select_column(
|
10
|
-
"SELECT name FROM authors LIMIT 1"
|
11
|
-
)
|
12
|
-
name.class.should.not.equal Array
|
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
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'selects one column of first row' do
|
26
|
-
name = $dbh.select_column(
|
27
|
-
"SELECT name FROM authors ORDER BY name DESC"
|
28
|
-
)
|
29
|
-
name.should.equal 'author3'
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'selects first column of first row' do
|
33
|
-
name = $dbh.select_column(
|
34
|
-
"SELECT name, id FROM authors ORDER BY name DESC"
|
35
|
-
)
|
36
|
-
name.should.equal 'author3'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe 'DBI::DatabaseHandle#one_transaction' do
|
41
|
-
|
42
|
-
it 'turns off autocommit for the duration of a single transaction' do
|
43
|
-
$dbh.d( "DELETE FROM many_col_table;" )
|
44
|
-
$dbh.i( "INSERT INTO many_col_table ( id, c1 ) VALUES ( 1, 10 );" )
|
45
|
-
|
46
|
-
# Here we will attempt to increment a value two times in parallel.
|
47
|
-
# If each multi-operation transaction is truly atomic, we expect that
|
48
|
-
# the final value will reflect two increments.
|
49
|
-
# If atomicity is not respected, the value should only reflect one
|
50
|
-
# increment.
|
51
|
-
|
52
|
-
# First, we test the non-transactional case, to show failure.
|
53
|
-
|
54
|
-
thread1 = Thread.new do
|
55
|
-
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
56
|
-
value.should.equal 10
|
57
|
-
sleep 1 # seconds
|
58
|
-
$dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
59
|
-
end
|
60
|
-
|
61
|
-
sleep 0.5
|
62
|
-
|
63
|
-
thread2 = Thread.new do
|
64
|
-
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
65
|
-
value.should.equal 10
|
66
|
-
# Update right away
|
67
|
-
$dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
68
|
-
end
|
69
|
-
|
70
|
-
thread2.join
|
71
|
-
thread1.join
|
72
|
-
|
73
|
-
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
74
|
-
# Failure; two increments should give a final value of 12.
|
75
|
-
value.should.equal( 10 + 1 )
|
76
|
-
|
77
|
-
# Now, we show that transactions keep things sane.
|
78
|
-
|
79
|
-
thread1 = Thread.new do
|
80
|
-
$dbh.one_transaction do |dbh|
|
81
|
-
value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
82
|
-
sleep 1 # seconds
|
83
|
-
dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
sleep 0.5
|
88
|
-
|
89
|
-
thread2 = Thread.new do
|
90
|
-
$dbh.one_transaction do |dbh|
|
91
|
-
value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
92
|
-
# Update right away
|
93
|
-
dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
thread2.join
|
98
|
-
thread1.join
|
99
|
-
|
100
|
-
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
101
|
-
value.should.equal( 11 + 1 + 1 )
|
102
|
-
|
103
|
-
reset_data
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
describe 'DBI::Row accessors' do
|
109
|
-
|
110
|
-
it 'provide read access via #fieldname' do
|
111
|
-
row = $dbh.select_one(
|
112
|
-
"SELECT * FROM posts ORDER BY author_id DESC LIMIT 1"
|
113
|
-
)
|
114
|
-
row.should.not.equal nil
|
115
|
-
|
116
|
-
row._id.should.be.same_as row[ 'id' ]
|
117
|
-
row.id_.should.be.same_as row[ 'id' ]
|
118
|
-
row.author_id.should.be.same_as row[ 'author_id' ]
|
119
|
-
row.text.should.be.same_as row[ 'text' ]
|
120
|
-
|
121
|
-
row.text.should.equal 'Second post.'
|
122
|
-
end
|
123
|
-
|
124
|
-
it 'provide in-memory (non-syncing) write access via #fieldname=' do
|
125
|
-
row = $dbh.select_one(
|
126
|
-
"SELECT * FROM posts ORDER BY author_id DESC LIMIT 1"
|
127
|
-
)
|
128
|
-
row.should.not.equal nil
|
129
|
-
|
130
|
-
old_id = row._id
|
131
|
-
row.id = old_id + 1
|
132
|
-
row._id.should.not.equal old_id
|
133
|
-
row._id.should.equal( old_id + 1 )
|
134
|
-
|
135
|
-
old_text = row.text
|
136
|
-
new_text = 'This is the new post text.'
|
137
|
-
row.text = new_text
|
138
|
-
row.text.should.not.equal old_text
|
139
|
-
row.text.should.equal new_text
|
140
|
-
end
|
141
|
-
|
142
|
-
end
|