rdo 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -37,7 +37,7 @@ conn.close
37
37
  ## Why your ORM so shit?
38
38
 
39
39
  RDO provides access to a number of RDBMS's. It allows you to query using SQL
40
- and issue commands using DDL, as thinly as is necessary. It is absolutely not,
40
+ and other things using DDL, as thinly as is necessary. It is absolutely not,
41
41
  nor is it trying to be an SQL abstraction layer, an ORM or anything of that
42
42
  nature. The intention is to provide a way to allow Ruby developers to write
43
43
  applications that use a database, but don't use an ORM (*scoff!*).
@@ -94,26 +94,30 @@ And then execute:
94
94
  <th>URI Schemes</th>
95
95
  <th>Gem</th>
96
96
  <th>Author</th>
97
+ <th>Status</th>
97
98
  </tr>
98
99
  </thead>
99
100
  <tbody>
100
101
  <tr>
101
102
  <th>SQLite</th>
102
- <td>sqlite</td>
103
+ <td>sqlite, sqlite3</td>
103
104
  <td><a href="https://github.com/d11wtq/rdo-sqlite">rdo-sqlite</a></td>
104
105
  <td><a href="https://github.com/d11wtq">d11wtq</a></td>
106
+ <td>Published</td>
105
107
  </tr>
106
108
  <tr>
107
109
  <th>PostgreSQL</th>
108
110
  <td>postgresql, postgres</td>
109
111
  <td><a href="https://github.com/d11wtq/rdo-postgres">rdo-postgres</a></td>
110
112
  <td><a href="https://github.com/d11wtq">d11wtq</a></td>
113
+ <td>Published</td>
111
114
  </tr>
112
115
  <tr>
113
116
  <th>MySQL</th>
114
117
  <td>mysql</td>
115
118
  <td><a href="https://github.com/d11wtq/rdo-mysql">rdo-mysql</a></td>
116
119
  <td><a href="https://github.com/d11wtq">d11wtq</a></td>
120
+ <td>Pending development</td>
117
121
  </tr>
118
122
  </tbody>
119
123
  </table>
@@ -137,6 +141,8 @@ conn = RDO.connect("postgresql://user:pass@host:port/db_name?encoding=utf-8")
137
141
  p conn.open? #=> true
138
142
  ```
139
143
 
144
+ For semantic reasons, #connect is aliased to #open.
145
+
140
146
  If it is not possible to establish a connection an RDO::Exception is raised,
141
147
  which should provide any reason given by the DBMS.
142
148
 
@@ -146,8 +152,7 @@ RDO will disconnect automatically when the connection is garbage-collected,
146
152
  or when the program exits, but if you need to disconnect explicitly,
147
153
  call #close. It is safe to call this even if the connection is already closed.
148
154
 
149
- Call #open to re-connect after closing a connection, for example when forking
150
- child processes.
155
+ If you have called #close, say before forking, call #open to re-connect.
151
156
 
152
157
  ``` ruby
153
158
  conn.close
@@ -157,6 +162,18 @@ conn.open
157
162
  p conn.open? #=> true
