rdo 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -0
- data/README.md +55 -48
- data/lib/rdo/result.rb +9 -1
- data/lib/rdo/version.rb +1 -1
- data/spec/rdo/result_spec.rb +18 -0
- data/util/macros.h +23 -14
- metadata +2 -2
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
# RDO—
|
1
|
+
# RDO—Database Connectivity for Ruby
|
2
2
|
|
3
3
|
RDO provides a simple, robust standardized way to access various RDBMS
|
4
4
|
implementations in Ruby. Drivers all conform to the same, beautiful rubyesque
|
5
|
-
interface. Where a feature is not natively supported by the DBMS—
|
5
|
+
interface. Where a feature is not natively supported by the DBMS—perhaps
|
6
6
|
prepared statements—it is seamlessly emulated, so you don't need to code
|
7
7
|
around it.
|
8
8
|
|
9
|
-
It targets **Ruby 1.9** and newer.
|
9
|
+
It targets **Ruby 1.9** and newer (including Rubinius 2.0).
|
10
10
|
|
11
11
|
[![Build Status](https://secure.travis-ci.org/d11wtq/rdo.png?branch=master)](http://travis-ci.org/d11wtq/rdo)
|
12
12
|
|
13
|
+
**RDO** stands for Ruby Data Objects.
|
14
|
+
|
13
15
|
``` ruby
|
14
16
|
require "rdo"
|
15
17
|
require "rdo-postgres"
|
@@ -36,27 +38,7 @@ end
|
|
36
38
|
conn.close
|
37
39
|
```
|
38
40
|
|
39
|
-
##
|
40
|
-
|
41
|
-
It's not an ORM. RDO provides access to a number of RDBMS's. It allows you to
|
42
|
-
query using pure SQL/DDL commands, as thinly as is necessary. It is absolutely
|
43
|
-
not, nor is it trying to be an SQL abstraction layer, an ORM or anything of
|
44
|
-
that nature. The intention is to provide a way to allow Ruby developers to
|
45
|
-
write applications that use a database, but don't use an ORM (*scoff!*).
|
46
|
-
|
47
|
-
Or perhaps you're actually writing the next kick-ass ORM? Either way, RDO
|
48
|
-
just lets you talk directly to your database.
|
49
|
-
|
50
|
-
## What's the point?
|
51
|
-
|
52
|
-
Let's face it, we've been writing database applications since the dark ages—
|
53
|
-
it's not that hard. What's lacking from Ruby, however, is any consistency for
|
54
|
-
dealing with a database directly. Several beautiful ORMs exist, but they
|
55
|
-
serve a different need. [DataMapper](https://github.com/datamapper/dm-core)
|
56
|
-
has a layer underneath it called [data_objects](https://github.com/datamapper/do),
|
57
|
-
but it isn't particularly user-friendly when used standalone and it requires
|
58
|
-
jumping through hoops to deal with certain database RDBMS features, such as
|
59
|
-
PostgreSQL bytea fields (which, in RDO, "just work").
|
41
|
+
## Features
|
60
42
|
|
61
43
|
RDO makes the following things standard:
|
62
44
|
|
@@ -65,10 +47,11 @@ RDO makes the following things standard:
|
|
65
47
|
- **Prepared statements** where possible; emulated where not
|
66
48
|
- **Type-casting** to equivalent Ruby types (e.g. Fixnum, BigDecimal,
|
67
49
|
Float, even Array)
|
68
|
-
- Access
|
50
|
+
- Access result information after write operations, with insert IDs standardized
|
69
51
|
- **Use simple core data types** (Hash) for reading values and field names
|
70
52
|
|
71
53
|
Note that data-type support is limited to whatever the DBMS actually supports.
|
54
|
+
See individual driver READMEs for type support information.
|
72
55
|
|
73
56
|
## Installation
|
74
57
|
|
@@ -133,6 +116,9 @@ And then execute:
|
|
133
116
|
</tbody>
|
134
117
|
</table>
|
135
118
|
|
119
|
+
I'm looking for contributors to develop and maintain drivers for other vendors:
|
120
|
+
Oracle, SQL Server and DB2 are of interest. Your project would be linked above.
|
121
|
+
|
136
122
|
## Usage
|
137
123
|
|
138
124
|
The interface for RDO is intentionally minimal. It should take a few minutes
|
@@ -179,9 +165,9 @@ p conn.open? #=> true
|
|
179
165
|
|
180
166
|
### Performing non-read commands
|
181
167
|
|
182
|
-
|
183
|
-
|
184
|
-
|
168
|
+
Any command supported by the DBMS is executed with #execute, which always
|
169
|
+
returns a RDO::Result object. Query inputs should be provided as binding
|
170
|
+
placeholders and additional arguments. No explicit type-conversion is
|
185
171
|
necessary.
|
186
172
|
|
187
173
|
``` ruby
|
@@ -211,8 +197,8 @@ include any error messaage provided by the DBMS.
|
|
211
197
|
### Performing read queries
|
212
198
|
|
213
199
|
There is no difference in the interface for reads or writes. Just call
|
214
|
-
the #execute method in both cases. This always returns
|
215
|
-
|
200
|
+
the #execute method in both cases. This always returns an RDO::Result,
|
201
|
+
which includes the Enumerable module. Some operations, such as #count
|
216
202
|
may be optimized by the driver.
|
217
203
|
|
218
204
|
``` ruby
|
@@ -270,16 +256,14 @@ conn.execute("INSERT INTO users (name) VALUES ('#{conn.quote(params[:name])}')")
|
|
270
256
|
RDO uses Symbols as keys in the hashes that represent data rows. Most of the
|
271
257
|
time this is desirable. If you query for something that returns field names
|
272
258
|
containing spaces, or punctuation, you need to convert a String to a Symbol
|
273
|
-
using #to_sym or #intern. Or
|
259
|
+
using #to_sym or #intern. Or use a quoted Symbol-literal.
|
274
260
|
|
275
261
|
``` ruby
|
276
262
|
result = conn(%q{SELECT 42 AS "The Meaning"})
|
263
|
+
p result.first[:"The Meaning"]
|
277
264
|
p result.first["The Meaning".intern]
|
278
265
|
```
|
279
266
|
|
280
|
-
I weighed up the possibility of using a custom data type, but I prefer core
|
281
|
-
ruby types unless there's an overwhelming reason to use a custom type, sorry.
|
282
|
-
|
283
267
|
### Selecting just a single value
|
284
268
|
|
285
269
|
RDO::Result has a #first_value method for convenience if you are only
|
@@ -303,6 +287,13 @@ DEBUG severity. Errors will be logged with FATAL severity.
|
|
303
287
|
RDO.connect("postgres://user:pass@host/db", logger: Logger.new(STDOUT))
|
304
288
|
```
|
305
289
|
|
290
|
+
You can access the logger through `connection.logger`.
|
291
|
+
|
292
|
+
``` ruby
|
293
|
+
conn.logger.level = Logger::DEBUG
|
294
|
+
conn.logger.debug? #=> true
|
295
|
+
```
|
296
|
+
|
306
297
|
A logger with some support for highlighting errors etc and which shows
|
307
298
|
query execution times is configured (but disabled) by default. It is
|
308
299
|
found at `RDO::ColoredLogger`. You can enable it by specify a log level:
|
@@ -320,7 +311,7 @@ Turning on debug logging globally is often a little overkill and too noisy.
|
|
320
311
|
You may enable debug logging in the context of a block, like so:
|
321
312
|
|
322
313
|
``` ruby
|
323
|
-
|
314
|
+
conn.debug do
|
324
315
|
# call some methods that execute SQL
|
325
316
|
end
|
326
317
|
```
|
@@ -335,8 +326,7 @@ Your contribution will be recognized here. If you don't know how to fix it,
|
|
335
326
|
file an issue in the issue tracker on GitHub.
|
336
327
|
|
337
328
|
When sending pull requests, please use topic branches—don't send a pull
|
338
|
-
request from the master branch of your fork
|
339
|
-
unintentionally.
|
329
|
+
request from the master branch of your fork.
|
340
330
|
|
341
331
|
I haven't looked at what I need to change to have the drivers compile on
|
342
332
|
Windows yet, but I will do. If anybody beats me to it, pull requests will
|
@@ -349,22 +339,39 @@ parts of the Ruby API I use are very typical.
|
|
349
339
|
|
350
340
|
The more drivers that RDO has support for, the better. Writing drivers for
|
351
341
|
RDO is quite painless. They are just thin wrappers around the C API for the
|
352
|
-
DBMS, which conform to RDO's interface.
|
342
|
+
DBMS, which conform to RDO's Driver interface.
|
343
|
+
|
344
|
+
```
|
345
|
+
RDO::Driver
|
346
|
+
- open
|
347
|
+
- open?
|
348
|
+
- close
|
349
|
+
- execute
|
350
|
+
- prepare
|
351
|
+
- quote
|
352
|
+
```
|
353
|
+
|
354
|
+
The #execute method returns an RDO::Result, which takes any Enumerable and
|
355
|
+
some options in its initializer. The Enumerable just iterates over the rows
|
356
|
+
in the result. The options Hash provides result information.
|
357
|
+
|
358
|
+
The #prepare method is optional, but should return an Object with the
|
359
|
+
following methods:
|
360
|
+
|
361
|
+
```
|
362
|
+
RDO::StatementExecutor
|
363
|
+
- command
|
364
|
+
- execute
|
365
|
+
```
|
366
|
+
|
367
|
+
The #command method just provides the String form of the statement. The #execute
|
368
|
+
method returns an RDO::Result, as per the Driver.
|
353
369
|
|
354
370
|
Some of the more boilerplate things you'd normally have to do are covered by
|
355
371
|
C macros in the util/macros.h file you'll find in this repository. Copy that
|
356
372
|
file to your own project and include it for one-line type conversions etc.
|
357
373
|
Take a look at one of the existing drivers to get an idea how to write a
|
358
|
-
driver.
|
359
|
-
|
360
|
-
Because one person could not possibly maintain drivers for all conceivable
|
361
|
-
DBMS's, it is better that different developers write and maintain different
|
362
|
-
drivers. If you have written a driver for RDO, please fork this git repo and
|
363
|
-
edit this README to list it, then send a pull request. That way others will
|
364
|
-
find it more easily.
|
365
|
-
|
366
|
-
I'm particularly interested in drivers for Oracle and Microsoft SQL Server,
|
367
|
-
though I don't personally use these.
|
374
|
+
driver (rdo-sqlite and rdo-mysql are probably simple ones).
|
368
375
|
|
369
376
|
## Copyright & Licensing
|
370
377
|
|
data/lib/rdo/result.rb
CHANGED
@@ -81,10 +81,18 @@ module RDO
|
|
81
81
|
if info[:count].nil? || block_given?
|
82
82
|
super
|
83
83
|
else
|
84
|
-
info[:count]
|
84
|
+
info[:count].to_i
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
# Check if the result has no rows.
|
89
|
+
#
|
90
|
+
# @return [Boolean]
|
91
|
+
# true if the result has no returned rows
|
92
|
+
def empty?
|
93
|
+
count.zero?
|
94
|
+
end
|
95
|
+
|
88
96
|
# Get the time spent processing the statement.
|
89
97
|
#
|
90
98
|
# @return [Float]
|
data/lib/rdo/version.rb
CHANGED
data/spec/rdo/result_spec.rb
CHANGED
@@ -97,6 +97,24 @@ describe RDO::Result do
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
+
describe "#empty?" do
|
101
|
+
context "when #count == 0" do
|
102
|
+
let(:result) { RDO::Result.new([], count: 0) }
|
103
|
+
|
104
|
+
it "returns true" do
|
105
|
+
result.should be_empty
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when #count > 0" do
|
110
|
+
let(:result) { RDO::Result.new([], count: 1) }
|
111
|
+
|
112
|
+
it "returns false" do
|
113
|
+
result.should_not be_empty
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
100
118
|
describe "#first_value" do
|
101
119
|
context "when there are tuples" do
|
102
120
|
let(:result) { RDO::Result.new([{id: 6, name: "bob"}]) }
|
data/util/macros.h
CHANGED
@@ -37,6 +37,25 @@
|
|
37
37
|
*/
|
38
38
|
#define RDO_ERROR(...) (rb_raise(rb_path2class("RDO::Exception"), __VA_ARGS__))
|
39
39
|
|
40
|
+
/**
|
41
|
+
* Calls Driver#interpolate for args.
|
42
|
+
*
|
43
|
+
* @param VALUE (RDO::Driver)
|
44
|
+
* the Driver to use #quote from
|
45
|
+
*
|
46
|
+
* @param (VALUE *) args
|
47
|
+
* a C array where the first element is the SQL and the remainder are the bind params
|
48
|
+
*
|
49
|
+
* @param int argc
|
50
|
+
* the number of elements in args
|
51
|
+
*
|
52
|
+
* @return VALUE (String)
|
53
|
+
* a Ruby String with the SQL including the interpolated params
|
54
|
+
*/
|
55
|
+
#define RDO_INTERPOLATE(driver, args, argc) \
|
56
|
+
(rb_funcall(driver, rb_intern("interpolate"), 2, \
|
57
|
+
args[0], rb_ary_new4(argc - 1, &args[1])))
|
58
|
+
|
40
59
|
/**
|
41
60
|
* Factory to return a new RDO::Result for an Enumerable object of tuples.
|
42
61
|
*
|
@@ -52,18 +71,6 @@
|
|
52
71
|
#define RDO_RESULT(tuples, info) \
|
53
72
|
(rb_funcall(rb_path2class("RDO::Result"), rb_intern("new"), 2, tuples, info))
|
54
73
|
|
55
|
-
/**
|
56
|
-
* Wrap the given StatementExecutor in a RDO::Statement.
|
57
|
-
*
|
58
|
-
* @param VALUE
|
59
|
-
* any object that responds to #command and #execute
|
60
|
-
*
|
61
|
-
* @return VALUE
|
62
|
-
* an RDO::Statement
|
63
|
-
*/
|
64
|
-
#define RDO_STATEMENT(executor) \
|
65
|
-
(rb_funcall(rb_path2class("RDO::Statement"), rb_intern("new"), 1, executor))
|
66
|
-
|
67
74
|
/**
|
68
75
|
* Convert a C string to a ruby String.
|
69
76
|
*
|
@@ -77,7 +84,8 @@
|
|
77
84
|
* a Ruby String
|
78
85
|
*/
|
79
86
|
#define RDO_STRING(s, len, enc) \
|
80
|
-
(rb_enc_associate_index(rb_str_new(s, len),
|
87
|
+
(rb_enc_associate_index(rb_str_new(s, len), \
|
88
|
+
enc > 0 ? enc : rb_enc_find_index("binary")))
|
81
89
|
|
82
90
|
/**
|
83
91
|
* Convert a C string to a ruby String, assuming possible NULL bytes.
|
@@ -91,7 +99,8 @@
|
|
91
99
|
* @return VALUE (String)
|
92
100
|
* a Ruby String
|
93
101
|
*/
|
94
|
-
#define RDO_BINARY_STRING(s, len)
|
102
|
+
#define RDO_BINARY_STRING(s, len) \
|
103
|
+
(RDO_STRING(s, len, rb_enc_find_index("binary")))
|
95
104
|
|
96
105
|
/**
|
97
106
|
* Convert a C string to a Fixnum.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|