data_objects 0.10.3 → 0.10.4.rc1
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/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
|