data_objects 0.10.3 → 0.10.4.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.markdown +1 -1
- data/Rakefile +6 -32
- data/lib/data_objects/connection.rb +9 -6
- data/lib/data_objects/pooling.rb +1 -1
- data/lib/data_objects/spec/lib/pending_helpers.rb +11 -0
- data/lib/data_objects/spec/{helpers → lib}/ssl.rb +0 -0
- data/lib/data_objects/spec/setup.rb +5 -0
- data/lib/data_objects/spec/{command_spec.rb → shared/command_spec.rb} +54 -50
- data/lib/data_objects/spec/shared/connection_spec.rb +245 -0
- data/lib/data_objects/spec/{encoding_spec.rb → shared/encoding_spec.rb} +41 -43
- data/lib/data_objects/spec/shared/error/sql_error_spec.rb +30 -0
- data/lib/data_objects/spec/{quoting_spec.rb → shared/quoting_spec.rb} +0 -0
- data/lib/data_objects/spec/{reader_spec.rb → shared/reader_spec.rb} +31 -26
- data/lib/data_objects/spec/shared/result_spec.rb +79 -0
- data/lib/data_objects/spec/{typecast → shared/typecast}/array_spec.rb +4 -2
- data/lib/data_objects/spec/{typecast → shared/typecast}/bigdecimal_spec.rb +12 -8
- data/lib/data_objects/spec/{typecast → shared/typecast}/boolean_spec.rb +14 -10
- data/lib/data_objects/spec/{typecast → shared/typecast}/byte_array_spec.rb +6 -4
- data/lib/data_objects/spec/{typecast → shared/typecast}/class_spec.rb +5 -3
- data/lib/data_objects/spec/{typecast → shared/typecast}/date_spec.rb +13 -9
- data/lib/data_objects/spec/{typecast → shared/typecast}/datetime_spec.rb +14 -11
- data/lib/data_objects/spec/{typecast → shared/typecast}/float_spec.rb +17 -13
- data/lib/data_objects/spec/{typecast → shared/typecast}/integer_spec.rb +7 -5
- data/lib/data_objects/spec/{typecast → shared/typecast}/ipaddr_spec.rb +0 -0
- data/lib/data_objects/spec/{typecast → shared/typecast}/nil_spec.rb +23 -17
- data/lib/data_objects/spec/{typecast → shared/typecast}/other_spec.rb +11 -9
- data/lib/data_objects/spec/{typecast → shared/typecast}/range_spec.rb +4 -2
- data/lib/data_objects/spec/{typecast → shared/typecast}/string_spec.rb +10 -8
- data/lib/data_objects/spec/{typecast → shared/typecast}/time_spec.rb +44 -6
- data/lib/data_objects/transaction.rb +9 -0
- data/lib/data_objects/uri.rb +62 -4
- data/lib/data_objects/version.rb +1 -1
- data/spec/command_spec.rb +2 -2
- data/spec/connection_spec.rb +45 -25
- data/spec/pooling_spec.rb +9 -9
- data/spec/reader_spec.rb +11 -12
- data/spec/result_spec.rb +13 -11
- data/spec/spec_helper.rb +1 -16
- data/spec/transaction_spec.rb +9 -11
- data/spec/uri_spec.rb +35 -27
- data/tasks/spec.rake +8 -17
- metadata +40 -56
- data/lib/data_objects/spec/bacon.rb +0 -9
- data/lib/data_objects/spec/connection_spec.rb +0 -217
- data/lib/data_objects/spec/error/sql_error_spec.rb +0 -19
- data/lib/data_objects/spec/helpers/immediate_red_green_output.rb +0 -59
- data/lib/data_objects/spec/helpers/pending.rb +0 -22
- data/lib/data_objects/spec/result_spec.rb +0 -79
- data/tasks/metrics.rake +0 -36
@@ -10,20 +10,22 @@ class ::CustomTextType
|
|
10
10
|
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
shared_examples_for 'supporting other (unknown) type' do
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
before do
|
18
|
-
@connection = DataObjects::Connection.new(CONFIG.uri)
|
19
|
-
end
|
20
|
-
|
21
|
-
after do
|
22
|
-
@connection.close
|
15
|
+
before :all do
|
16
|
+
setup_test_environment
|
23
17
|
end
|
24
18
|
|
25
19
|
describe 'writing an object of unknown type' do
|
26
20
|
|
21
|
+
before do
|
22
|
+
@connection = DataObjects::Connection.new(CONFIG.uri)
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
@connection.close
|
27
|
+
end
|
28
|
+
|
27
29
|
before do
|
28
30
|
@command = @connection.create_command("SELECT ad_description FROM widgets WHERE ad_description = ?")
|
29
31
|
@command.set_types(::CustomTextType)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
shared_examples_for 'supporting String' do
|
4
4
|
|
5
|
-
|
5
|
+
before :all do
|
6
|
+
setup_test_environment
|
7
|
+
end
|
6
8
|
|
7
9
|
before do
|
8
10
|
@connection = DataObjects::Connection.new(CONFIG.uri)
|
@@ -27,7 +29,7 @@ shared 'supporting String' do
|
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'should return the correctly typed result' do
|
30
|
-
@values.first.should
|
32
|
+
@values.first.should be_kind_of(String)
|
31
33
|
end
|
32
34
|
|
33
35
|
it 'should return the correct result' do
|
@@ -51,7 +53,7 @@ shared 'supporting String' do
|
|
51
53
|
end
|
52
54
|
|
53
55
|
it 'should return the correctly typed result' do
|
54
|
-
@values.first.should
|
56
|
+
@values.first.should be_kind_of(String)
|
55
57
|
end
|
56
58
|
|
57
59
|
it 'should return the correct result' do
|
@@ -76,7 +78,7 @@ shared 'supporting String' do
|
|
76
78
|
|
77
79
|
it 'should return the correct entry' do
|
78
80
|
# Some of the drivers starts autoincrementation from 0 not 1
|
79
|
-
@values.first.should
|
81
|
+
@values.first.should satisfy { |val| val == 1 or val == 2 }
|
80
82
|
end
|
81
83
|
|
82
84
|
end
|
@@ -94,7 +96,7 @@ shared 'supporting String' do
|
|
94
96
|
|
95
97
|
it 'should write a multibyte String' do
|
96
98
|
@command = @connection.create_command('INSERT INTO users (name) VALUES(?)')
|
97
|
-
|
99
|
+
expect { @command.execute_non_query(name) }.not_to raise_error(DataObjects::DataError)
|
98
100
|
end
|
99
101
|
|
100
102
|
it 'should read back the multibyte String' do
|
@@ -107,7 +109,7 @@ shared 'supporting String' do
|
|
107
109
|
|
108
110
|
it 'should write a multibyte String (without query parameters)' do
|
109
111
|
@command = @connection.create_command("INSERT INTO users (name) VALUES(#{@n}\'#{name}\')")
|
110
|
-
|
112
|
+
expect { @command.execute_non_query }.not_to raise_error(DataObjects::DataError)
|
111
113
|
end
|
112
114
|
|
113
115
|
it 'should read back the multibyte String (without query parameters)' do
|
@@ -137,7 +139,7 @@ shared 'supporting String' do
|
|
137
139
|
|
138
140
|
it 'should return the correct entry' do
|
139
141
|
# Some of the drivers starts autoincrementation from 0 not 1
|
140
|
-
@values.first.should
|
142
|
+
@values.first.should satisfy { |val| val == 1 or val == 2 }
|
141
143
|
end
|
142
144
|
|
143
145
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
shared_examples_for 'supporting Time' do
|
2
2
|
|
3
|
-
|
3
|
+
before :all do
|
4
|
+
setup_test_environment
|
5
|
+
end
|
4
6
|
|
5
7
|
before do
|
6
8
|
@connection = DataObjects::Connection.new(CONFIG.uri)
|
@@ -27,7 +29,7 @@ shared 'supporting Time' do
|
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'should return the correctly typed result' do
|
30
|
-
@values.first.should
|
32
|
+
@values.first.should be_kind_of(Time)
|
31
33
|
end
|
32
34
|
|
33
35
|
it 'should return the correct result' do
|
@@ -51,11 +53,11 @@ shared 'supporting Time' do
|
|
51
53
|
end
|
52
54
|
|
53
55
|
it 'should return a nil class' do
|
54
|
-
@values.first.should
|
56
|
+
@values.first.should be_kind_of(NilClass)
|
55
57
|
end
|
56
58
|
|
57
59
|
it 'should return nil' do
|
58
|
-
@values.first.should
|
60
|
+
@values.first.should be_nil
|
59
61
|
end
|
60
62
|
|
61
63
|
end
|
@@ -76,9 +78,45 @@ shared 'supporting Time' do
|
|
76
78
|
|
77
79
|
it 'should return the correct entry' do
|
78
80
|
#Some of the drivers starts autoincrementation from 0 not 1
|
79
|
-
@values.first.should
|
81
|
+
@values.first.should satisfy { |val| val == 1 or val == 0 }
|
80
82
|
end
|
81
83
|
|
82
84
|
end
|
83
85
|
|
84
86
|
end
|
87
|
+
|
88
|
+
shared_examples_for 'supporting sub second Time' do
|
89
|
+
|
90
|
+
before :all do
|
91
|
+
setup_test_environment
|
92
|
+
end
|
93
|
+
|
94
|
+
before do
|
95
|
+
@connection = DataObjects::Connection.new(CONFIG.uri)
|
96
|
+
@connection.create_command(<<-EOF).execute_non_query(Time.parse('2010-12-15 14:32:08.49377-08'))
|
97
|
+
update widgets set release_timestamp = ? where id = 1
|
98
|
+
EOF
|
99
|
+
@connection.create_command(<<-EOF).execute_non_query(Time.parse('2010-12-15 14:32:28.942694-08'))
|
100
|
+
update widgets set release_timestamp = ? where id = 2
|
101
|
+
EOF
|
102
|
+
|
103
|
+
@command = @connection.create_command("SELECT release_timestamp FROM widgets WHERE id < ? order by id")
|
104
|
+
@command.set_types(Time)
|
105
|
+
@reader = @command.execute_reader(3)
|
106
|
+
@reader.next!
|
107
|
+
@values = @reader.values
|
108
|
+
end
|
109
|
+
|
110
|
+
after do
|
111
|
+
@connection.close
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should handle variable subsecond lengths properly' do
|
115
|
+
@values.first.should == Time.at(1292452328, 493770)
|
116
|
+
|
117
|
+
@reader.next!
|
118
|
+
@values = @reader.values
|
119
|
+
@values.first.should == Time.at(1292452348, 942694)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -72,6 +72,15 @@ module DataObjects
|
|
72
72
|
DataObjects::SavePoint.new(uri, connection)
|
73
73
|
end
|
74
74
|
|
75
|
+
# SavePoints can only occur in the context of a Transaction, thus they
|
76
|
+
# re-use TXN's connection (which was acquired from the connection pool
|
77
|
+
# legitimately via DO::Connection.new). We no-op #close in SP because
|
78
|
+
# calling DO::Connection#close will release the connection back into the
|
79
|
+
# pool (before the top-level Transaction might be done with it).
|
80
|
+
def close
|
81
|
+
# no-op
|
82
|
+
end
|
83
|
+
|
75
84
|
def begin
|
76
85
|
run %{SAVEPOINT "#{@id}"}
|
77
86
|
end
|
data/lib/data_objects/uri.rb
CHANGED
@@ -13,18 +13,75 @@ module DataObjects
|
|
13
13
|
# path:: The name or path to the database
|
14
14
|
# query:: Parameters for the connection, for example encoding=utf8
|
15
15
|
# fragment:: Not currently known to be in use, but available to the adapters
|
16
|
-
class URI
|
16
|
+
class URI
|
17
|
+
attr_reader :scheme, :subscheme, :user, :password, :host, :port, :path, :query, :fragment
|
18
|
+
|
17
19
|
# Make a DataObjects::URI object by parsing a string. Simply delegates to Addressable::URI::parse.
|
18
20
|
def self.parse(uri)
|
19
21
|
return uri if uri.kind_of?(self)
|
20
|
-
|
21
|
-
|
22
|
+
|
23
|
+
if uri.kind_of?(Addressable::URI)
|
24
|
+
scheme = uri.scheme
|
25
|
+
else
|
26
|
+
if uri[0,4] == 'jdbc'
|
27
|
+
scheme = uri[0,4]
|
28
|
+
uri = Addressable::URI::parse(uri[5, uri.length])
|
29
|
+
subscheme = uri.scheme
|
30
|
+
else
|
31
|
+
uri = Addressable::URI::parse(uri)
|
32
|
+
scheme = uri.scheme
|
33
|
+
subscheme = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
self.new(
|
38
|
+
:scheme => scheme,
|
39
|
+
:subscheme => subscheme,
|
40
|
+
:user => uri.user,
|
41
|
+
:password => uri.password,
|
42
|
+
:host => uri.host,
|
43
|
+
:port => uri.port,
|
44
|
+
:path => uri.path,
|
45
|
+
:query => uri.query_values,
|
46
|
+
:fragment => uri.fragment,
|
47
|
+
:relative => !!uri.to_s.index('//') # basic (naive) check for relativity / opaqueness
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(*args)
|
52
|
+
if (component = args.first).kind_of?(Hash)
|
53
|
+
@scheme = component[:scheme]
|
54
|
+
@subscheme = component[:subscheme]
|
55
|
+
@user = component[:user]
|
56
|
+
@password = component[:password]
|
57
|
+
@host = component[:host]
|
58
|
+
@port = component[:port]
|
59
|
+
@path = component[:path]
|
60
|
+
@query = component[:query]
|
61
|
+
@fragment = component[:fragment]
|
62
|
+
@relative = component[:relative]
|
63
|
+
elsif args.size > 1
|
64
|
+
warn "DataObjects::URI.new with arguments is deprecated, use a Hash of URI components (#{caller.first})"
|
65
|
+
@scheme, @user, @password, @host, @port, @path, @query, @fragment = *args
|
66
|
+
else
|
67
|
+
raise ArgumentError, "argument should be a Hash of URI components, was: #{args.inspect}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def opaque?
|
72
|
+
!@relative
|
73
|
+
end
|
74
|
+
|
75
|
+
def relative?
|
76
|
+
@relative
|
22
77
|
end
|
23
78
|
|
24
79
|
# Display this URI object as a string
|
25
80
|
def to_s
|
26
81
|
string = ""
|
27
|
-
string << "#{scheme}
|
82
|
+
string << "#{scheme}:" if scheme
|
83
|
+
string << "#{subscheme}:" if subscheme
|
84
|
+
string << '//' if relative?
|
28
85
|
if user
|
29
86
|
string << "#{user}"
|
30
87
|
string << ":#{password}" if password
|
@@ -51,5 +108,6 @@ module DataObjects
|
|
51
108
|
def hash
|
52
109
|
to_s.hash
|
53
110
|
end
|
111
|
+
|
54
112
|
end
|
55
113
|
end
|
data/lib/data_objects/version.rb
CHANGED
data/spec/command_spec.rb
CHANGED
@@ -12,13 +12,13 @@ describe DataObjects::Command do
|
|
12
12
|
|
13
13
|
%w{connection execute_non_query execute_reader set_types}.each do |meth|
|
14
14
|
it "should respond to ##{meth}" do
|
15
|
-
@command.should
|
15
|
+
@command.should respond_to(meth.intern)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
%w{execute_non_query execute_reader set_types}.each do |meth|
|
20
20
|
it "should raise NotImplementedError on ##{meth}" do
|
21
|
-
|
21
|
+
lambda { @command.send(meth.intern, nil) }.should raise_error(NotImplementedError)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
data/spec/connection_spec.rb
CHANGED
@@ -1,41 +1,61 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
describe DataObjects::Connection do
|
4
|
-
|
5
|
-
@connection = DataObjects::Connection.new('mock://localhost')
|
6
|
-
end
|
5
|
+
subject { connection }
|
7
6
|
|
8
|
-
|
9
|
-
@connection.release
|
10
|
-
end
|
7
|
+
let(:connection) { described_class.new(uri) }
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
after { connection.close }
|
10
|
+
|
11
|
+
context 'should define a standard API' do
|
12
|
+
let(:uri) { 'mock://localhost' }
|
13
|
+
|
14
|
+
it { should respond_to(:dispose) }
|
15
|
+
it { should respond_to(:create_command) }
|
17
16
|
|
18
|
-
|
19
|
-
@connection.to_s.should == 'mock://localhost'
|
17
|
+
its(:to_s) { should == 'mock://localhost' }
|
20
18
|
end
|
21
19
|
|
22
|
-
describe
|
20
|
+
describe 'initialization' do
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
context 'with a connection uri as a Addressable::URI' do
|
23
|
+
let(:uri) { Addressable::URI::parse('mock://localhost/database') }
|
24
|
+
|
25
|
+
it { should be_kind_of(DataObjects::Mock::Connection) }
|
26
|
+
it { should be_kind_of(DataObjects::Pooling) }
|
27
|
+
|
28
|
+
its(:to_s) { should == 'mock://localhost/database' }
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
[
|
32
|
+
'java:comp/env/jdbc/DataSource?driver=mock2',
|
33
|
+
Addressable::URI.parse('java:comp/env/jdbc/DataSource?driver=mock2')
|
34
|
+
].each do |jndi_url|
|
35
|
+
context 'should return the Connection specified by the scheme without pooling' do
|
36
|
+
let(:uri) { jndi_url }
|
37
|
+
|
38
|
+
it { should be_kind_of(DataObjects::Mock2::Connection) }
|
39
|
+
it { should_not be_kind_of(DataObjects::Pooling) }
|
34
40
|
end
|
41
|
+
end
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
%w(
|
44
|
+
jdbc:mock:memory::
|
45
|
+
jdbc:mock://host/database
|
46
|
+
jdbc:mock://host:6969/database
|
47
|
+
jdbc:mock:thin:host:database
|
48
|
+
jdbc:mock:thin:@host.domain.com:6969:database
|
49
|
+
jdbc:mock://server:6969/database;property=value;
|
50
|
+
jdbc:mock://[1111:2222:3333:4444:5555:6666:7777:8888]/database
|
51
|
+
).each do |jdbc_url|
|
52
|
+
context "with JDBC URL '#{jdbc_url}'" do
|
53
|
+
let(:uri) { jdbc_url }
|
54
|
+
|
55
|
+
it { should be_kind_of(DataObjects::Mock::Connection) }
|
56
|
+
end
|
39
57
|
end
|
58
|
+
|
40
59
|
end
|
60
|
+
|
41
61
|
end
|
data/spec/pooling_spec.rb
CHANGED
@@ -64,7 +64,7 @@ describe "DataObjects::Pooling" do
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
after do
|
67
|
+
after :each do
|
68
68
|
DataObjects::Pooling.lock.synchronize do
|
69
69
|
DataObjects::Pooling.pools.each do |pool|
|
70
70
|
pool.lock.synchronize do
|
@@ -95,24 +95,24 @@ describe "DataObjects::Pooling" do
|
|
95
95
|
it "should track the initialized pools" do
|
96
96
|
bob = Person.new('Bob') # Ensure the pool is "primed"
|
97
97
|
bob.name.should == 'Bob'
|
98
|
-
bob.instance_variable_get(:@__pool).
|
98
|
+
bob.instance_variable_get(:@__pool).should_not be_nil
|
99
99
|
Person.__pools.size.should == 1
|
100
100
|
bob.release
|
101
101
|
Person.__pools.size.should == 1
|
102
102
|
|
103
|
-
DataObjects::Pooling::pools.
|
103
|
+
DataObjects::Pooling::pools.should_not be_empty
|
104
104
|
|
105
105
|
sleep(1.2)
|
106
106
|
|
107
107
|
# NOTE: This assertion is commented out, as our MockConnection objects are
|
108
108
|
# currently in the pool.
|
109
|
-
DataObjects::Pooling::pools.should
|
110
|
-
bob.name.should
|
109
|
+
# DataObjects::Pooling::pools.should be_empty
|
110
|
+
bob.name.should be_nil
|
111
111
|
end
|
112
112
|
|
113
113
|
it "should allow you to overwrite Class#new" do
|
114
114
|
bob = Overwriter.new('Bob')
|
115
|
-
bob.should
|
115
|
+
bob.should be_overwritten
|
116
116
|
bob.release
|
117
117
|
end
|
118
118
|
|
@@ -127,7 +127,7 @@ describe "DataObjects::Pooling" do
|
|
127
127
|
bob = Person.new('Bob')
|
128
128
|
t1.join
|
129
129
|
bob.release
|
130
|
-
end.
|
130
|
+
end.should_not raise_error(DataObjects::Pooling::InvalidResourceError)
|
131
131
|
end
|
132
132
|
|
133
133
|
it "should allow you to flush a pool" do
|
@@ -141,7 +141,7 @@ describe "DataObjects::Pooling" do
|
|
141
141
|
Overwriter.__pools[['Bob']].flush!
|
142
142
|
Overwriter.__pools[['Bob']].size.should == 0
|
143
143
|
|
144
|
-
bob.name.should
|
144
|
+
bob.name.should be_nil
|
145
145
|
end
|
146
146
|
|
147
147
|
it "should wake up the scavenger thread when exiting" do
|
@@ -149,7 +149,7 @@ describe "DataObjects::Pooling" do
|
|
149
149
|
bob.release
|
150
150
|
DataObjects.exiting = true
|
151
151
|
sleep(0.1)
|
152
|
-
DataObjects::Pooling.scavenger?.should
|
152
|
+
DataObjects::Pooling.scavenger?.should be_false
|
153
153
|
end
|
154
154
|
|
155
155
|
it "should be able to detach an instance from the pool" do
|