m4dbi 0.5.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 +3 -0
- data/HIM +50 -0
- data/READHIM +1 -0
- data/lib/m4dbi/collection.rb +69 -0
- data/lib/m4dbi/database-handle.rb +86 -0
- data/lib/m4dbi/hash.rb +21 -0
- data/lib/m4dbi/model.rb +372 -0
- data/lib/m4dbi/row.rb +35 -0
- data/lib/m4dbi/traits.rb +91 -0
- data/lib/m4dbi.rb +8 -0
- data/spec/dbi.rb +128 -0
- data/spec/hash.rb +27 -0
- data/spec/helper.rb +13 -0
- data/spec/model.rb +797 -0
- metadata +80 -0
data/lib/m4dbi/traits.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# (taken from http://ramaze.net)
|
2
|
+
|
3
|
+
# Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
|
4
|
+
# All files in this distribution are subject to the terms of the Ruby license.
|
5
|
+
|
6
|
+
Traits = Hash.new{|h,k| h[k] = {}} unless defined?(Traits)
|
7
|
+
|
8
|
+
# Extensions for Object
|
9
|
+
|
10
|
+
class Object
|
11
|
+
|
12
|
+
# Adds a method to Object to annotate your objects with certain traits.
|
13
|
+
# It's basically a simple Hash that takes the current object as key
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
#
|
17
|
+
# class Foo
|
18
|
+
# trait :instance => false
|
19
|
+
#
|
20
|
+
# def initialize
|
21
|
+
# trait :instance => true
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Foo.trait[:instance]
|
26
|
+
# # false
|
27
|
+
#
|
28
|
+
# foo = Foo.new
|
29
|
+
# foo.trait[:instance]
|
30
|
+
# # true
|
31
|
+
|
32
|
+
def trait hash = nil
|
33
|
+
if hash
|
34
|
+
Traits[self].merge! hash
|
35
|
+
else
|
36
|
+
Traits[self]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# builds a trait from all the ancestors, closer ancestors
|
41
|
+
# overwrite distant ancestors
|
42
|
+
#
|
43
|
+
# class Foo
|
44
|
+
# trait :one => :eins
|
45
|
+
# trait :first => :erstes
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# class Bar < Foo
|
49
|
+
# trait :two => :zwei
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# class Foobar < Bar
|
53
|
+
# trait :three => :drei
|
54
|
+
# trait :first => :overwritten
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Foobar.ancestral_trait
|
58
|
+
# {:three=>:drei, :two=>:zwei, :one=>:eins, :first=>:overwritten}
|
59
|
+
|
60
|
+
def ancestral_trait
|
61
|
+
if respond_to?(:ancestors)
|
62
|
+
ancs = ancestors
|
63
|
+
else
|
64
|
+
ancs = self.class.ancestors
|
65
|
+
end
|
66
|
+
ancs.reverse.inject({}){|s,v| s.merge(v.trait)}.merge(trait)
|
67
|
+
end
|
68
|
+
|
69
|
+
# trait for self.class
|
70
|
+
|
71
|
+
#def class_trait
|
72
|
+
#if respond_to?(:ancestors)
|
73
|
+
#trait
|
74
|
+
#else
|
75
|
+
#self.class.trait
|
76
|
+
#end
|
77
|
+
#end
|
78
|
+
|
79
|
+
def ancestral_trait_reader( *names )
|
80
|
+
names.each do |name|
|
81
|
+
name = name.to_sym
|
82
|
+
define_method( name ) { ancestral_trait[ name ] }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
def ancestral_trait_class_reader( *names )
|
86
|
+
names.each do |name|
|
87
|
+
name = name.to_sym
|
88
|
+
meta_def( name ) { ancestral_trait[ name ] }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/m4dbi.rb
ADDED
data/spec/dbi.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
$dbh = DBI.connect( "DBI:Pg:m4dbi", "m4dbi", "m4dbi" )
|
4
|
+
# See test-schema.sql and test-data.sql
|
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
|
+
end
|
15
|
+
|
16
|
+
it 'selects one column of first row' do
|
17
|
+
name = $dbh.select_column(
|
18
|
+
"SELECT name FROM authors ORDER BY name DESC"
|
19
|
+
)
|
20
|
+
name.should.equal 'author3'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'selects first column of first row' do
|
24
|
+
name = $dbh.select_column(
|
25
|
+
"SELECT name, id FROM authors ORDER BY name DESC"
|
26
|
+
)
|
27
|
+
name.should.equal 'author3'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'DBI::DatabaseHandle#one_transaction' do
|
33
|
+
|
34
|
+
it 'turns off autocommit for the duration of a single transaction' do
|
35
|
+
$dbh.d( "DELETE FROM many_col_table;" )
|
36
|
+
$dbh.i( "INSERT INTO many_col_table ( id, c1 ) VALUES ( 1, 10 );" )
|
37
|
+
|
38
|
+
# Here we will attempt to increment a value two times in parallel.
|
39
|
+
# If each multi-operation transaction is truly atomic, we expect that
|
40
|
+
# the final value will reflect two increments.
|
41
|
+
# If atomicity is not respected, the value should only reflect one
|
42
|
+
# increment.
|
43
|
+
|
44
|
+
# First, we test the non-transactional case, to show failure.
|
45
|
+
|
46
|
+
thread1 = Thread.new do
|
47
|
+
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
48
|
+
value.should.equal 10
|
49
|
+
sleep 2 # seconds
|
50
|
+
$dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
51
|
+
end
|
52
|
+
|
53
|
+
thread2 = Thread.new do
|
54
|
+
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
55
|
+
value.should.equal 10
|
56
|
+
# Update right away
|
57
|
+
$dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
58
|
+
end
|
59
|
+
|
60
|
+
thread2.join
|
61
|
+
thread1.join
|
62
|
+
|
63
|
+
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
64
|
+
# Failure; two increments should give a final value of 12.
|
65
|
+
value.should.equal( 10 + 1 )
|
66
|
+
|
67
|
+
# Now, we show that transactions keep things sane.
|
68
|
+
|
69
|
+
thread1 = Thread.new do
|
70
|
+
$dbh.one_transaction do |dbh|
|
71
|
+
value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
72
|
+
sleep 2 # seconds
|
73
|
+
dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
thread2 = Thread.new do
|
78
|
+
$dbh.one_transaction do |dbh|
|
79
|
+
value = dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
80
|
+
# Update right away
|
81
|
+
dbh.u "UPDATE many_col_table SET c1 = ?", ( value + 1 )
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
thread2.join
|
86
|
+
thread1.join
|
87
|
+
|
88
|
+
value = $dbh.sc "SELECT c1 FROM many_col_table WHERE id = 1;"
|
89
|
+
value.should.equal( 11 + 1 + 1 )
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'DBI::Row accessors' do
|
95
|
+
|
96
|
+
it 'provide read access via #fieldname' do
|
97
|
+
row = $dbh.select_one(
|
98
|
+
"SELECT * FROM posts ORDER BY author_id DESC LIMIT 1"
|
99
|
+
)
|
100
|
+
row.should.not.equal nil
|
101
|
+
|
102
|
+
row._id.should.be.same_as row[ 'id' ]
|
103
|
+
row.id_.should.be.same_as row[ 'id' ]
|
104
|
+
row.author_id.should.be.same_as row[ 'author_id' ]
|
105
|
+
row.text.should.be.same_as row[ 'text' ]
|
106
|
+
|
107
|
+
row.text.should.equal 'Second post.'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'provide in-memory (non-syncing) write 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
|
+
old_id = row._id
|
117
|
+
row.id = old_id + 1
|
118
|
+
row._id.should.not.equal old_id
|
119
|
+
row._id.should.equal( old_id + 1 )
|
120
|
+
|
121
|
+
old_text = row.text
|
122
|
+
new_text = 'This is the new post text.'
|
123
|
+
row.text = new_text
|
124
|
+
row.text.should.not.equal old_text
|
125
|
+
row.text.should.equal new_text
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/spec/hash.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
describe 'Hash' do
|
4
|
+
it 'is convertible to an SQL subclause and matching value Array' do
|
5
|
+
h = {
|
6
|
+
:a => 2,
|
7
|
+
:b => 'foo',
|
8
|
+
:abc => Time.now,
|
9
|
+
:xyz => 9.02,
|
10
|
+
}
|
11
|
+
clause, values = h.to_clause( " AND " )
|
12
|
+
where_clause, where_values = h.to_where_clause
|
13
|
+
|
14
|
+
s = "a = ? AND b = ? AND abc = ? AND xyz = ?"
|
15
|
+
clause.length.should.equal s.length
|
16
|
+
where_clause.length.should.equal s.length
|
17
|
+
|
18
|
+
h.each do |key,value|
|
19
|
+
clause.should.match /#{key} = ?/
|
20
|
+
where_clause.should.match /#{key} = ?/
|
21
|
+
values.find { |v| v == value }.should.not.be.nil
|
22
|
+
where_values.find { |v| v == value }.should.not.be.nil
|
23
|
+
end
|
24
|
+
values.size.should.equal h.keys.size
|
25
|
+
where_values.size.should.equal h.keys.size
|
26
|
+
end
|
27
|
+
end
|