rdo 0.1.6 → 0.1.7
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/.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
|
[](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
|