rdo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +309 -0
- data/Rakefile +2 -0
- data/lib/rdo.rb +32 -0
- data/lib/rdo/connection.rb +113 -0
- data/lib/rdo/driver.rb +120 -0
- data/lib/rdo/emulated_statement_executor.rb +36 -0
- data/lib/rdo/exception.rb +11 -0
- data/lib/rdo/result.rb +95 -0
- data/lib/rdo/statement.rb +28 -0
- data/lib/rdo/util.rb +102 -0
- data/lib/rdo/version.rb +3 -0
- data/rdo.gemspec +52 -0
- data/spec/rdo/connection_spec.rb +220 -0
- data/spec/rdo/driver_spec.rb +29 -0
- data/spec/rdo/emulated_statements_spec.rb +22 -0
- data/spec/rdo/result_spec.rb +117 -0
- data/spec/rdo/statement_spec.rb +24 -0
- data/spec/rdo/util_spec.rb +92 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/driver_with_everything.rb +38 -0
- data/spec/support/driver_without_statements.rb +23 -0
- data/util/macros.h +177 -0
- metadata +110 -0
@@ -0,0 +1,220 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Connection do
|
4
|
+
after(:each) { RDO::Connection.drivers.clear }
|
5
|
+
|
6
|
+
describe ".register_driver" do
|
7
|
+
before(:each) do
|
8
|
+
RDO::Connection.register_driver(:bob, RDO::Driver)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "registers a driver name for a driver class" do
|
12
|
+
RDO::Connection.drivers["bob"].should == RDO::Driver
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#initialize" do
|
17
|
+
let(:driver) { double(:driver, open: true) }
|
18
|
+
let(:driver_class) { double(new: driver) }
|
19
|
+
|
20
|
+
before(:each) { RDO::Connection.register_driver(:test, driver_class) }
|
21
|
+
|
22
|
+
context "with a connection uri string" do
|
23
|
+
context "for a registered driver" do
|
24
|
+
it "instantiates the driver" do
|
25
|
+
driver_class.should_receive(:new).and_return(driver)
|
26
|
+
RDO::Connection.new("test://whatever")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "converts the uri to an options hash" do
|
30
|
+
driver_class.should_receive(:new).
|
31
|
+
with(hash_including(driver: "test")).
|
32
|
+
and_return(driver)
|
33
|
+
|
34
|
+
RDO::Connection.new("test://whatever")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "parses the host name" do
|
38
|
+
driver_class.should_receive(:new).
|
39
|
+
with(hash_including(host: "whatever")).
|
40
|
+
and_return(driver)
|
41
|
+
|
42
|
+
RDO::Connection.new("test://whatever:3456")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "parses the username" do
|
46
|
+
driver_class.should_receive(:new).
|
47
|
+
with(hash_including(user: "bob")).
|
48
|
+
and_return(driver)
|
49
|
+
|
50
|
+
RDO::Connection.new("test://bob:@whatever:3456")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "parses the password" do
|
54
|
+
driver_class.should_receive(:new).
|
55
|
+
with(hash_including(password: "secret")).
|
56
|
+
and_return(driver)
|
57
|
+
|
58
|
+
RDO::Connection.new("test://user:secret@whatever:3456")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "parses the port number" do
|
62
|
+
driver_class.should_receive(:new).
|
63
|
+
with(hash_including(port: 3456)).
|
64
|
+
and_return(driver)
|
65
|
+
|
66
|
+
RDO::Connection.new("test://whatever:3456")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "parses the path" do
|
70
|
+
driver_class.should_receive(:new).
|
71
|
+
with(hash_including(path: "/some/path.db")).
|
72
|
+
and_return(driver)
|
73
|
+
|
74
|
+
RDO::Connection.new("test://whatever/some/path.db")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "parses the database name" do
|
78
|
+
driver_class.should_receive(:new).
|
79
|
+
with(hash_including(database: "my_db")).
|
80
|
+
and_return(driver)
|
81
|
+
|
82
|
+
RDO::Connection.new("test://whatever/my_db")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "parses the encoding" do
|
86
|
+
driver_class.should_receive(:new).
|
87
|
+
with(hash_including(encoding: "utf-8")).
|
88
|
+
and_return(driver)
|
89
|
+
|
90
|
+
RDO::Connection.new("test://whatever/my_db?encoding=utf-8")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "parses driver-specific options" do
|
94
|
+
driver_class.should_receive(:new).
|
95
|
+
with(hash_including(special_mode: "true")).
|
96
|
+
and_return(driver)
|
97
|
+
|
98
|
+
RDO::Connection.new("test://whatever/my_db?encoding=utf-8&special_mode=true")
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with only a path" do
|
102
|
+
it "parses the path" do
|
103
|
+
driver_class.should_receive(:new).
|
104
|
+
with(hash_including(path: "/some/path.db")).
|
105
|
+
and_return(driver)
|
106
|
+
|
107
|
+
RDO::Connection.new("test:/some/path.db")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "invokes #open on the driver" do
|
112
|
+
driver.should_receive(:open).and_return(true)
|
113
|
+
RDO::Connection.new("test://host/db")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "for an unknown driver" do
|
118
|
+
it "raises an RDO::Exception" do
|
119
|
+
expect {
|
120
|
+
RDO::Connection.new("wat://ever")
|
121
|
+
}.to raise_error(RDO::Exception)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "with an options hash" do
|
127
|
+
context "for a registered driver" do
|
128
|
+
it "instantiates the driver" do
|
129
|
+
driver_class.should_receive(:new).and_return(driver)
|
130
|
+
RDO::Connection.new(driver: :test, host: "whatever")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "passes the options to the driver" do
|
134
|
+
driver_class.should_receive(:new).
|
135
|
+
with(hash_including(host: "whatever")).
|
136
|
+
and_return(driver)
|
137
|
+
RDO::Connection.new(driver: :test, host: "whatever")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "invokes #open on the driver" do
|
141
|
+
driver.should_receive(:open).and_return(true)
|
142
|
+
RDO::Connection.new(driver: :test, host: "whatever")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "for an unknown driver" do
|
147
|
+
it "raises an RDO::Exception" do
|
148
|
+
expect {
|
149
|
+
RDO::Connection.new(driver: :wat, host: "test")
|
150
|
+
}.to raise_error(RDO::Exception)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "driver methods" do
|
157
|
+
let(:connection) { RDO::Connection.new("test://host") }
|
158
|
+
let(:driver) { double(:driver, open: true) }
|
159
|
+
let(:driver_class) { double(new: driver) }
|
160
|
+
|
161
|
+
before(:each) do
|
162
|
+
RDO::Connection.register_driver(:test, driver_class)
|
163
|
+
connection
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#open" do
|
167
|
+
it "delegates to the driver" do
|
168
|
+
driver.should_receive(:open).and_return(true)
|
169
|
+
connection.open.should == true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#close" do
|
174
|
+
it "delegates to the driver" do
|
175
|
+
driver.should_receive(:close).and_return(true)
|
176
|
+
connection.close.should == true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "#open?" do
|
181
|
+
it "delegates to the driver" do
|
182
|
+
driver.should_receive(:open?).and_return(false)
|
183
|
+
connection.should_not be_open
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "#execute" do
|
188
|
+
let(:result) { RDO::Result.new([]) }
|
189
|
+
|
190
|
+
it "delegates to the driver" do
|
191
|
+
driver.should_receive(:execute).
|
192
|
+
with("SELECT * FROM bob WHERE ?", true).
|
193
|
+
and_return(result)
|
194
|
+
connection.execute("SELECT * FROM bob WHERE ?", true).should == result
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "#prepare" do
|
199
|
+
let(:stmt) { RDO::Statement.new(stub(:executor)) }
|
200
|
+
|
201
|
+
it "delegates to the driver" do
|
202
|
+
driver.should_receive(:prepare).
|
203
|
+
with("SELECT * FROM bob WHERE ?").
|
204
|
+
and_return(stmt)
|
205
|
+
connection.prepare("SELECT * FROM bob WHERE ?").should == stmt
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "#quote" do
|
210
|
+
let(:quoted) { "Weird ['] quotes" }
|
211
|
+
|
212
|
+
it "delegates to the driver" do
|
213
|
+
driver.should_receive(:quote).
|
214
|
+
with("Weird ' quotes").
|
215
|
+
and_return(quoted)
|
216
|
+
connection.quote("Weird ' quotes").should == quoted
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Driver do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "does not call #open" do
|
6
|
+
RDO::DriverWithEverything.new.should_not be_open
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#prepare" do
|
11
|
+
let(:driver) { RDO::DriverWithoutStatements.new }
|
12
|
+
|
13
|
+
it "returns a Statement" do
|
14
|
+
driver.prepare("SELECT * FROM bob WHERE ?").should be_a_kind_of(RDO::Statement)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has the correct command" do
|
18
|
+
driver.prepare("SELECT * FROM bob WHERE ?").command.should == "SELECT * FROM bob WHERE ?"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "calls #execute on the driver" do
|
22
|
+
driver.should_receive(:execute).
|
23
|
+
with("SELECT * FROM bob WHERE ?", true).
|
24
|
+
and_return(RDO::Result.new([]))
|
25
|
+
stmt = driver.prepare("SELECT * FROM bob WHERE ?")
|
26
|
+
stmt.execute(true).should be_a_kind_of(RDO::Result)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Driver, "emulated prepared statements" do
|
4
|
+
let(:driver) { RDO::DriverWithoutStatements.new }
|
5
|
+
|
6
|
+
describe "#command" do
|
7
|
+
it "returns the command string" do
|
8
|
+
driver.prepare("SELECT * FROM users").command.should == "SELECT * FROM users"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#execute" do
|
13
|
+
let(:result) { stub(:result) }
|
14
|
+
|
15
|
+
it "delegates to the driver" do
|
16
|
+
driver.should_receive(:execute).with(
|
17
|
+
"SELECT * FROM users WHERE ? AND ?", 1, 2
|
18
|
+
).and_return(result)
|
19
|
+
driver.prepare("SELECT * FROM users WHERE ? AND ?").execute(1, 2).should == result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Result do
|
4
|
+
it "is enumerable" do
|
5
|
+
RDO::Result.new([]).should be_a_kind_of(Enumerable)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#each" do
|
9
|
+
let(:result) { RDO::Result.new([{id: 7}, {id: 42}]) }
|
10
|
+
|
11
|
+
it "enumerates all tuples" do
|
12
|
+
tuples = []
|
13
|
+
result.each{|row| tuples << row}
|
14
|
+
tuples.should == [{id: 7}, {id: 42}]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns the result" do
|
18
|
+
result.each.should equal(result)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#info" do
|
23
|
+
let(:result) { RDO::Result.new([], foo: "bar", zip: 42) }
|
24
|
+
|
25
|
+
it "returns all info passed to #initialize" do
|
26
|
+
result.info.should == {foo: "bar", zip: 42}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#insert_id" do
|
31
|
+
context "when provided in the info" do
|
32
|
+
let(:result) { RDO::Result.new([], insert_id: 21) }
|
33
|
+
|
34
|
+
it "returns the ID from the info" do
|
35
|
+
result.insert_id.should == 21
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when not provided in the info" do
|
40
|
+
context "and there are tuples" do
|
41
|
+
let(:result) { RDO::Result.new([{id: 6, name: "bob"}]) }
|
42
|
+
|
43
|
+
it "infers from the tuples" do
|
44
|
+
result.insert_id.should == 6
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "and there are no tuples" do
|
49
|
+
let(:result) { RDO::Result.new([]) }
|
50
|
+
|
51
|
+
it "returns nil" do
|
52
|
+
result.insert_id.should be_nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#affected_rows" do
|
59
|
+
context "when provided in the info" do
|
60
|
+
let(:result) { RDO::Result.new([], affected_rows: 3) }
|
61
|
+
|
62
|
+
it "returns the value from the info" do
|
63
|
+
result.affected_rows.should == 3
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when not provided in the info" do
|
68
|
+
let(:result) { RDO::Result.new([]) }
|
69
|
+
|
70
|
+
it "returns nil" do
|
71
|
+
result.insert_id.should be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#count" do
|
77
|
+
context "when provided in the info" do
|
78
|
+
let(:result) { RDO::Result.new([{id: 7}, {id: 42}], count: 50) }
|
79
|
+
|
80
|
+
it "returns the count from the info" do
|
81
|
+
result.count.should == 50
|
82
|
+
end
|
83
|
+
|
84
|
+
context "with a block given" do
|
85
|
+
it "delegates to Enumerable" do
|
86
|
+
result.count{|row| row[:id] > 10}.should == 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when not provided in the info" do
|
92
|
+
let(:result) { RDO::Result.new([{id: 7}, {id: 42}]) }
|
93
|
+
|
94
|
+
it "delegates to Enumerable" do
|
95
|
+
result.count.should == 2
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#first_value" do
|
101
|
+
context "when there are tuples" do
|
102
|
+
let(:result) { RDO::Result.new([{id: 6, name: "bob"}]) }
|
103
|
+
|
104
|
+
it "reads the first column in the first tuple" do
|
105
|
+
result.first_value.should == 6
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when there are no tuples" do
|
110
|
+
let(:result) { RDO::Result.new([]) }
|
111
|
+
|
112
|
+
it "returns nil" do
|
113
|
+
result.first_value.should be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Statement do
|
4
|
+
let(:executor) { double(:executor) }
|
5
|
+
let(:stmt) { RDO::Statement.new(executor) }
|
6
|
+
|
7
|
+
describe "#command" do
|
8
|
+
let(:command) { "SELECT * FROM users" }
|
9
|
+
|
10
|
+
it "delegates to the executor" do
|
11
|
+
executor.should_receive(:command).and_return(command)
|
12
|
+
stmt.command.should == command
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#execute" do
|
17
|
+
let(:result) { stub(:result) }
|
18
|
+
|
19
|
+
it "delegates to the executor" do
|
20
|
+
executor.should_receive(:execute).with(1, 2).and_return(result)
|
21
|
+
stmt.execute(1, 2).should == result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RDO::Util do
|
4
|
+
describe ".system_time_zone" do
|
5
|
+
it "returns the time zone of the local system" do
|
6
|
+
require "date"
|
7
|
+
RDO::Util.system_time_zone.should == DateTime.now.zone
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".float" do
|
12
|
+
context "with Infinity" do
|
13
|
+
it "returns Float::INFINITY" do
|
14
|
+
RDO::Util.float("Infinity").should == Float::INFINITY
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with -Infinity" do
|
19
|
+
it "returns -Float::INFINITY" do
|
20
|
+
RDO::Util.float("-Infinity").should == -Float::INFINITY
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with NaN" do
|
25
|
+
it "returns Float::NAN" do
|
26
|
+
RDO::Util.float("NaN").should be_nan
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with a number" do
|
31
|
+
it "returns a Float" do
|
32
|
+
RDO::Util.float("1.2").should == 1.2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with an exponent" do
|
37
|
+
it "returns a Float" do
|
38
|
+
RDO::Util.float("1.1E-2").should == Float("1.1E-2")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "decimal" do
|
44
|
+
context "with NaN" do
|
45
|
+
it "returns NaN" do
|
46
|
+
RDO::Util.decimal("NaN").should be_nan
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with a number" do
|
51
|
+
it "returns a BigDecimal" do
|
52
|
+
require "bigdecimal"
|
53
|
+
RDO::Util.decimal("1.2").should == BigDecimal("1.2")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "with an exponent" do
|
58
|
+
it "returns a BigDecimal" do
|
59
|
+
require "bigdecimal"
|
60
|
+
RDO::Util.decimal("1E-2").should == BigDecimal("1E-2")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".date" do
|
66
|
+
it "returns a Date" do
|
67
|
+
RDO::Util.date("2012-09-22").should == Date.new(2012, 9, 22)
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with BC" do
|
71
|
+
it "returns a Date" do
|
72
|
+
RDO::Util.date("431-09-22 BC").should == Date.new(-430, 9, 22)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe ".date_time_with_zone" do
|
78
|
+
it "returns a DateTime" do
|
79
|
+
require "date"
|
80
|
+
RDO::Util.date_time_with_zone("2012-09-22 10:04:32 +06:00").should ==
|
81
|
+
DateTime.parse("2012-09-22 10:04:32 +06:00")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ".date_time_without_zone" do
|
86
|
+
it "returns a DateTime in the system time zone" do
|
87
|
+
require "date"
|
88
|
+
RDO::Util.date_time_without_zone("2012-09-22 10:04:32").should ==
|
89
|
+
DateTime.parse("2012-09-22 10:04:32 #{DateTime.now.zone}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|