swift 0.10.0 → 0.11.0
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/API.rdoc +9 -16
- data/README.rdoc +10 -7
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/ext/adapter.cc +155 -25
- data/ext/extconf.rb +3 -3
- data/ext/pool.cc +1 -1
- data/ext/result.cc +2 -2
- data/ext/statement.cc +1 -1
- data/ext/swift.cc +49 -4
- data/ext/swift.h +2 -1
- data/lib/swift/adapter/sql.rb +69 -0
- data/lib/swift/adapter.rb +15 -136
- data/lib/swift/attribute.rb +7 -0
- data/lib/swift/db.rb +5 -3
- data/lib/swift/migrations.rb +51 -8
- data/lib/swift/scheme.rb +32 -36
- data/lib/swift.rb +2 -1
- data/swift.gemspec +10 -25
- data/test/helper.rb +6 -0
- data/test/minitest_teardown_hack.rb +20 -0
- data/test/test_scheme.rb +17 -22
- data/test/test_swift.rb +71 -0
- metadata +42 -78
- data/examples/async.rb +0 -65
- data/examples/db.rb +0 -44
- data/examples/scheme.rb +0 -54
data/API.rdoc
CHANGED
@@ -13,29 +13,23 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
13
13
|
# Abstract.
|
14
14
|
Adapter
|
15
15
|
.new #=> Adapter
|
16
|
-
#all #=> Result
|
17
16
|
#begin #=> Adapter
|
18
17
|
#commit
|
19
18
|
#create #=> Scheme or Result
|
20
|
-
#
|
19
|
+
#delete #=> Result
|
21
20
|
#execute #=> Result
|
22
|
-
#first #=> Scheme
|
23
21
|
#get #=> Scheme
|
24
22
|
#prepare #=> Statement
|
25
23
|
#rollback
|
26
24
|
#transaction #=> Adapter
|
27
25
|
#update #=> Scheme or Result
|
28
26
|
#reconnect
|
29
|
-
#delete #=> Result
|
30
|
-
|
31
|
-
# TODO: DBI < Adapter
|
32
|
-
# returning? #=> true or false
|
33
27
|
|
34
28
|
# Concrete.
|
35
29
|
DB
|
36
|
-
Mysql < Adapter
|
37
|
-
Postgres < Adapter
|
38
|
-
Sqlite3 < Adapter
|
30
|
+
Mysql < Adapter::Sql
|
31
|
+
Postgres < Adapter::Sql
|
32
|
+
Sqlite3 < Adapter::Sql
|
39
33
|
|
40
34
|
# Enumerable collection of Scheme or Hash tuples.
|
41
35
|
Result
|
@@ -48,21 +42,20 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
48
42
|
#execute #=> Result
|
49
43
|
|
50
44
|
Scheme
|
51
|
-
.all #=> Result
|
52
45
|
.attribute #=> Type
|
53
46
|
.create #=> Scheme or Result
|
54
|
-
.first #=> Scheme
|
55
47
|
.get #=> Scheme
|
56
48
|
.header #=> Header
|
57
49
|
.load #=> Scheme
|
58
50
|
.new #=> Scheme
|
59
|
-
.scheme #=> Alias for self.class
|
60
51
|
.store #=> Symbol
|
61
|
-
#
|
52
|
+
#execute #=> Result
|
53
|
+
#prepare #=> Statement
|
54
|
+
#scheme #=> Alias for self.class
|
62
55
|
#tuple #=> Hash
|
63
56
|
#update #=> Scheme or Result
|
64
57
|
|
65
|
-
# Enumerable collection of Types for Scheme
|
58
|
+
# Enumerable collection of Types for Scheme.
|
66
59
|
Header
|
67
60
|
.new #=> Header
|
68
61
|
#all #=> [Type, ...]
|
@@ -83,7 +76,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
83
76
|
#default #=> Object
|
84
77
|
#define_scheme_methods
|
85
78
|
|
86
|
-
# Concrete
|
79
|
+
# Concrete.
|
87
80
|
Type
|
88
81
|
BigDecimal < Attribute
|
89
82
|
Boolean < Attribute
|
data/README.rdoc
CHANGED
@@ -121,12 +121,12 @@ Scheme/relation level helpers.
|
|
121
121
|
user.update
|
122
122
|
|
123
123
|
# Destroy
|
124
|
-
user.
|
124
|
+
user.delete
|
125
125
|
|
126
126
|
=== Conditions SQL syntax.
|
127
127
|
|
128
|
-
SQL is easy and most people know it so Swift ORM provides
|
129
|
-
|
128
|
+
SQL is easy and most people know it so Swift ORM provides simple #to_s
|
129
|
+
attribute to field name typecasting.
|
130
130
|
|
131
131
|
class User < Swift::Scheme
|
132
132
|
store :users
|
@@ -138,7 +138,10 @@ names to field names.
|
|
138
138
|
|
139
139
|
# Convert :name and :age to fields.
|
140
140
|
# select * from users where eman like '%Arthurton' and ega > 20
|
141
|
-
users = User.
|
141
|
+
users = User.execute(
|
142
|
+
%Q{select * from #{User} where #{User.name} like ? and #{User.age} > ?},
|
143
|
+
'%Arthurton', 20
|
144
|
+
)
|
142
145
|
|
143
146
|
=== Identity Map
|
144
147
|
|
@@ -162,8 +165,9 @@ Swift comes with a simple identity map. Just require it after you load swift.
|
|
162
165
|
# Create
|
163
166
|
User.create name: 'James Arthurton', email: 'james@arthurton.local' # => User
|
164
167
|
|
165
|
-
User.
|
166
|
-
|
168
|
+
find_user = User.prepare(%Q{select * from #{User} where #{User.name = ?})
|
169
|
+
find_user.execute('James Arthurton')
|
170
|
+
find_user.execute('James Arthurton') # Gets same object reference
|
167
171
|
|
168
172
|
=== Bulk inserts
|
169
173
|
|
@@ -265,7 +269,6 @@ The adapter level SELECT benchmarks without using ORM.
|
|
265
269
|
|
266
270
|
* More tests.
|
267
271
|
* Assertions for dumb stuff.
|
268
|
-
* Abstract interface for other adapters? Move dbic++ to Swift::DBI::(Adapter, Pool, Result, Statment etc.)
|
269
272
|
|
270
273
|
== Contributing
|
271
274
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
data/ext/adapter.cc
CHANGED
@@ -1,28 +1,40 @@
|
|
1
1
|
#include "adapter.h"
|
2
2
|
|
3
|
-
//
|
3
|
+
// Extend the default dbi::FieldSet class with some ruby love.
|
4
4
|
class Fields : public dbi::FieldSet {
|
5
5
|
public:
|
6
|
-
|
6
|
+
Fields() : dbi::FieldSet(0) {}
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
void operator<<(VALUE v) {
|
9
|
+
VALUE name = TO_S(v);
|
10
|
+
fields.push_back(std::string(RSTRING_PTR(name), RSTRING_LEN(name)));
|
11
|
+
}
|
12
12
|
};
|
13
13
|
|
14
14
|
static VALUE cSwiftAdapter;
|
15
15
|
|
16
|
+
void build_extra_options_string(VALUE key, VALUE value, VALUE ptr) {
|
17
|
+
std::string *optstring = (std::string *)ptr;
|
18
|
+
*optstring += CSTRING(key) + std::string("=") + CSTRING(value) + std::string(";");
|
19
|
+
}
|
20
|
+
|
21
|
+
std::string parse_extra_options(VALUE options) {
|
22
|
+
std::string optstring = "";
|
23
|
+
if (!NIL_P(options))
|
24
|
+
rb_hash_foreach(options, RUBY_STATIC_FUNC(build_extra_options_string), (VALUE)&optstring);
|
25
|
+
return optstring;
|
26
|
+
}
|
27
|
+
|
16
28
|
static void adapter_free(dbi::Handle *handle) {
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
if (handle) {
|
30
|
+
handle->conn()->cleanup();
|
31
|
+
delete handle;
|
32
|
+
}
|
21
33
|
}
|
22
34
|
|
23
35
|
VALUE adapter_alloc(VALUE klass) {
|
24
|
-
|
25
|
-
|
36
|
+
dbi::Handle *handle = 0;
|
37
|
+
return Data_Wrap_Struct(klass, 0, adapter_free, handle);
|
26
38
|
}
|
27
39
|
|
28
40
|
dbi::Handle* adapter_handle(VALUE self) {
|
@@ -33,6 +45,14 @@ dbi::Handle* adapter_handle(VALUE self) {
|
|
33
45
|
return handle;
|
34
46
|
}
|
35
47
|
|
48
|
+
/*
|
49
|
+
Begin a transaction (unit of work).
|
50
|
+
|
51
|
+
@overload commit(name = nil)
|
52
|
+
@param [Symbol, String] name Optional transaction name.
|
53
|
+
|
54
|
+
@see Swift::Adapter#transaction
|
55
|
+
*/
|
36
56
|
static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
|
37
57
|
VALUE save_point;
|
38
58
|
rb_scan_args(argc, argv, "01", &save_point);
|
@@ -45,17 +65,31 @@ static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
|
|
45
65
|
return Qtrue;
|
46
66
|
}
|
47
67
|
|
68
|
+
/*
|
69
|
+
Close the connection.
|
70
|
+
*/
|
48
71
|
static VALUE adapter_close(VALUE self) {
|
49
72
|
dbi::Handle *handle = adapter_handle(self);
|
50
73
|
try { handle->close(); } CATCH_DBI_EXCEPTIONS();
|
51
74
|
return Qtrue;
|
52
75
|
}
|
53
76
|
|
54
|
-
|
77
|
+
/*
|
78
|
+
Shallow copy of adapter.
|
79
|
+
|
80
|
+
@note Currently not allowed.
|
81
|
+
@see Object.clone
|
82
|
+
*/
|
55
83
|
static VALUE adapter_clone(VALUE self) {
|
56
84
|
rb_raise(eSwiftRuntimeError, "clone is not allowed.");
|
57
85
|
}
|
58
86
|
|
87
|
+
/*
|
88
|
+
Commit a transaction (unit of work).
|
89
|
+
|
90
|
+
@overload commit(name = nil)
|
91
|
+
@param [Symbol, String] name Optional transaction name.
|
92
|
+
*/
|
59
93
|
static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
|
60
94
|
VALUE save_point;
|
61
95
|
rb_scan_args(argc, argv, "01", &save_point);
|
@@ -68,14 +102,28 @@ static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
|
|
68
102
|
return Qtrue;
|
69
103
|
}
|
70
104
|
|
71
|
-
|
105
|
+
/*
|
106
|
+
Shallow copy of adapter.
|
107
|
+
|
108
|
+
@note Currently not allowed.
|
109
|
+
@see Object.dup
|
110
|
+
*/
|
72
111
|
static VALUE adapter_dup(VALUE self) {
|
73
112
|
rb_raise(eSwiftRuntimeError, "dup is not allowed.");
|
74
113
|
}
|
75
114
|
|
76
|
-
|
115
|
+
/*
|
116
|
+
Escape a string.
|
117
|
+
|
118
|
+
@note Bind values do not need to be escaped.
|
119
|
+
|
120
|
+
@overload escape(value)
|
121
|
+
@param [String] value String to be escaped.
|
122
|
+
@return [String]
|
123
|
+
*/
|
77
124
|
static VALUE adapter_escape(VALUE self, VALUE value) {
|
78
|
-
if (TYPE(value) != T_STRING)
|
125
|
+
if (TYPE(value) != T_STRING)
|
126
|
+
value = TO_S(value);
|
79
127
|
|
80
128
|
dbi::Handle *handle = adapter_handle(self);
|
81
129
|
try {
|
@@ -85,7 +133,19 @@ static VALUE adapter_escape(VALUE self, VALUE value) {
|
|
85
133
|
CATCH_DBI_EXCEPTIONS();
|
86
134
|
}
|
87
135
|
|
88
|
-
|
136
|
+
/*
|
137
|
+
Execute a single statement.
|
138
|
+
|
139
|
+
@example
|
140
|
+
result = User.execute("select * from #{User} where #{User.name} = ?", 'apple')
|
141
|
+
result.first # User object.
|
142
|
+
|
143
|
+
@overload execute(statement = '', *binds, &block)
|
144
|
+
@param [String] statement Query statement.
|
145
|
+
@param [*Object] binds Bind values.
|
146
|
+
@yield [Swift::Result]
|
147
|
+
@return [Swift::Result]
|
148
|
+
*/
|
89
149
|
static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
90
150
|
VALUE statement, bind_values, block, rows, scheme = Qnil;
|
91
151
|
|
@@ -116,12 +176,38 @@ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
|
116
176
|
CATCH_DBI_EXCEPTIONS();
|
117
177
|
}
|
118
178
|
|
179
|
+
/*
|
180
|
+
Reestablish a connection.
|
181
|
+
*/
|
119
182
|
static VALUE adapter_reconnect(VALUE self) {
|
120
183
|
dbi::Handle *handle = adapter_handle(self);
|
121
184
|
try { handle->reconnect(); } CATCH_DBI_EXCEPTIONS();
|
122
185
|
return Qtrue;
|
123
186
|
}
|
124
187
|
|
188
|
+
/*
|
189
|
+
Setup a new DB connection.
|
190
|
+
|
191
|
+
You almost certainly want to setup a <tt>:default</tt> named adapter. The <tt>:default</tt> scope will be used
|
192
|
+
for unscoped calls to <tt>Swift.db</tt>.
|
193
|
+
|
194
|
+
@example
|
195
|
+
Swift.setup :default, Swift::DB::Postgres, db: 'db1'
|
196
|
+
Swift.setup :other, Swift::DB::Postgres, db: 'db2'
|
197
|
+
|
198
|
+
@overload new(options = {})
|
199
|
+
@param [Hash] options Connection options
|
200
|
+
@option options [String] :db Name.
|
201
|
+
@option options [String] :user (*nix login user)
|
202
|
+
@option options [String] :password ('')
|
203
|
+
@option options [String] :host ('localhost')
|
204
|
+
@option options [Integer] :port (DB default)
|
205
|
+
@option options [String] :timezone (*nix TZ format) See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
206
|
+
@return [Swift::Adapter]
|
207
|
+
|
208
|
+
@see Swift::DB
|
209
|
+
@see Swift::Adapter
|
210
|
+
*/
|
125
211
|
static VALUE adapter_initialize(VALUE self, VALUE options) {
|
126
212
|
VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
|
127
213
|
VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
|
@@ -130,7 +216,18 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
130
216
|
if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Adapter#new called without :db");
|
131
217
|
if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver");
|
132
218
|
|
133
|
-
user
|
219
|
+
user = NIL_P(user) ? current_user() : user;
|
220
|
+
VALUE extra = rb_hash_dup(options);
|
221
|
+
|
222
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("db")));
|
223
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("driver")));
|
224
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("user")));
|
225
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("password")));
|
226
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("host")));
|
227
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("port")));
|
228
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("timezone")));
|
229
|
+
|
230
|
+
std::string extra_options_string = parse_extra_options(extra);
|
134
231
|
|
135
232
|
try {
|
136
233
|
DATA_PTR(self) = new dbi::Handle(
|
@@ -139,16 +236,30 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
139
236
|
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
|
140
237
|
CSTRING(db),
|
141
238
|
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
|
142
|
-
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
|
239
|
+
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port")))),
|
240
|
+
extra_options_string.size() > 0 ? (char*)extra_options_string.c_str() : 0
|
143
241
|
);
|
144
242
|
}
|
145
243
|
CATCH_DBI_EXCEPTIONS();
|
146
244
|
|
147
|
-
rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
|
148
245
|
rb_iv_set(self, "@options", options);
|
246
|
+
rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
|
247
|
+
|
149
248
|
return Qnil;
|
150
249
|
}
|
151
250
|
|
251
|
+
/*
|
252
|
+
Prepare a statement for on or more executions.
|
253
|
+
|
254
|
+
@example
|
255
|
+
sth = User.prepare("select * from #{User} where #{User.name} = ?")
|
256
|
+
sth.execute('apple') #=> Result
|
257
|
+
sth.execute('benny') #=> Result
|
258
|
+
|
259
|
+
@overload prepare(statement, &block)
|
260
|
+
@param [String] statement Query statement.
|
261
|
+
@return [Swift::Statement]
|
262
|
+
*/
|
152
263
|
static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
|
153
264
|
VALUE sql, scheme, prepared;
|
154
265
|
dbi::AbstractStatement *statement;
|
@@ -170,6 +281,12 @@ static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
|
|
170
281
|
CATCH_DBI_EXCEPTIONS();
|
171
282
|
}
|
172
283
|
|
284
|
+
/*
|
285
|
+
Rollback the current transaction.
|
286
|
+
|
287
|
+
@overload rollback(name = nil)
|
288
|
+
@param [Symbol, String] name Optional transaction name.
|
289
|
+
*/
|
173
290
|
static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
174
291
|
VALUE save_point;
|
175
292
|
dbi::Handle *handle = adapter_handle(self);
|
@@ -182,12 +299,16 @@ static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
|
182
299
|
return Qtrue;
|
183
300
|
}
|
184
301
|
|
302
|
+
/*
|
303
|
+
Block form transaction sugar.
|
304
|
+
|
305
|
+
@overload transaction(name = nil, &block)
|
306
|
+
@param [Symbol, String] name Optional transaction name.
|
307
|
+
*/
|
185
308
|
static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
186
309
|
int status;
|
187
310
|
VALUE sp, block, block_result = Qnil;
|
188
|
-
|
189
311
|
dbi::Handle *handle = adapter_handle(self);
|
190
|
-
|
191
312
|
rb_scan_args(argc, argv, "01&", &sp, &block);
|
192
313
|
|
193
314
|
if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block.");
|
@@ -209,6 +330,16 @@ static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
|
209
330
|
return block_result;
|
210
331
|
}
|
211
332
|
|
333
|
+
/*
|
334
|
+
Bulk insert resources.
|
335
|
+
|
336
|
+
@overload write(store, fields, stream)
|
337
|
+
@param [Swift::Scheme, String] store Write to store.
|
338
|
+
@param [Array<Swift::Attribute, String>] fields Write to fields in store.
|
339
|
+
@param [IO] stream IO to read from.
|
340
|
+
|
341
|
+
@note The format of the stream and bulk write performance are entirely down to each adapter.
|
342
|
+
*/
|
212
343
|
static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
|
213
344
|
uint64_t rows = 0;
|
214
345
|
VALUE stream, table, fields;
|
@@ -234,11 +365,11 @@ static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
|
|
234
365
|
|
235
366
|
if (TYPE(stream) == T_STRING) {
|
236
367
|
dbi::StringIO io(RSTRING_PTR(stream), RSTRING_LEN(stream));
|
237
|
-
rows = handle->write(RSTRING_PTR(table), write_fields, &io);
|
368
|
+
rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io);
|
238
369
|
}
|
239
370
|
else {
|
240
371
|
AdapterIO io(stream);
|
241
|
-
rows = handle->write(RSTRING_PTR(table), write_fields, &io);
|
372
|
+
rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io);
|
242
373
|
}
|
243
374
|
return SIZET2NUM(rows);
|
244
375
|
}
|
@@ -266,4 +397,3 @@ void init_swift_adapter() {
|
|
266
397
|
rb_define_alloc_func(cSwiftAdapter, adapter_alloc);
|
267
398
|
}
|
268
399
|
|
269
|
-
|
data/ext/extconf.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
#!/usr/bin/ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
4
|
Config::CONFIG['CC'] = 'g++'
|
5
5
|
Config::CONFIG['CPP'] = 'g++'
|
6
6
|
|
7
|
-
$CFLAGS = '-fPIC -Os'
|
7
|
+
$CFLAGS = '-fPIC -Os -I/usr/include -I/opt/local/include -I/usr/local/include'
|
8
8
|
|
9
9
|
def apt_install_hint pkg
|
10
10
|
"sudo apt-get install #{pkg}"
|
11
11
|
end
|
12
12
|
|
13
13
|
def library_installed? name, hint
|
14
|
-
if
|
14
|
+
if find_library(name, 'main', *%w(/usr/lib /usr/local/lib /opt/lib /opt/local/lib /sw/lib))
|
15
15
|
true
|
16
16
|
else
|
17
17
|
$stderr.puts <<-ERROR
|
data/ext/pool.cc
CHANGED
@@ -26,7 +26,7 @@ VALUE pool_init(VALUE self, VALUE n, VALUE options) {
|
|
26
26
|
if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Pool#new called without :db");
|
27
27
|
if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "#new called without :driver");
|
28
28
|
|
29
|
-
user = NIL_P(user) ?
|
29
|
+
user = NIL_P(user) ? current_user() : user;
|
30
30
|
if (NUM2INT(n) < 1) rb_raise(eSwiftArgumentError, "Pool#new called with invalid pool size.");
|
31
31
|
|
32
32
|
try {
|
data/ext/result.cc
CHANGED
@@ -45,12 +45,12 @@ dbi::AbstractResult* result_handle(VALUE self) {
|
|
45
45
|
return handle->result;
|
46
46
|
}
|
47
47
|
|
48
|
-
//
|
48
|
+
// NOTE clone and dup cannot be allowed since the underlying c++ object needs to be cloned, which
|
49
|
+
// frankly is too much work :)
|
49
50
|
static VALUE result_clone(VALUE self) {
|
50
51
|
rb_raise(eSwiftRuntimeError, "clone is not allowed.");
|
51
52
|
}
|
52
53
|
|
53
|
-
// TODO:
|
54
54
|
static VALUE result_dup(VALUE self) {
|
55
55
|
rb_raise(eSwiftRuntimeError, "dup is not allowed.");
|
56
56
|
}
|
data/ext/statement.cc
CHANGED
@@ -43,7 +43,7 @@ dbi::AbstractStatement* statement_handle(VALUE self) {
|
|
43
43
|
return handle->statement;
|
44
44
|
}
|
45
45
|
|
46
|
-
//
|
46
|
+
// array -> splat -> array is an overhead, but it reads nicer.
|
47
47
|
static VALUE statement_execute(int argc, VALUE *argv, VALUE self) {
|
48
48
|
VALUE bind_values, block;
|
49
49
|
rb_scan_args(argc, argv, "0*&", &bind_values, &block);
|
data/ext/swift.cc
CHANGED
@@ -7,17 +7,45 @@ VALUE eSwiftArgumentError;
|
|
7
7
|
VALUE eSwiftRuntimeError;
|
8
8
|
VALUE eSwiftConnectionError;
|
9
9
|
|
10
|
+
/*
|
11
|
+
Initialize Swift with a non standard dbic++ path.
|
12
|
+
|
13
|
+
@note
|
14
|
+
By default Swift looks in '/usr/lib/dbic++/'. Not normally required unless you install dbic++ somewhere funny.
|
15
|
+
|
16
|
+
@overload init(path)
|
17
|
+
@param [path] path Non standard dbic++ path.
|
18
|
+
*/
|
10
19
|
VALUE swift_init(VALUE self, VALUE path) {
|
11
20
|
try { dbi::dbiInitialize(CSTRING(path)); } CATCH_DBI_EXCEPTIONS();
|
12
21
|
return Qtrue;
|
13
22
|
}
|
14
23
|
|
24
|
+
/*
|
25
|
+
Trace statement execution.
|
26
|
+
|
27
|
+
@example Toggle tracing.
|
28
|
+
Swift.trace true
|
29
|
+
Swift.db.execute 'select * from users'
|
30
|
+
Swift.trace false
|
31
|
+
@example Block form.
|
32
|
+
Swift.trace true do
|
33
|
+
Swift.db.execute 'select * from users'
|
34
|
+
end
|
35
|
+
|
36
|
+
@overload trace(show = true, output = $stderr)
|
37
|
+
@param [true, false] show Optional trace toggle boolean.
|
38
|
+
@param [IO] output Optional output. Defaults to stderr.
|
39
|
+
*/
|
15
40
|
VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
16
41
|
VALUE flag, io;
|
17
42
|
rb_io_t *fptr;
|
18
|
-
int fd = 2; // defaults to stderr
|
43
|
+
int status, fd = 2; // defaults to stderr
|
19
44
|
|
20
|
-
rb_scan_args(argc, argv, "
|
45
|
+
rb_scan_args(argc, argv, "02", &flag, &io);
|
46
|
+
|
47
|
+
if (NIL_P(flag))
|
48
|
+
flag = Qtrue;
|
21
49
|
|
22
50
|
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
23
51
|
rb_raise(eSwiftArgumentError, "Swift#trace expects a boolean flag, got %s", CSTRING(flag));
|
@@ -27,8 +55,25 @@ VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
|
27
55
|
fd = fptr->fd;
|
28
56
|
}
|
29
57
|
|
30
|
-
|
31
|
-
|
58
|
+
// block form trace
|
59
|
+
if (rb_block_given_p()) {
|
60
|
+
// orig values
|
61
|
+
bool orig_trace = dbi::_trace;
|
62
|
+
int orig_trace_fd = dbi::_trace_fd;
|
63
|
+
|
64
|
+
dbi::trace(flag == Qtrue ? true : false, fd);
|
65
|
+
VALUE block_result = rb_protect(rb_yield, Qnil, &status);
|
66
|
+
dbi::trace(orig_trace, orig_trace_fd);
|
67
|
+
|
68
|
+
if (status)
|
69
|
+
rb_jump_tag(status);
|
70
|
+
else
|
71
|
+
return block_result;
|
72
|
+
}
|
73
|
+
else {
|
74
|
+
dbi::trace(flag == Qtrue ? true : false, fd);
|
75
|
+
return flag;
|
76
|
+
}
|
32
77
|
}
|
33
78
|
|
34
79
|
VALUE atexit_gc(...) {
|
data/ext/swift.h
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
#define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
|
13
13
|
#define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
|
14
14
|
#define CSTRING(v) RSTRING_PTR(TO_S(v))
|
15
|
+
#define RUBY_STATIC_FUNC(func) ((int (*)(ANYARGS))func)
|
15
16
|
|
16
17
|
extern VALUE eSwiftError;
|
17
18
|
extern VALUE eSwiftArgumentError;
|
@@ -31,7 +32,7 @@ extern VALUE eSwiftConnectionError;
|
|
31
32
|
|
32
33
|
|
33
34
|
// works without a controlling tty, getlogin() will fail when process is daemonized.
|
34
|
-
inline VALUE
|
35
|
+
inline VALUE current_user() {
|
35
36
|
struct passwd *ptr = getpwuid(getuid());
|
36
37
|
return ptr ? rb_str_new2(ptr->pw_name) : rb_str_new2("root");
|
37
38
|
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'swift/adapter'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
class Adapter
|
5
|
+
|
6
|
+
# Abstract SQL Adapter.
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
class Sql < Adapter
|
10
|
+
protected
|
11
|
+
def returning?
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_cached scheme, name, &block
|
16
|
+
@prepared ||= Hash.new{|h,k| h[k] = Hash.new}
|
17
|
+
@prepared[scheme][name] ||= prepare(scheme, yield)
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_get scheme
|
21
|
+
prepare_cached(scheme, :get) do
|
22
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
23
|
+
"select * from #{scheme.store} where #{where} limit 1"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare_create scheme
|
28
|
+
prepare_cached(scheme, :create) do
|
29
|
+
values = (['?'] * scheme.header.insertable.size).join(', ')
|
30
|
+
returning = "returning #{scheme.header.serial}" if scheme.header.serial and returning?
|
31
|
+
"insert into #{scheme.store} (#{scheme.header.insertable.join(', ')}) values (#{values}) #{returning}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_update scheme
|
36
|
+
prepare_cached(scheme, :update) do
|
37
|
+
set = scheme.header.updatable.map{|field| "#{field} = ?"}.join(', ')
|
38
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
39
|
+
"update #{scheme.store} set #{set} where #{where}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepare_delete scheme
|
44
|
+
prepare_cached(scheme, :delete) do
|
45
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
46
|
+
"delete from #{scheme.store} where #{where}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def field_definition attribute
|
51
|
+
"#{attribute.field} " + field_type(attribute)
|
52
|
+
end
|
53
|
+
|
54
|
+
def field_type attribute
|
55
|
+
case attribute
|
56
|
+
when Type::String then 'text'
|
57
|
+
when Type::Integer then attribute.serial ? 'serial' : 'integer'
|
58
|
+
when Type::Float then 'float'
|
59
|
+
when Type::BigDecimal then 'numeric'
|
60
|
+
when Type::Time then 'timestamp'
|
61
|
+
when Type::Date then 'date'
|
62
|
+
when Type::Boolean then 'boolean'
|
63
|
+
when Type::IO then 'blob'
|
64
|
+
else 'text'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end # Sql
|
68
|
+
end # Adapter
|
69
|
+
end # Swift
|