158
163
  ```
159
164
 
165
+ ### One-time use connections
166
+
167
+ If you pass a block to RDO.connect, RDO yields the connection into the block,
168
+ returns the result of the block, the closes the connection.
169
+
170
+ ``` ruby
171
+ puts RDO.open("sqlite:some.db") do |c|
172
+ c.execute("SELECT value FROM config WHERE name = ?", "api_key").first_value
173
+ end
174
+ # => "EXAMPLE_KEY"
175
+ ```
176
+
160
177
  ### Performing non-read commands
161
178
 
162
179
  All SQL and DDL (Data Definition Language) is executed with #execute, which
data/lib/rdo.rb CHANGED
@@ -17,16 +17,32 @@ require "rdo/util"
17
17
  module RDO
18
18
  class << self
19
19
  # Establish a connection to the RDBMS.
20
+ # The connection will be returned open.
20
21
  #
21
22
  # The needed driver must be loaded before calling this.
22
23
  #
24
+ # If a block is given, the connection is passed to the block and then
25
+ # closed at the end of the block, before this method finally returns
26
+ # the result of the block.
27
+ #
23
28
  # @param [Object] options
24
29
  # either a connection URI string, or an option Hash
25
30
  #
26
31
  # @return [Connection]
27
32
  # a Connection for the required driver
28
33
  def connect(options)
29
- Connection.new(options)
34
+ if block_given?
35
+ begin
36
+ c = Connection.new(options)
37
+ yield c
38
+ ensure
39
+ c.close unless c.nil?
40
+ end
41
+ else
42
+ Connection.new(options)
43
+ end
30
44
  end
45
+
46
+ alias_method :open, :connect
31
47
  end
32
48
  end
@@ -92,20 +92,38 @@ module RDO
92
92
  end
93
93
 
94
94
  def parse_connection_uri(str)
95
- uri = URI.parse(str.to_s)
95
+ uri = # handle e.g. sqlite: and sqlite:// (empty host and path)
96
+ if str =~ %r{\A[a-z0-9_\+-]+:\Z}i
97
+ URI.parse(str.to_s + "//rdo-spoof").tap{|u| u.host = nil}
98
+ elsif str =~ %r{\A[a-z0-9_\+-]+://\Z}i
99
+ URI.parse(str.to_s + "rdo-spoof").tap{|u| u.host = nil}
100
+ else
101
+ URI.parse(str.to_s)
102
+ end
103
+
96
104
  normalize_options(
97
105
  {
98
106
  driver: uri.scheme,
99
107
  host: uri.host,
100
108
  port: uri.port,
101
- path: uri.path,
102
- database: uri.path.to_s.sub("/", ""),
109
+ path: extract_uri_path(uri),
110
+ database: extract_uri_path(uri).to_s.sub("/", ""),
103
111
  user: uri.user,
104
112
  password: uri.password
105
- }.merge(parse_query_string(uri.query))
113
+ }.merge(parse_query_string(extract_uri_query(uri)))
106
114
  )
107
115
  end
108
116
 
117
+ def extract_uri_path(uri)
118
+ return uri.path unless uri.opaque
119
+ uri.opaque.sub(/\?.*\Z/, "")
120
+ end
121
+
122
+ def extract_uri_query(uri)
123
+ return uri.query unless uri.opaque
124
+ uri.opaque.sub(/\A.*?\?/, "")
125
+ end
126
+
109
127
  def parse_query_string(str)
110
128
  str.nil? ? {} : Hash[CGI.parse(str).map{|k,v| [k, v.size == 1 ? v.first : v]}]
111
129
  end
@@ -6,5 +6,5 @@
6
6
  ##
7
7
 
8
8
  module RDO
9
- VERSION = "0.0.6"
9
+ VERSION = "0.0.7"
10
10
  end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe RDO do
4
+ let(:driver_class) { stub(new: driver) }
5
+ let(:driver) { stub(:driver, open: true, close: true) }
6
+
7
+ before(:each) { RDO::Connection.register_driver(:test, driver_class) }
8
+
9
+ describe ".connect" do
10
+ it "returns a connection for the given driver" do
11
+ RDO.connect("test://whatever").should be_a_kind_of(RDO::Connection)
12
+ end
13
+
14
+ it "opens the connection" do
15
+ driver.should_receive(:open).and_return(true)
16
+ RDO.connect("test://whatever")
17
+ end
18
+
19
+ it "is aliased as .open" do
20
+ RDO.open("test://whatever").should be_a_kind_of(RDO::Connection)
21
+ end
22
+
23
+ context "when given a block" do
24
+ it "yields the connection" do
25
+ conn = nil
26
+ RDO.open("test://whatever") { |c| conn = c }
27
+ conn.should be_a_kind_of(RDO::Connection)
28
+ end
29
+
30
+ it "opens the connection" do
31
+ driver.should_receive(:open).and_return(true)
32
+ RDO.open("test://whatever") { |c| }
33
+ end
34
+
35
+ it "closes the connection" do
36
+ driver.should_receive(:close).and_return(true)
37
+ RDO.open("test://whatever") { |c| }
38
+ end
39
+
40
+ it "returns the result of the block" do
41
+ RDO.open("test://whatever"){ |c| "test" }.should == "test"
42
+ end
43
+
44
+ context "on error in block" do
45
+ it "still closes the connection" do
46
+ driver.should_receive(:close).and_return(true)
47
+ begin
48
+ RDO.open("test://whatever") { |c| raise "Blarg" }
49
+ rescue
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  /*
2
- * RDO Postgres Driver.
2
+ * RDO—Ruby Data Objects.
3
3
  * Copyright © 2012 Chris Corbyn.
4
4
  *
5
5
  * See LICENSE file for details.
@@ -18,6 +18,52 @@
18
18
  * --------------------------------------------------------------------------
19
19
  */
20
20
 
21
+ #include <ruby.h>
22
+ #include <ruby/encoding.h>
23
+
24
+ /**
25
+ * Convenience to call #to_s on any Ruby object.
26
+ */
27
+ #define RDO_OBJ_TO_S(obj) (rb_funcall(obj, rb_intern("to_s"), 0))
28
+
29
+ /**
30
+ * Raise an RDO::Exception with the given msg format and any number of parameters.
31
+ *
32
+ * @param (char *) msg
33
+ * a format string passed to rb_raise()
34
+ *
35
+ * @param (void *) ...
36
+ * args used to interpolate the error message
37
+ */
38
+ #define RDO_ERROR(...) (rb_raise(rb_path2class("RDO::Exception"), __VA_ARGS__))
39
+
40
+ /**
41
+ * Factory to return a new RDO::Result for an Enumerable object of tuples.
42
+ *
43
+ * @param VALUE (Enumerable) tuples
44
+ * an object that knows how to iterate all tuples
45
+ *
46
+ * @param VALUE (Hash)
47
+ * an optional hash of query info.
48
+ *
49
+ * @return VALUE (RDO::Result)
50
+ * a new Result object
51
+ */
52
+ #define RDO_RESULT(tuples, info) \
53
+ (rb_funcall(rb_path2class("RDO::Result"), rb_intern("new"), 2, tuples, info))
54
+
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
+
21
67
  /**
22
68
  * Convert a C string to a ruby String.
23
69
  *
@@ -30,9 +76,8 @@
30
76
  * @return VALUE (String)
31
77
  * a Ruby String
32
78
  */
33
- #define RDO_STRING(s, len, enc) ( \
34
- rb_enc_associate_index(rb_str_new(s, len), enc > 0 ? enc : 0) \
35
- )
79
+ #define RDO_STRING(s, len, enc) \
80
+ (rb_enc_associate_index(rb_str_new(s, len), enc > 0 ? enc : 0))
36
81
 
37
82
  /**
38
83
  * Convert a C string to a ruby String, assuming possible NULL bytes.
@@ -64,11 +109,9 @@
64
109
  * @return VALUE (Float)
65
110
  * a ruby Float
66
111
  */
67
- #define RDO_FLOAT(s) ( \
68
- rb_funcall(rb_path2class("RDO::Util"), \
69
- rb_intern("float"), 1, \
70
- rb_str_new2(s)) \
71
- )
112
+ #define RDO_FLOAT(s) \
113
+ (rb_funcall(rb_path2class("RDO::Util"), \
114
+ rb_intern("float"), 1, rb_str_new2(s)))
72
115
 
73
116
  /**
74
117
  * Convert a C string representing a precision decimal into a BigDecimal.
@@ -83,11 +126,9 @@
83
126
  * RDO_DECIMAL("1.234")
84
127
  * => #<BigDecimal:7feb42b2b6e8,'0.1234E1',18(18)>
85
128
  */
86
- #define RDO_DECIMAL(s) ( \
87
- rb_funcall(rb_path2class("RDO::Util"), \
88
- rb_intern("decimal"), 1, \
89
- rb_str_new2(s)) \
90
- )
129
+ #define RDO_DECIMAL(s) \
130
+ (rb_funcall(rb_path2class("RDO::Util"), \
131
+ rb_intern("decimal"), 1, rb_str_new2(s)))
91
132
 
92
133
  /**
93
134
  * Convert a C string representing a date into a Date.
@@ -102,11 +143,9 @@
102
143
  * RDO_DATE("431-09-22 BC")
103
144
  * #<Date: -0430-09-22 ((1564265j,0s,0n),+0s,2299161j)>
104
145
  */
105
- #define RDO_DATE(s) ( \
106
- rb_funcall(rb_path2class("RDO::Util"), \
107
- rb_intern("date"), 1, \
108
- rb_str_new2(s)) \
109
- )
146
+ #define RDO_DATE(s) \
147
+ (rb_funcall(rb_path2class("RDO::Util"), \
148
+ rb_intern("date"), 1, rb_str_new2(s)))
110
149
 
111
150
  /**
112
151
  * Convert a C string representing a date & time with no time zone into a DateTime.
@@ -121,11 +160,9 @@
121
160
  * RDO_DATE_TIME_WITHOUT_ZONE("2012-09-22 04:36:12")
122
161
  * #<DateTime: 2012-09-22T04:36:12+10:00 ((2456192j,66972s,0n),+36000s,2299161j)>
123
162
  */
124
- #define RDO_DATE_TIME_WITHOUT_ZONE(s) ( \
125
- rb_funcall(rb_path2class("RDO::Util"), \
126
- rb_intern("date_time_without_zone"), 1, \
127
- rb_str_new2(s)) \
128
- )
163
+ #define RDO_DATE_TIME_WITHOUT_ZONE(s) \
164
+ (rb_funcall(rb_path2class("RDO::Util"), \
165
+ rb_intern("date_time_without_zone"), 1, rb_str_new2(s)))
129
166
 
130
167
  /**
131
168
  * Convert a C string representing a date & time that includes a time zone into a DateTime.
@@ -140,11 +177,9 @@
140
177
  * RDO_DATE_TIME_WITHOUT_ZONE("2012-09-22 04:36:12+10:00")
141
178
  * #<DateTime: 2012-09-22T04:36:12+10:00 ((2456192j,66972s,0n),+36000s,2299161j)>
142
179
  */
143
- #define RDO_DATE_TIME_WITH_ZONE(s) ( \
144
- rb_funcall(rb_path2class("RDO::Util"), \
145
- rb_intern("date_time_with_zone"), 1, \
146
- rb_str_new2(s)) \
147
- )
180
+ #define RDO_DATE_TIME_WITH_ZONE(s) \
181
+ (rb_funcall(rb_path2class("RDO::Util"), \
182
+ rb_intern("date_time_with_zone"), 1, rb_str_new2(s)))
148
183
 
149
184
  /**
150
185
  * Convert a boolean string to TrueClass/FalseClass.
@@ -156,22 +191,3 @@
156
191
  * the boolean representation
157
192
  */
158
193
  #define RDO_BOOL(s) ((s[0] == 't') ? Qtrue : Qfalse)
159
-
160
- /**
161
- * Wrap the given StatementExecutor in a RDO::Statement.
162
- *
163
- * @param VALUE
164
- * any object that responds to #command and #execute
165
- *
166
- * @return VALUE
167
- * an RDO::Statement
168
- */
169
- #define RDO_STATEMENT(executor) ( \
170
- rb_funcall(rb_path2class("RDO::Statement"), \
171
- rb_intern("new"), 1, executor) \
172
- )
173
-
174
- /**
175
- * Convenience to call #to_s on any Ruby object.
176
- */
177
- #define RDO_OBJ_TO_S(obj) (rb_funcall(obj, rb_intern("to_s"), 0))
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.0.6
4
+ version: 0.0.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-09-24 00:00:00.000000000 Z
12
+ date: 2012-09-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -70,6 +70,7 @@ files:
70
70
  - spec/rdo/connection_spec.rb
71
71
  - spec/rdo/driver_spec.rb
72
72
  - spec/rdo/emulated_statements_spec.rb
73
+ - spec/rdo/rdo_spec.rb
73
74
  - spec/rdo/result_spec.rb
74
75
  - spec/rdo/statement_spec.rb
75
76
  - spec/rdo/util_spec.rb
@@ -105,6 +106,7 @@ test_files:
105
106
  - spec/rdo/connection_spec.rb
106
107
  - spec/rdo/driver_spec.rb
107
108
  - spec/rdo/emulated_statements_spec.rb
109
+ - spec/rdo/rdo_spec.rb
108
110
  - spec/rdo/result_spec.rb
109
111
  - spec/rdo/statement_spec.rb
110
112
  - spec/rdo/util_spec.rb