rdo-mysql 0.0.1

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.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.so
19
+ *.bundle
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ services: mysql
3
+ before_script:
4
+ - mysql -u root -e "CREATE DATABASE rdo;"
5
+ - mysql -u root -e "GRANT ALL ON rdo.* TO rdo@localhost IDENTIFIED BY 'rdo';"
6
+ script: "bundle exec rake spec"
7
+ rvm:
8
+ - 1.9.2
9
+ - 1.9.3
10
+ notifications:
11
+ email: chris@w3style.co.uk
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # allow a local version of RDO to be used during dev
6
+ if ENV["RDO_PATH"]
7
+ gem 'rdo', path: ENV["RDO_PATH"]
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 d11wtq
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ # RDO MySQL Driver
2
+
3
+ This is the MySQL driver for [RDO—Ruby Data Objects](https://github.com/d11wtq/rdo).
4
+
5
+ [![Build Status](https://secure.travis-ci.org/d11wtq/rdo-mysql.png)](http://travis-ci.org/d11wtq/rdo-mysql)
6
+
7
+ Refer to the RDO project [README](https://github.com/d11wtq/rdo) for full usage information.
8
+
9
+ ## Installation
10
+
11
+ Via rubygems:
12
+
13
+ $ gem install rdo-mysql
14
+
15
+ Or add this line to your application's Gemfile:
16
+
17
+ gem "rdo-mysql"
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ ## Usage
24
+
25
+ ``` ruby
26
+ require "rdo-mysql"
27
+
28
+ conn = RDO.connect("mysql://user:pass@localhost/db_name?encoding=utf-8")
29
+ ```
30
+
31
+ ## Prepared statements
32
+
33
+ MySQL, in theory supports prepared statements. But really you would not want
34
+ to use them. There are still a number of known limitations with MySQL prepared
35
+ statements, as outlined here:
36
+
37
+ - http://dev.mysql.com/doc/refman/5.0/en/c-api-prepared-statement-problems.html
38
+ - http://dev.mysql.com/doc/refman/5.1/en/c-api-prepared-statement-problems.html
39
+ - http://dev.mysql.com/doc/refman/5.5/en/c-api-prepared-statement-problems.html
40
+
41
+ rdo-mysql uses RDO's emulated prepared statement support instead. If people
42
+ shout loud enough that they'd prefer to use MySQL's problematic prepared
43
+ statements anyway, I'll implement them.
44
+
45
+ ## Contributing
46
+
47
+ If you find any bugs, please send a pull request if you think you can
48
+ fix it, or file in an issue in the issue tracker.
49
+
50
+ When sending pull requests, please use topic branches—don't send a pull
51
+ request from the master branch of your fork, as that may change
52
+ unintentionally.
53
+
54
+ Contributors will be credited in this README.
55
+
56
+ ## Copyright & Licensing
57
+
58
+ Written by Chris Corbyn.
59
+
60
+ Licensed under the MIT license. See the LICENSE file for full details.
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+
5
+ Rake::ExtensionTask.new('rdo_mysql') do |ext|
6
+ ext.lib_dir = File.join('lib', 'rdo_mysql')
7
+ end
8
+
9
+ desc "Run the full RSpec suite"
10
+ RSpec::Core::RakeTask.new('spec') do |t|
11
+ t.pattern = 'spec/'
12
+ end
13
+
14
+ Rake::Task['spec'].prerequisites << :compile
@@ -0,0 +1,179 @@
1
+ /*
2
+ * RDO MySQL Driver.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ #include "driver.h"
9
+ #include <stdio.h>
10
+ #include "tuples.h"
11
+ #include "macros.h"
12
+ #include <ruby/encoding.h>
13
+
14
+ /** Struct wrapped by RDO::MySQL::Driver */
15
+ typedef struct {
16
+ MYSQL * conn;
17
+ int is_open;
18
+ int encoding;
19
+ } RDOMySQLDriver;
20
+
21
+ /** Free memory associated with connection during GC */
22
+ static void rdo_mysql_driver_free(RDOMySQLDriver * driver) {
23
+ mysql_close(driver->conn);
24
+ free(driver);
25
+ }
26
+
27
+ /** Extract result information from the given result */
28
+ static VALUE rdo_mysql_result_info_new(MYSQL * conn, MYSQL_RES * res) {
29
+ unsigned long long count = (res == NULL) ? 0 : mysql_num_rows(res);
30
+
31
+ VALUE info = rb_hash_new();
32
+ rb_hash_aset(info, ID2SYM(rb_intern("count")), LL2NUM(count));
33
+ rb_hash_aset(info, ID2SYM(rb_intern("insert_id")),
34
+ LL2NUM(mysql_insert_id(conn)));
35
+ rb_hash_aset(info, ID2SYM(rb_intern("affected_rows")),
36
+ LL2NUM(mysql_affected_rows(conn)));
37
+
38
+ return info;
39
+ }
40
+
41
+ /** Allocate memory, wrapping RDOMySQLDriver */
42
+ static VALUE rdo_mysql_driver_allocate(VALUE klass) {
43
+ RDOMySQLDriver * driver = malloc(sizeof(RDOMySQLDriver));
44
+ driver->conn = NULL;
45
+ driver->is_open = 0;
46
+ driver->encoding = -1;
47
+
48
+ return Data_Wrap_Struct(klass, 0, rdo_mysql_driver_free, driver);
49
+ }
50
+
51
+ /** Open a connection to MySQL */
52
+ static VALUE rdo_mysql_driver_open(VALUE self) {
53
+ RDOMySQLDriver * driver;
54
+ Data_Get_Struct(self, RDOMySQLDriver, driver);
55
+
56
+ if (driver->is_open) {
57
+ return Qtrue;
58
+ }
59
+
60
+ if (!(driver->conn = mysql_init(NULL))) {
61
+ RDO_ERROR("Failed to connect to MySQL. Could not allocate memory.");
62
+ }
63
+
64
+ VALUE host = rb_funcall(self, rb_intern("host"), 0);
65
+ VALUE port = rb_funcall(self, rb_intern("port"), 0);
66
+ VALUE user = rb_funcall(self, rb_intern("user"), 0);
67
+ VALUE pass = rb_funcall(self, rb_intern("password"), 0);
68
+ VALUE db = rb_funcall(self, rb_intern("database"), 0);
69
+
70
+ Check_Type(host, T_STRING);
71
+ Check_Type(port, T_FIXNUM);
72
+ Check_Type(user, T_STRING);
73
+ Check_Type(pass, T_STRING);
74
+ Check_Type(db, T_STRING);
75
+
76
+ if (!mysql_real_connect(driver->conn,
77
+ RSTRING_PTR(host),
78
+ RSTRING_PTR(user),
79
+ RSTRING_PTR(pass),
80
+ RSTRING_PTR(db),
81
+ NUM2INT(port),
82
+ NULL, // UNIX socket
83
+ 0)) {
84
+ RDO_ERROR("MySQL connection failed: %s", mysql_error(driver->conn));
85
+ } else {
86
+ driver->is_open = 1;
87
+ driver->encoding = rb_enc_find_index(
88
+ RSTRING_PTR(rb_funcall(self, rb_intern("encoding"), 0)));
89
+ rb_funcall(self, rb_intern("after_open"), 0);
90
+ }
91
+
92
+ return Qtrue;
93
+ }
94
+
95
+ /** Return true if the connection is open */
96
+ static VALUE rdo_mysql_driver_open_p(VALUE self) {
97
+ RDOMySQLDriver * driver;
98
+ Data_Get_Struct(self, RDOMySQLDriver, driver);
99
+
100
+ return driver->is_open ? Qtrue : Qfalse;
101
+ }
102
+
103
+ /** Close the connection to MySQL */
104
+ static VALUE rdo_mysql_driver_close(VALUE self) {
105
+ RDOMySQLDriver * driver;
106
+ Data_Get_Struct(self, RDOMySQLDriver, driver);
107
+
108
+ mysql_close(driver->conn);
109
+ driver->conn = NULL;
110
+ driver->is_open = 0;
111
+ driver->encoding = -1;
112
+
113
+ return Qtrue;
114
+ }
115
+
116
+ /** Quote values for safe insertion into a statement */
117
+ static VALUE rdo_mysql_driver_quote(VALUE self, VALUE obj) {
118
+ if (TYPE(obj) != T_STRING) {
119
+ obj = RDO_OBJ_TO_S(obj);
120
+ }
121
+
122
+ RDOMySQLDriver * driver;
123
+ Data_Get_Struct(self, RDOMySQLDriver, driver);
124
+
125
+ if (!(driver->is_open)) {
126
+ RDO_ERROR("Unable to quote value: connection is closed");
127
+ }
128
+
129
+ char quoted[RSTRING_LEN(obj) * 2 + 1];
130
+
131
+ mysql_real_escape_string(driver->conn,
132
+ quoted, RSTRING_PTR(obj), RSTRING_LEN(obj));
133
+
134
+ return rb_str_new2(quoted);
135
+ }
136
+
137
+ /** Execute a statement with possible bind parameters */
138
+ static VALUE rdo_mysql_driver_execute(int argc, VALUE * args, VALUE self) {
139
+ if (argc < 1) {
140
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
141
+ }
142
+
143
+ RDOMySQLDriver * driver;
144
+ Data_Get_Struct(self, RDOMySQLDriver, driver);
145
+
146
+ if (!(driver->is_open)) {
147
+ RDO_ERROR("Cannot execute query: connection is not open");
148
+ }
149
+
150
+ VALUE stmt = RDO_INTERPOLATE(self, args, argc);
151
+
152
+ if (mysql_real_query(driver->conn,
153
+ RSTRING_PTR(stmt),
154
+ RSTRING_LEN(stmt)) != 0) {
155
+ RDO_ERROR("Failed to execute query: %s", mysql_error(driver->conn));
156
+ }
157
+
158
+ MYSQL_RES * res = mysql_store_result(driver->conn);
159
+
160
+ return RDO_RESULT(rdo_mysql_tuple_list_new(res, driver->encoding),
161
+ rdo_mysql_result_info_new(driver->conn, res));
162
+ }
163
+
164
+ /** Initializer driver during extension initialization */
165
+ void Init_rdo_mysql_driver(void) {
166
+ rb_require("rdo/mysql/driver");
167
+
168
+ VALUE cMySQL = rb_path2class("RDO::MySQL::Driver");
169
+
170
+ rb_define_alloc_func(cMySQL, rdo_mysql_driver_allocate);
171
+
172
+ rb_define_method(cMySQL, "open", rdo_mysql_driver_open, 0);
173
+ rb_define_method(cMySQL, "open?", rdo_mysql_driver_open_p, 0);
174
+ rb_define_method(cMySQL, "close", rdo_mysql_driver_close, 0);
175
+ rb_define_method(cMySQL, "quote", rdo_mysql_driver_quote, 1);
176
+ rb_define_method(cMySQL, "execute", rdo_mysql_driver_execute, -1);
177
+
178
+ Init_rdo_mysql_tuples();
179
+ }
@@ -0,0 +1,12 @@
1
+ /*
2
+ * RDO MySQL Driver.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ #include <ruby.h>
9
+ #include <mysql.h>
10
+
11
+ /** Initializer driver during extension initialization */
12
+ void Init_rdo_mysql_driver(void);
@@ -0,0 +1,34 @@
1
+ require "mkmf"
2
+
3
+ if ENV["CC"]
4
+ RbConfig::MAKEFILE_CONFIG["CC"] = ENV["CC"]
5
+ end
6
+
7
+ def config_value(type, flag)
8
+ IO.popen("mysql_config --#{type}").
9
+ readline.chomp.
10
+ split(/\s+/).select{|s| s =~ /^#{flag}/}.
11
+ map{|s| s.sub(/^#{flag}/, "")}.uniq
12
+ rescue
13
+ Array[]
14
+ end
15
+
16
+ def have_build_env
17
+ [
18
+ have_header("mysql.h"),
19
+ config_value("libs", "-l").all?{|lib| have_library(lib)}
20
+ ].all?
21
+ end
22
+
23
+ dir_config(
24
+ "mysqlclient",
25
+ config_value("include", "-I"),
26
+ config_value("libs", "-L")
27
+ )
28
+
29
+ unless have_build_env
30
+ puts "Unable to find mysqlclient libraries and headers. Not building."
31
+ exit(1)
32
+ end
33
+
34
+ create_makefile("rdo_mysql/rdo_mysql")
@@ -0,0 +1,212 @@
1
+ /*
2
+ * RDO—Ruby Data Objects.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ /** -------------------------------------------------------------------------
9
+ * These macros are for use by RDO driver developers.
10
+ *
11
+ * They simplify the logic needed when converting types provided by the RDBMS
12
+ * into their equivalent Ruby types.
13
+ *
14
+ * All of these macros take a C string and return a Ruby VALUE.
15
+ *
16
+ * The actual logic for many of the conversions is handled in RDO::Util, which
17
+ * is written in Ruby.
18
+ * --------------------------------------------------------------------------
19
+ */
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
+ * 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
+
59
+ /**
60
+ * Factory to return a new RDO::Result for an Enumerable object of tuples.
61
+ *
62
+ * @param VALUE (Enumerable) tuples
63
+ * an object that knows how to iterate all tuples
64
+ *
65
+ * @param VALUE (Hash)
66
+ * an optional hash of query info.
67
+ *
68
+ * @return VALUE (RDO::Result)
69
+ * a new Result object
70
+ */
71
+ #define RDO_RESULT(tuples, info) \
72
+ (rb_funcall(rb_path2class("RDO::Result"), rb_intern("new"), 2, tuples, info))
73
+
74
+ /**
75
+ * Wrap the given StatementExecutor in a RDO::Statement.
76
+ *
77
+ * @param VALUE
78
+ * any object that responds to #command and #execute
79
+ *
80
+ * @return VALUE
81
+ * an RDO::Statement
82
+ */
83
+ #define RDO_STATEMENT(executor) \
84
+ (rb_funcall(rb_path2class("RDO::Statement"), rb_intern("new"), 1, executor))
85
+
86
+ /**
87
+ * Convert a C string to a ruby String.
88
+ *
89
+ * @param (char *) s
90
+ * a C string that is valid in the default encoding
91
+ *
92
+ * @param (size_t) len
93
+ * the length of the string
94
+ *
95
+ * @return VALUE (String)
96
+ * a Ruby String
97
+ */
98
+ #define RDO_STRING(s, len, enc) \
99
+ (rb_enc_associate_index(rb_str_new(s, len), enc > 0 ? enc : 0))
100
+
101
+ /**
102
+ * Convert a C string to a ruby String, assuming possible NULL bytes.
103
+ *
104
+ * @param (char *) s
105
+ * a C string, possibly containing nulls
106
+ *
107
+ * @param (size_t) len
108
+ * the length of the string
109
+ *
110
+ * @return VALUE (String)
111
+ * a Ruby String
112
+ */
113
+ #define RDO_BINARY_STRING(s, len) (rb_str_new(s, len))
114
+
115
+ /**
116
+ * Convert a C string to a Fixnum.
117
+ */
118
+ #define RDO_FIXNUM(s) (rb_cstr2inum(s, 10))
119
+
120
+ /**
121
+ * Convert a C string to a Float.
122
+ *
123
+ * This supports Infinity and NaN.
124
+ *
125
+ * @param (char *) s
126
+ * a C string representing a float (e.g. "1.234")
127
+ *
128
+ * @return VALUE (Float)
129
+ * a ruby Float
130
+ */
131
+ #define RDO_FLOAT(s) \
132
+ (rb_funcall(rb_path2class("RDO::Util"), \
133
+ rb_intern("float"), 1, rb_str_new2(s)))
134
+
135
+ /**
136
+ * Convert a C string representing a precision decimal into a BigDecimal.
137
+ *
138
+ * @param (char *) s
139
+ * a C string representing a decimal ("1.245")
140
+ *
141
+ * @return VALUE (BigDecimal)
142
+ * a BigDecimal representation of this string
143
+ *
144
+ * @example
145
+ * RDO_DECIMAL("1.234")
146
+ * => #<BigDecimal:7feb42b2b6e8,'0.1234E1',18(18)>
147
+ */
148
+ #define RDO_DECIMAL(s) \
149
+ (rb_funcall(rb_path2class("RDO::Util"), \
150
+ rb_intern("decimal"), 1, rb_str_new2(s)))
151
+
152
+ /**
153
+ * Convert a C string representing a date into a Date.
154
+ *
155
+ * @param (char *) s
156
+ * the C string with a parseable date
157
+ *
158
+ * @return VALUE (Date)
159
+ * a Date, exactly as was specified in the input
160
+ *
161
+ * @example
162
+ * RDO_DATE("431-09-22 BC")
163
+ * #<Date: -0430-09-22 ((1564265j,0s,0n),+0s,2299161j)>
164
+ */
165
+ #define RDO_DATE(s) \
166
+ (rb_funcall(rb_path2class("RDO::Util"), \
167
+ rb_intern("date"), 1, rb_str_new2(s)))
168
+
169
+ /**
170
+ * Convert a C string representing a date & time with no time zone into a DateTime.
171
+ *
172
+ * @param (char *) s
173
+ * the C string with the date & time provided
174
+ *
175
+ * @return VALUE (DateTime)
176
+ * a DateTime, assuming the system time zone
177
+ *
178
+ * @example
179
+ * RDO_DATE_TIME_WITHOUT_ZONE("2012-09-22 04:36:12")
180
+ * #<DateTime: 2012-09-22T04:36:12+10:00 ((2456192j,66972s,0n),+36000s,2299161j)>
181
+ */
182
+ #define RDO_DATE_TIME_WITHOUT_ZONE(s) \
183
+ (rb_funcall(rb_path2class("RDO::Util"), \
184
+ rb_intern("date_time_without_zone"), 1, rb_str_new2(s)))
185
+
186
+ /**
187
+ * Convert a C string representing a date & time that includes a time zone into a DateTime.
188
+ *
189
+ * @param (char *) s
190
+ * the C string with the date & time provided, including the time zone
191
+ *
192
+ * @return VALUE (DateTime)
193
+ * a DateTime, exactly as was specified in the input
194
+ *
195
+ * @example
196
+ * RDO_DATE_TIME_WITHOUT_ZONE("2012-09-22 04:36:12+10:00")
197
+ * #<DateTime: 2012-09-22T04:36:12+10:00 ((2456192j,66972s,0n),+36000s,2299161j)>
198
+ */
199
+ #define RDO_DATE_TIME_WITH_ZONE(s) \
200
+ (rb_funcall(rb_path2class("RDO::Util"), \
201
+ rb_intern("date_time_with_zone"), 1, rb_str_new2(s)))
202
+
203
+ /**
204
+ * Convert a boolean string to TrueClass/FalseClass.
205
+ *
206
+ * @param (char *) s
207
+ * a C string that is either 't', 'true', 'f' or 'false'
208
+ *
209
+ * @return VALUE (TrueClass, FalseClass)
210
+ * the boolean representation
211
+ */
212
+ #define RDO_BOOL(s) ((s[0] == 't') ? Qtrue : Qfalse)
@@ -0,0 +1,15 @@
1
+ /*
2
+ * RDO MySQL Driver.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ #include <ruby.h>
9
+ #include "driver.h"
10
+
11
+ /** Extension initializer, called when .so is loaded */
12
+ void Init_rdo_mysql(void) {
13
+ rb_require("rdo");
14
+ Init_rdo_mysql_driver();
15
+ }
@@ -0,0 +1,130 @@
1
+ /*
2
+ * RDO MySQL Driver.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ #include "tuples.h"
9
+ #include <stdio.h>
10
+ #include "macros.h"
11
+
12
+ /* I hate magic numbers */
13
+ #define RDO_MYSQL_BINARY_ENC 63
14
+
15
+ /** RDO::MySQL::TupleList class */
16
+ static VALUE rdo_mysql_cTupleList;
17
+
18
+ /** Struct wrapped by TupleList class */
19
+ typedef struct {
20
+ MYSQL_RES * res;
21
+ int encoding;
22
+ } RDOMySQLTupleList;
23
+
24
+ /** Free memory allocated to list during GC */
25
+ static void rdo_mysql_tuple_list_free(RDOMySQLTupleList * list) {
26
+ mysql_free_result(list->res);
27
+ free(list);
28
+ }
29
+
30
+ /** Constructor to create a new TupleList for the given result */
31
+ VALUE rdo_mysql_tuple_list_new(MYSQL_RES * res, int encoding) {
32
+ RDOMySQLTupleList * list = malloc(sizeof(RDOMySQLTupleList));
33
+ list->res = res;
34
+ list->encoding = encoding;
35
+
36
+ VALUE obj = Data_Wrap_Struct(rdo_mysql_cTupleList,
37
+ 0, rdo_mysql_tuple_list_free, list);
38
+
39
+ rb_obj_call_init(obj, 0, NULL);
40
+
41
+ return obj;
42
+ }
43
+
44
+ /** Cast the given value to a Ruby type */
45
+ static VALUE rdo_mysql_cast_value(char * v, unsigned long len, MYSQL_FIELD f, int enc) {
46
+ switch (f.type) {
47
+ case MYSQL_TYPE_NULL:
48
+ return Qnil;
49
+
50
+ case MYSQL_TYPE_TINY:
51
+ case MYSQL_TYPE_SHORT:
52
+ case MYSQL_TYPE_LONG:
53
+ case MYSQL_TYPE_INT24:
54
+ case MYSQL_TYPE_LONGLONG:
55
+ return RDO_FIXNUM(v);
56
+
57
+ case MYSQL_TYPE_STRING:
58
+ case MYSQL_TYPE_VAR_STRING:
59
+ case MYSQL_TYPE_BLOB:
60
+ if (f.charsetnr == RDO_MYSQL_BINARY_ENC)
61
+ return RDO_BINARY_STRING(v, len);
62
+ else
63
+ return RDO_STRING(v, len, enc);
64
+
65
+ case MYSQL_TYPE_DECIMAL:
66
+ case MYSQL_TYPE_NEWDECIMAL:
67
+ return RDO_DECIMAL(v);
68
+
69
+ case MYSQL_TYPE_FLOAT:
70
+ case MYSQL_TYPE_DOUBLE:
71
+ return RDO_FLOAT(v);
72
+
73
+ case MYSQL_TYPE_DATE:
74
+ return RDO_DATE(v);
75
+
76
+ case MYSQL_TYPE_TIMESTAMP:
77
+ case MYSQL_TYPE_DATETIME:
78
+ return RDO_DATE_TIME_WITHOUT_ZONE(v);
79
+
80
+ default:
81
+ return RDO_BINARY_STRING(v, len);
82
+ }
83
+ }
84
+
85
+ /** Iterate over all tuples in the list */
86
+ static VALUE rdo_mysql_tuple_list_each(VALUE self) {
87
+ RDOMySQLTupleList * list;
88
+ Data_Get_Struct(self, RDOMySQLTupleList, list);
89
+
90
+ if (!rb_block_given_p() || list->res == NULL) {
91
+ return self;
92
+ }
93
+
94
+ mysql_data_seek(list->res, 0);
95
+
96
+ unsigned int nfields = mysql_num_fields(list->res);
97
+ MYSQL_FIELD * fields = mysql_fetch_fields(list->res);
98
+ MYSQL_ROW row;
99
+
100
+ while ((row = mysql_fetch_row(list->res))) {
101
+ unsigned long * lengths = mysql_fetch_lengths(list->res);
102
+ VALUE hash = rb_hash_new();
103
+ unsigned int i = 0;
104
+
105
+ for (; i < nfields; ++i) {
106
+ rb_hash_aset(hash,
107
+ ID2SYM(rb_intern(fields[i].name)),
108
+ rdo_mysql_cast_value(row[i], lengths[i], fields[i], list->encoding));
109
+ }
110
+
111
+ rb_yield(hash);
112
+ }
113
+
114
+ return self;
115
+ }
116
+
117
+ /** Initializer for the TupleList class */
118
+ void Init_rdo_mysql_tuples(void) {
119
+ rb_require("rdo/mysql");
120
+
121
+ VALUE mMySQL = rb_path2class("RDO::MySQL");
122
+
123
+ rdo_mysql_cTupleList = rb_define_class_under(mMySQL,
124
+ "TupleList", rb_cObject);
125
+
126
+ rb_define_method(rdo_mysql_cTupleList,
127
+ "each", rdo_mysql_tuple_list_each, 0);
128
+
129
+ rb_include_module(rdo_mysql_cTupleList, rb_mEnumerable);
130
+ }
@@ -0,0 +1,15 @@
1
+ /*
2
+ * RDO MySQL Driver.
3
+ * Copyright © 2012 Chris Corbyn.
4
+ *
5
+ * See LICENSE file for details.
6
+ */
7
+
8
+ #include <ruby.h>
9
+ #include <mysql.h>
10
+
11
+ /** Constructor to return a new TupleList wrapping res */
12
+ VALUE rdo_mysql_tuple_list_new(MYSQL_RES * res, int encoding);
13
+
14
+ /** Called during extension initialization to create the TupleList class */
15
+ void Init_rdo_mysql_tuples(void);
@@ -0,0 +1 @@
1
+ require "rdo/mysql"
@@ -0,0 +1,15 @@
1
+ ##
2
+ # RDO MySQL driver.
3
+ # Copyright © 2012 Chris Corbyn.
4
+ #
5
+ # See LICENSE file for details.
6
+ ##
7
+
8
+ require "rdo"
9
+ require "rdo/mysql/version"
10
+ require "rdo/mysql/driver"
11
+
12
+ # c extension
13
+ require "rdo_mysql/rdo_mysql"
14
+
15
+ RDO::Connection.register_driver(:mysql, RDO::MySQL::Driver)
@@ -0,0 +1,57 @@
1
+ ##
2
+ # RDO MySQL driver.
3
+ # Copyright © 2012 Chris Corbyn.
4
+ #
5
+ # See LICENSE file for details.
6
+ ##
7
+
8
+ module RDO
9
+ module MySQL
10
+ # Principal RDO driver class for MySQL.
11
+ class Driver < RDO::Driver
12
+ # implementation defined by ext/rdo_mysql/driver.c
13
+
14
+ private
15
+
16
+ %w[host user password database].each do |key|
17
+ define_method(key) { options[key.intern].to_s }
18
+ end
19
+
20
+ def port
21
+ options[:port].to_i
22
+ end
23
+
24
+ def after_open
25
+ set_time_zone
26
+ set_encoding
27
+ end
28
+
29
+ def set_time_zone
30
+ execute(
31
+ "SET time_zone = ?",
32
+ options.fetch(:time_zone, RDO::Util.system_time_zone)
33
+ )
34
+ end
35
+
36
+ def set_encoding
37
+ execute("SET NAMES ?", charset_name(encoding))
38
+ end
39
+
40
+ def encoding
41
+ options.fetch(:encoding, 'utf8')
42
+ end
43
+
44
+ # mysql uses a restrictive syntax for setting the encoding
45
+ def charset_name(name)
46
+ case name.to_s.downcase
47
+ when "utf-8"
48
+ "utf8"
49
+ when /^iso-8859-[0-9]+$/
50
+ name.to_s.gsub(/^.*?-([0-9]+)$/, "latin\\1")
51
+ else
52
+ name
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,12 @@
1
+ ##
2
+ # RDO MySQL driver.
3
+ # Copyright © 2012 Chris Corbyn.
4
+ #
5
+ # See LICENSE file for details.
6
+ ##
7
+
8
+ module RDO
9
+ module MySQL
10
+ VERSION = "0.0.1"
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rdo/mysql/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["d11wtq"]
6
+ gem.email = ["chris@w3style.co.uk"]
7
+ gem.description = "Provides access to MySQL using the RDO interface"
8
+ gem.summary = "MySQL Driver for RDO"
9
+ gem.homepage = "https://github.com/d11wtq/rdo-mysql"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "rdo-mysql"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = RDO::MySQL::VERSION
17
+ gem.extensions = ["ext/rdo_mysql/extconf.rb"]
18
+
19
+ gem.add_runtime_dependency "rdo", ">= 0.0.8"
20
+
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "rake-compiler"
23
+ end
@@ -0,0 +1,155 @@
1
+ require "spec_helper"
2
+ require "uri"
3
+
4
+ describe RDO::MySQL::Driver do
5
+ let(:options) { connection_uri }
6
+ let(:connection) { RDO::Connection.new(options) }
7
+
8
+ after(:each) { connection.close rescue nil }
9
+
10
+ describe "#initialize" do
11
+ context "with valid settings" do
12
+ it "opens a connection to the server" do
13
+ connection.should be_open
14
+ end
15
+ end
16
+
17
+ context "with invalid settings" do
18
+ let(:options) { URI.parse(connection_uri).tap{|u| u.user = "bad_user"}.to_s }
19
+
20
+ it "raises an RDO::Exception" do
21
+ expect { connection }.to raise_error(RDO::Exception)
22
+ end
23
+
24
+ it "provides a meaningful error" do
25
+ begin
26
+ connection && fail("RDO::Exception should be raised")
27
+ rescue RDO::Exception => e
28
+ e.message.should =~ /mysql.*?bad_user/i
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#close" do
35
+ it "closes the connection to the server" do
36
+ connection.close
37
+ connection.should_not be_open
38
+ end
39
+
40
+ it "returns true" do
41
+ connection.close.should == true
42
+ end
43
+
44
+ context "called multiple times" do
45
+ it "has no negative side-effects" do
46
+ 5.times { connection.close }
47
+ connection.should_not be_open
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#open" do
53
+ it "opens a connection to the server" do
54
+ connection.close && connection.open
55
+ connection.should be_open
56
+ end
57
+
58
+ it "returns true" do
59
+ connection.close
60
+ connection.open.should == true
61
+ end
62
+
63
+ context "called multiple times" do
64
+ before(:each) { connection.close }
65
+
66
+ it "has no negative side-effects" do
67
+ 5.times { connection.open }
68
+ connection.should be_open
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#quote" do
74
+ it "escapes values for safe insertion into a query" do
75
+ connection.quote("that's life!").should == "that\\'s life!"
76
+ end
77
+ end
78
+
79
+ describe "#execute" do
80
+ before(:each) do
81
+ connection.execute("DROP TABLE IF EXISTS test")
82
+ connection.execute <<-SQL
83
+ CREATE TABLE test (
84
+ id INT PRIMARY KEY AUTO_INCREMENT,
85
+ name VARCHAR(255),
86
+ age INT
87
+ ) ENGINE=InnoDB CHARSET=utf8
88
+ SQL
89
+ end
90
+
91
+ after(:each) do
92
+ connection.execute("DROP TABLE IF EXISTS test")
93
+ end
94
+
95
+ context "with an insert" do
96
+ let(:result) do
97
+ connection.execute("INSERT INTO test (name) VALUES (?)", "jimmy")
98
+ end
99
+
100
+ it "returns a RDO::Result" do
101
+ result.should be_a_kind_of(RDO::Result)
102
+ end
103
+
104
+ it "provides the #insert_id" do
105
+ result.insert_id.should == 1
106
+ end
107
+ end
108
+
109
+ context "with a select" do
110
+ before(:each) do
111
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "jimmy", 22)
112
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "harry", 28)
113
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "kat", 31)
114
+ end
115
+
116
+ let(:result) do
117
+ connection.execute("SELECT * FROM test WHERE age > ?", 25)
118
+ end
119
+
120
+ it "returns a RDO::Result" do
121
+ result.should be_a_kind_of(RDO::Result)
122
+ end
123
+
124
+ it "provides the #count" do
125
+ result.count.should == 2
126
+ end
127
+
128
+ it "allows enumeration of the rows" do
129
+ rows = []
130
+ result.each {|row| rows << row}
131
+ rows.should == [{id: 2, name: "harry", age: 28}, {id: 3, name: "kat", age: 31}]
132
+ end
133
+ end
134
+
135
+ context "with an update" do
136
+ before(:each) do
137
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "jimmy", 22)
138
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "harry", 28)
139
+ connection.execute("INSERT INTO test (name, age) VALUES (?, ?)", "kat", 31)
140
+ end
141
+
142
+ let(:result) do
143
+ connection.execute("UPDATE test SET age = age + 3 WHERE age > ?", 25)
144
+ end
145
+
146
+ it "returns a RDO::Result" do
147
+ result.should be_a_kind_of(RDO::Result)
148
+ end
149
+
150
+ it "provides the #affected_rows" do
151
+ result.affected_rows.should == 2
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,212 @@
1
+ require "spec_helper"
2
+ require "bigdecimal"
3
+ require "date"
4
+ require "uri"
5
+
6
+ describe RDO::MySQL::Driver, "type casting" do
7
+ let(:options) { connection_uri }
8
+ let(:connection) { RDO.connect(options) }
9
+ let(:value) { connection.execute(sql).first_value }
10
+
11
+ describe "null cast" do
12
+ let(:sql) { "SELECT NULL" }
13
+
14
+ it "returns nil" do
15
+ value.should be_nil
16
+ end
17
+ end
18
+
19
+ describe "varchar cast" do
20
+ before(:each) do
21
+ connection.execute("CREATE TEMPORARY TABLE test (v VARCHAR(16))")
22
+ connection.execute("INSERT INTO test (v) VALUES ('bob')")
23
+ end
24
+
25
+ let(:sql) { "SELECT v FROM test" }
26
+
27
+ it "returns a String" do
28
+ value.should == "bob"
29
+ end
30
+
31
+ context "with utf-8 encoding" do
32
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=utf-8"}.to_s }
33
+
34
+ it "has the correct encoding" do
35
+ value.encoding.should == Encoding.find("utf-8")
36
+ end
37
+ end
38
+
39
+ context "with iso-8859-1 encoding" do
40
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=iso-8859-1"}.to_s }
41
+
42
+ it "has the correct encoding" do
43
+ value.encoding.should == Encoding.find("iso-8859-1")
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "char cast" do
49
+ before(:each) do
50
+ connection.execute("CREATE TEMPORARY TABLE test (v CHAR(4))")
51
+ connection.execute("INSERT INTO test (v) VALUES ('dave')")
52
+ end
53
+
54
+ let(:sql) { "SELECT v FROM test" }
55
+
56
+ it "returns a String" do
57
+ value.should == "dave"
58
+ end
59
+
60
+ context "with utf-8 encoding" do
61
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=utf-8"}.to_s }
62
+
63
+ it "has the correct encoding" do
64
+ value.encoding.should == Encoding.find("utf-8")
65
+ end
66
+ end
67
+
68
+ context "with iso-8859-1 encoding" do
69
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=iso-8859-1"}.to_s }
70
+
71
+ it "has the correct encoding" do
72
+ value.encoding.should == Encoding.find("iso-8859-1")
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "text cast" do
78
+ before(:each) do
79
+ connection.execute("CREATE TEMPORARY TABLE test (v TEXT)")
80
+ connection.execute("INSERT INTO test (v) VALUES ('bobby')")
81
+ end
82
+
83
+ let(:sql) { "SELECT v FROM test" }
84
+
85
+ it "returns a String" do
86
+ value.should == "bobby"
87
+ end
88
+
89
+ context "with utf-8 encoding" do
90
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=utf-8"}.to_s }
91
+
92
+ it "has the correct encoding" do
93
+ value.encoding.should == Encoding.find("utf-8")
94
+ end
95
+ end
96
+
97
+ context "with iso-8859-1 encoding" do
98
+ let(:options) { URI.parse(connection_uri).tap{|u| u.query = "encoding=iso-8859-1"}.to_s }
99
+
100
+ it "has the correct encoding" do
101
+ value.encoding.should == Encoding.find("iso-8859-1")
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "binary cast" do
107
+ before(:each) do
108
+ connection.execute("CREATE TEMPORARY TABLE test (v BINARY(3))")
109
+ connection.execute("INSERT INTO test (v) VALUES (?)", "\x00\x11\x22")
110
+ end
111
+
112
+ let(:sql) { "SELECT v FROM test" }
113
+
114
+ it "returns a String" do
115
+ value.should == "\x00\x11\x22"
116
+ end
117
+
118
+ it "has binary encoding" do
119
+ value.encoding.should == Encoding.find("binary")
120
+ end
121
+ end
122
+
123
+ describe "blob cast" do
124
+ before(:each) do
125
+ connection.execute("CREATE TEMPORARY TABLE test (v BLOB)")
126
+ connection.execute("INSERT INTO test (v) VALUES (?)", "\x00\x11\x22\x33\x44")
127
+ end
128
+
129
+ let(:sql) { "SELECT v FROM test" }
130
+
131
+ it "returns a String" do
132
+ value.should == "\x00\x11\x22\x33\x44"
133
+ end
134
+
135
+ it "has binary encoding" do
136
+ value.encoding.should == Encoding.find("binary")
137
+ end
138
+ end
139
+
140
+ describe "integer cast" do
141
+ let(:sql) { "SELECT 1234" }
142
+
143
+ it "returns a Fixnum" do
144
+ value.should == 1234
145
+ end
146
+ end
147
+
148
+ describe "float cast" do
149
+ before(:each) do
150
+ connection.execute("CREATE TEMPORARY TABLE test (n FLOAT)")
151
+ connection.execute("INSERT INTO test (n) VALUES (12.34)")
152
+ end
153
+
154
+ let(:sql) { "SELECT n FROM test" }
155
+
156
+ it "returns a Float" do
157
+ value.should == 12.34
158
+ end
159
+ end
160
+
161
+ describe "decimal cast" do
162
+ before(:each) do
163
+ connection.execute("CREATE TEMPORARY TABLE test (n DECIMAL(6,2))")
164
+ connection.execute("INSERT INTO test (n) VALUES ('1234.56')")
165
+ end
166
+
167
+ let(:sql) { "SELECT n FROM test" }
168
+
169
+ it "returns a BigDecimal" do
170
+ value.should == BigDecimal("1234.56")
171
+ end
172
+ end
173
+
174
+ describe "date cast" do
175
+ before(:each) do
176
+ connection.execute("CREATE TEMPORARY TABLE test (d DATE)")
177
+ connection.execute("INSERT INTO test (d) VALUES ('2012-09-30')")
178
+ end
179
+
180
+ let(:sql) { "SELECT d FROM test" }
181
+
182
+ it "returns a Date" do
183
+ value.should == Date.new(2012, 9, 30)
184
+ end
185
+ end
186
+
187
+ describe "datetime cast" do
188
+ before(:each) do
189
+ connection.execute("CREATE TEMPORARY TABLE test (d DATETIME)")
190
+ connection.execute("INSERT INTO test (d) VALUES ('2012-09-30 19:04:36')")
191
+ end
192
+
193
+ let(:sql) { "SELECT d FROM test" }
194
+
195
+ it "returns a DateTime in the system time zone" do
196
+ value.should == DateTime.new(2012, 9, 30, 19, 4, 36, DateTime.now.zone)
197
+ end
198
+ end
199
+
200
+ describe "timestamp cast" do
201
+ before(:each) do
202
+ connection.execute("CREATE TEMPORARY TABLE test (d TIMESTAMP)")
203
+ connection.execute("INSERT INTO test (d) VALUES ('2012-09-30 19:04:36')")
204
+ end
205
+
206
+ let(:sql) { "SELECT d FROM test" }
207
+
208
+ it "returns a DateTime in the system time zone" do
209
+ value.should == DateTime.new(2012, 9, 30, 19, 4, 36, DateTime.now.zone)
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,10 @@
1
+ require "rdo"
2
+ require "rdo/mysql"
3
+
4
+ ENV["CONNECTION"] ||= "mysql://rdo:rdo@localhost/rdo?encoding=utf-8"
5
+
6
+ RSpec.configure do |config|
7
+ def connection_uri
8
+ ENV["CONNECTION"]
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdo-mysql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - d11wtq
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdo
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.8
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake-compiler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Provides access to MySQL using the RDO interface
63
+ email:
64
+ - chris@w3style.co.uk
65
+ executables: []
66
+ extensions:
67
+ - ext/rdo_mysql/extconf.rb
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - .rspec
72
+ - .travis.yml
73
+ - Gemfile
74
+ - LICENSE
75
+ - README.md
76
+ - Rakefile
77
+ - ext/rdo_mysql/driver.c
78
+ - ext/rdo_mysql/driver.h
79
+ - ext/rdo_mysql/extconf.rb
80
+ - ext/rdo_mysql/macros.h
81
+ - ext/rdo_mysql/rdo_mysql.c
82
+ - ext/rdo_mysql/tuples.c
83
+ - ext/rdo_mysql/tuples.h
84
+ - lib/rdo-mysql.rb
85
+ - lib/rdo/mysql.rb
86
+ - lib/rdo/mysql/driver.rb
87
+ - lib/rdo/mysql/version.rb
88
+ - rdo-mysql.gemspec
89
+ - spec/mysql/driver_spec.rb
90
+ - spec/mysql/type_cast_spec.rb
91
+ - spec/spec_helper.rb
92
+ homepage: https://github.com/d11wtq/rdo-mysql
93
+ licenses: []
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 1.8.24
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: MySQL Driver for RDO
116
+ test_files:
117
+ - spec/mysql/driver_spec.rb
118
+ - spec/mysql/type_cast_spec.rb
119
+ - spec/spec_helper.rb