sam / do fork watch download tarball
public
Rubygem
Description: DataObjects
Homepage: http://rubyforge.org/projects/dorb
Clone URL: git://github.com/sam/do.git
Revert "Added #set_types typecasting support", this wasn't supposed to 
happen in master

This reverts commit 4d4d94a2534a4098f4ef3e23b06c2015be8c8dbb.
Scott Bauer (author)
Thu Feb 21 10:50:17 -0800 2008
commit  d18a513e5756c5599f77d10094d9c22b4697a480
tree    5019e29f96019e8945ecbb944a901abfbed075fb
parent  4d4d94a2534a4098f4ef3e23b06c2015be8c8dbb
...
3
4
5
6
7
8
9
10
...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 
 
97
98
99
...
116
117
118
119
 
 
120
121
122
...
124
125
126
127
 
 
128
129
130
...
140
141
142
143
144
145
146
...
152
153
154
155
 
 
156
157
158
...
166
167
168
 
 
169
170
171
172
173
174
 
 
175
176
177
178
179
180
181
182
 
 
 
 
 
183
184
185
186
187
 
 
 
 
188
189
190
191
 
 
 
 
192
193
194
195
196
197
198
199
200
 
 
201
202
203
204
205
206
207
208
209
210
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
213
214
215
216
217
218
219
220
221
222
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
225
226
227
228
 
 
229
230
231
232
233
234
 
 
 
 
 
 
235
236
 
237
238
239
240
 
241
242
243
244
245
246
247
248
 
 
 
 
 
 
 
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
 
 
265
266
267
268
269
270
271
272
 
 
 
 
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
 
292
293
294
295
 
 
 
 
 
 
 
296
297
...
3
4
5
 
 
6
7
8
...
10
11
12
 
 
 
13
14
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
17
18
19
20
...
37
38
39
 
40
41
42
43
44
...
46
47
48
 
49
50
51
52
53
...
63
64
65
 
66
67
68
...
74
75
76
 
77
78
79
80
81
...
89
90
91
92
93
94
 
95
96
97
 
98
99
100
101
102
103
104
 
 
105
106
107
108
109
110
111
112
 
 
 
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
 
 
 
 
130
131
132
133
 
 
 
134
135
136
137
 
 
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
 
 
 
 
 
 
 
 
 
 
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
 
 
 
 
225
226
227
228
 
229
 
 
230
231
232
233
234
235
236
 
237
238
239
 
 
240
241
242
 
 
 
 
 
 
243
244
245
246
247
248
249
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
252
253
254
 
255
 
 
 
 
256
257
258
259
260
261
262
 
263
264
265
266
267
268
 
269
 
270
 
271
272
273
 
274
275
276
277
 
278
279
280
281
282
283
284
285
286
0
@@ -3,8 +3,6 @@
0
 #include <errmsg.h>
0
 #include <mysqld_error.h>
0
 
0
-#define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
0
-#define CHAR_TO_STRING(name) rb_str_new2(name)
0
 #define ID_TO_I rb_intern("to_i")
0
 #define ID_TO_F rb_intern("to_f")
0
 #define ID_PARSE rb_intern("parse")
0
@@ -12,88 +10,11 @@
0
 #define ID_NEW rb_intern("new")
0
 #define ID_CONST_GET rb_intern("const_get")
0
 
0
-static VALUE rb_cDate;
0
-static VALUE rb_cDateTime;
0
-
0
 VALUE mRbMysql;
0
 VALUE cConnection;
0
 VALUE cResult;
0
-
0
-// Figures out what we should cast a given mysql field type to
0
-VALUE ruby_type_from_mysql_type(MYSQL_FIELD *field) {
0
-
0
- char* ruby_type_name;
0
-
0
- switch(field->type) {
0
- case MYSQL_TYPE_NULL: {
0
- ruby_type_name = NULL;
0
- break;
0
- }
0
- case MYSQL_TYPE_TINY: {
0
- ruby_type_name = "TrueClass";
0
- break;
0
- }
0
- case MYSQL_TYPE_BIT:
0
- case MYSQL_TYPE_SHORT:
0
- case MYSQL_TYPE_LONG:
0
- case MYSQL_TYPE_INT24:
0
- case MYSQL_TYPE_LONGLONG: {
0
- ruby_type_name = "FixNum";
0
- break;
0
- }
0
- case MYSQL_TYPE_DECIMAL:
0
- case MYSQL_TYPE_NEWDECIMAL:
0
- case MYSQL_TYPE_FLOAT:
0
- case MYSQL_TYPE_DOUBLE:
0
- case MYSQL_TYPE_YEAR: {
0
- ruby_type_name = "Float"; break;
0
- }
0
- case MYSQL_TYPE_TIMESTAMP:
0
- case MYSQL_TYPE_DATETIME: {
0
- ruby_type_name = "DateTime";
0
- break;
0
- }
0
- case MYSQL_TYPE_TIME: {
0
- ruby_type_name = "DateTime";
0
- break;
0
- }
0
- case MYSQL_TYPE_DATE: {
0
- ruby_type_name = "Date";
0
- break;
0
- }
0
- default: {
0
- ruby_type_name = "String";
0
- }
0
- }
0
-
0
- return CHAR_TO_STRING(ruby_type_name);
0
-}
0
-
0
-// Convert C-string to a Ruby instance of type "ruby_class_name"
0
-VALUE cast_mysql_value_to_ruby_value(const char* data, char* ruby_class_name) {
0
- if (NULL == data)
0
- return Qnil;
0
-
0
- VALUE ruby_value = Qnil;
0
-
0
- if (0 == strcmp("Fixnum", ruby_class_name) || 0 == strcmp("Bignum", ruby_class_name)) {
0
- ruby_value = (0 == strlen(data) ? Qnil : LL2NUM(atoi(data)));
0
- } else if (0 == strcmp("String", ruby_class_name)) {
0
- ruby_value = CHAR_TO_STRING(data);
0
- } else if (0 == strcmp("Float", ruby_class_name) ) {
0
- ruby_value = rb_float_new(strtod(data, NULL));
0
- } else if (0 == strcmp("TrueClass", ruby_class_name) || 0 == strcmp("FalseClass", ruby_class_name)) {
0
- ruby_value = !(NULL == data || 0 == data || 0 == strcmp("0", data));
0
- } else if (0 == strcmp("Date", ruby_class_name)) {
0
- ruby_value = rb_funcall(rb_cDate, ID_PARSE, 1, CHAR_TO_STRING(data));
0
- } else if (0 == strcmp("DateTime", ruby_class_name)) {
0
- ruby_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, CHAR_TO_STRING(data));
0
- } else {
0
- ruby_value = CHAR_TO_STRING(data);
0
- }
0
-
0
- return ruby_value;
0
-}
0
+VALUE rb_cDate;
0
+VALUE rb_cDateTime;
0
 
0
 VALUE cConnection_initialize(VALUE self, VALUE host, VALUE user, VALUE password, VALUE database, VALUE port, VALUE unix_socket, VALUE client_flag) {
0
   MYSQL *db = 0 ;
0
@@ -116,7 +37,8 @@ VALUE cConnection_initialize(VALUE self, VALUE host, VALUE user, VALUE password,
0
 }
0
 
0
 VALUE cConnection_last_error(VALUE self) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ MYSQL *db;
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
   
0
   char *error_message = (char *)mysql_error(db);
0
   
0
@@ -124,7 +46,8 @@ VALUE cConnection_last_error(VALUE self) {
0
 }
0
 
0
 VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ MYSQL *db;
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
   
0
   int query_result = 0;
0
   MYSQL_RES *response = 0;
0
@@ -140,7 +63,6 @@ VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
     return Qnil;
0
   
0
   reader = rb_funcall(cResult, ID_NEW, 0);
0
- rb_iv_set(reader, "@connection", self);
0
   rb_iv_set(reader, "@affected_rows", INT2NUM(affected_rows));
0
   rb_iv_set(reader, "@reader", Qnil);
0
   
0
@@ -152,7 +74,8 @@ VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
 }
0
 
0
 VALUE cConnection_execute_reader(VALUE self, VALUE query) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ MYSQL *db;
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
   
0
   int query_result = 0;
0
   MYSQL_RES *response = 0;
0
@@ -166,131 +89,197 @@ VALUE cConnection_execute_reader(VALUE self, VALUE query) {
0
     return Qnil;
0
   }
0
 
0
+ // Create a reader and populate the affected rows and field count
0
+ // reader = Data_Wrap_Struct(cResult, 0, mysql_free_result, response);
0
   result = rb_funcall(cResult, ID_NEW, 0);
0
- rb_iv_set(result, "@connection", self);
0
   rb_iv_set(result, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
0
 
0
   int field_count = (int)mysql_field_count(db);
0
-
0
+
0
+ // rb_iv_set(result, "@affected_rows", INT2NUM(mysql_affected_rows(db)));
0
   rb_iv_set(result, "@affected_rows", Qnil);
0
   rb_iv_set(result, "@field_count", INT2NUM(field_count));
0
 
0
   VALUE field_names = rb_ary_new();
0
   VALUE field_types = rb_ary_new();
0
-
0
- MYSQL_FIELD *field;
0
 
0
+ // Allocate an array of pointers to MYSQL_FIELD structs so
0
+ // we can typecast later without having to use
0
+ MYSQL_FIELD **field_ptr;
0
+ field_ptr = malloc(field_count * sizeof(MYSQL_FIELD *));
0
+
0
   int i;
0
   for(i = 0; i < field_count; i++) {
0
- field = mysql_fetch_field_direct(response, i);;
0
- rb_ary_push(field_names, rb_str_new2(field->name));
0
- rb_ary_push(field_types, ruby_type_from_mysql_type(field));
0
+ field_ptr[i] = mysql_fetch_field_direct(response, i);;
0
+
0
+ rb_ary_push(field_names, rb_str_new2(field_ptr[i]->name));
0
+ rb_ary_push(field_types, INT2NUM(field_ptr[i]->type));
0
   }
0
 
0
   rb_iv_set(result, "@field_names", field_names);
0
   rb_iv_set(result, "@field_types", field_types);
0
+
0
+ // So we can typecast in fetch_row
0
+ VALUE native_field_types = Data_Wrap_Struct(rb_cObject, 0, free, field_ptr);
0
+ rb_iv_set(result, "@native_field_types", native_field_types);
0
 
0
   return result;
0
 }
0
 
0
 VALUE cConnection_close(VALUE self) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
-
0
- if (NULL == db)
0
- return Qfalse;
0
+ MYSQL *db;
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
   
0
   mysql_close(db);
0
- free(db);
0
-
0
- rb_iv_set(self, "@connection", Qnil);
0
 
0
   return Qtrue;
0
 }
0
 
0
-VALUE cConnection_is_opened(VALUE self) {
0
- return Qnil == rb_iv_get(self, "@connection") ? Qfalse : Qtrue;
0
+VALUE cResult_close(VALUE self) {
0
+ VALUE reader_value = rb_iv_get(self, "@reader");
0
+
0
+ if (Qnil != reader_value) {
0
+ MYSQL_RES *reader;
0
+ Data_Get_Struct(reader_value, MYSQL_RES, reader);
0
+
0
+ mysql_free_result(reader);
0
+
0
+ rb_iv_set(self, "@reader", Qnil);
0
+
0
+ return Qtrue;
0
+ } else {
0
+ return Qfalse;
0
+ }
0
 }
0
 
0
-// Accepts an array of Ruby types (Fixnum, Float, String, etc...) and turns them
0
-// into Ruby-strings so we can easily typecast later
0
-VALUE cResult_set_types(VALUE self, VALUE array) {
0
-
0
- int i;
0
-
0
- VALUE type_strings = rb_ary_new();
0
-
0
- for (i = 0; i < RARRAY(array)->len; i++) {
0
- rb_ary_push(type_strings, rb_str_new2(rb_class2name(rb_ary_entry(array, i))));
0
+VALUE typecast(const char* data, MYSQL_FIELD * field) {
0
+
0
+ // when "NULL"
0
+ // nil
0
+ // when "TINY"
0
+ // val != "0"
0
+ // when "BIT"
0
+ // val.to_i(2)
0
+ // when "SHORT", "LONG", "INT24", "LONGLONG"
0
+ // val == '' ? nil : val.to_i
0
+ // when "DECIMAL", "NEWDECIMAL", "FLOAT", "DOUBLE", "YEAR"
0
+ // val.to_f
0
+ // when "TIMESTAMP", "DATETIME"
0
+ // DateTime.parse(val) rescue nil
0
+ // when "TIME"
0
+ // DateTime.parse(val).to_time rescue nil
0
+ // when "DATE"
0
+ // Date.parse(val) rescue nil
0
+ // else
0
+ // val
0
+
0
+ // TODO: This is hacky and slow, we shouldn't be casting to a Ruby string
0
+ // just because its easy...
0
+ VALUE ruby_value = rb_str_new2(data);
0
+
0
+ switch(field->type) {
0
+ case MYSQL_TYPE_NULL: {
0
+ ruby_value = Qnil;
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_TINY: {
0
+ ruby_value = (0 == strcmp("0", data) ? Qfalse : Qtrue);
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_BIT: {
0
+ ruby_value = rb_funcall(ruby_value, ID_TO_I, 1, 2);
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_SHORT:
0
+ case MYSQL_TYPE_LONG:
0
+ case MYSQL_TYPE_INT24:
0
+ case MYSQL_TYPE_LONGLONG: {
0
+ ruby_value = "" == data ? Qnil : rb_funcall(ruby_value, ID_TO_I, 0);
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_DECIMAL:
0
+ case MYSQL_TYPE_NEWDECIMAL:
0
+ case MYSQL_TYPE_FLOAT:
0
+ case MYSQL_TYPE_DOUBLE:
0
+ case MYSQL_TYPE_YEAR: {
0
+ ruby_value = rb_funcall(ruby_value, ID_TO_F, 0); break;
0
+ }
0
+ case MYSQL_TYPE_TIMESTAMP:
0
+ case MYSQL_TYPE_DATETIME: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ ruby_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, ruby_value);
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_TIME: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ VALUE dt_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, ruby_value);
0
+ ruby_value = rb_funcall(dt_value, ID_TO_TIME, 0);
0
+ break;
0
+ }
0
+ case MYSQL_TYPE_DATE: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ ruby_value = rb_funcall(rb_cDate, ID_PARSE, 1, ruby_value);
0
+ break;
0
+ }
0
   }
0
-
0
- rb_iv_set(self, "@field_types", type_strings);
0
-
0
- return array;
0
+
0
+ return ruby_value;
0
 }
0
 
0
-// Retrieve a single row
0
 VALUE cResult_fetch_row(VALUE self) {
0
- MYSQL_RES *reader = DATA_PTR(rb_iv_get(self, "@reader"));
0
- VALUE ruby_field_type_strings = rb_iv_get(self, "@field_types");
0
+ MYSQL_RES *reader;
0
+ Data_Get_Struct(rb_iv_get(self, "@reader"), MYSQL_RES, reader);
0
+
0
+ // There's got to be a better/faster way to do this.
0
+ MYSQL_FIELD **field_ptr;
0
+ Data_Get_Struct(rb_iv_get(self, "@native_field_types"), MYSQL_FIELD *, field_ptr);
0
 
0
- VALUE row = rb_ary_new();
0
+ VALUE arr = rb_ary_new();
0
   MYSQL_ROW result = (MYSQL_ROW)mysql_fetch_row(reader);
0
 
0
- // TODO: there's probably something more specific we need to do here.
0
- if (!result)
0
+ if (!result)
0
     return Qnil;
0
 
0
- int i;
0
-
0
- for (i = 0; i < reader->field_count; i++) {
0
- // The field_type data could be cached
0
- char* field_type = RSTRING(rb_ary_entry(ruby_field_type_strings, i))->ptr;
0
- rb_ary_push(row, cast_mysql_value_to_ruby_value(result[i], field_type));
0
+ int i;
0
+ for (i = 0; i < reader->field_count; i++) {
0
+ if (result[i] == NULL) {
0
+ rb_ary_push(arr, Qnil);
0
+ } else {
0
+ rb_ary_push(arr, typecast(result[i], field_ptr[i]));
0
+ }
0
   }
0
-
0
- return row;
0
-}
0
-
0
-// This should be called to ensure that the internal result reader is freed
0
-VALUE cResult_close(VALUE self) {
0
- MYSQL_RES *reader = DATA_PTR(rb_iv_get(self, "@reader"));
0
-
0
- if (NULL == reader)
0
- return Qfalse;
0
-
0
- mysql_free_result(reader);
0
- rb_iv_set(self, "@reader", Qnil);
0
-
0
- return Qtrue;
0
+
0
+ return arr;
0
 }
0
 
0
-
0
 void Init_rbmysql() {
0
- // Store references to a few helpful clases that aren't in Ruby Core
0
- rb_cDate = RUBY_CLASS("Date");
0
- rb_cDateTime = RUBY_CLASS("DateTime");
0
-
0
+ // Get references to Date and DateTime
0
+ rb_cDate = rb_funcall(rb_mKernel, ID_CONST_GET, 1, rb_str_new2("Date"));
0
+ rb_cDateTime = rb_funcall(rb_mKernel, ID_CONST_GET, 1, rb_str_new2("DateTime"));
0
+
0
   // Top Level Module that all the classes live under
0
   mRbMysql = rb_define_module("RbMysql");
0
   
0
- // Build the RBMysql::Connection Class
0
   cConnection = rb_define_class_under(mRbMysql, "Connection", rb_cObject);
0
   rb_define_method(cConnection, "initialize", cConnection_initialize, 7);
0
   rb_define_method(cConnection, "execute_reader", cConnection_execute_reader, 1);
0
   rb_define_method(cConnection, "execute_non_query", cConnection_execute_non_query, 1);
0
   rb_define_method(cConnection, "last_error", cConnection_last_error, 0);
0
   rb_define_method(cConnection, "close", cConnection_close, 0);
0
- rb_define_method(cConnection, "opened?", cConnection_is_opened, 0);
0
 
0
- // Build the RBMysql::Result Class
0
   cResult = rb_define_class_under(mRbMysql, "Result", rb_cObject);
0
- rb_define_attr(cResult, "connection", 1, 0);
0
   rb_define_attr(cResult, "affected_rows", 1, 0);
0
   rb_define_attr(cResult, "field_count", 1, 0);
0
   rb_define_attr(cResult, "field_names", 1, 0);
0
- rb_define_attr(cResult, "field_types", 1, 1);
0
+ rb_define_attr(cResult, "field_types", 1, 0);
0
   rb_define_attr(cResult, "inserted_id", 1, 0);
0
   rb_define_method(cResult, "fetch_row", cResult_fetch_row, 0);
0
   rb_define_method(cResult, "close", cResult_close, 0);
0
- rb_define_method(cResult, "set_types", cResult_set_types, 1);
0
+
0
+ // rb_define_singleton_method(RbMysql, "mysql_port", mysql_port_get, 0);
0
+ // rb_define_singleton_method(RbMysql, "mysql_port=", mysql_port_set, 1);
0
+ // rb_define_singleton_method(RbMysql, "mysql_unix_port", mysql_unix_port_get, 0);
0
+ // rb_define_singleton_method(RbMysql, "mysql_unix_port=", mysql_unix_port_set, 1);
0
+ // rb_define_const(RbMysql, "CLIENT_NET_READ_TIMEOUT", INT2NUM(365*24*3600));
0
+ // rb_define_const(RbMysql, "CLIENT_NET_WRITE_TIMEOUT", INT2NUM(365*24*3600));
0
 }
0
\ No newline at end of file
...
64
65
66
67
68
69
70
71
72
73
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
167
168
169
170
 
171
172
173
...
64
65
66
 
 
 
 
67
68
69
...
109
110
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
113
114
...
138
139
140
 
141
142
143
144
0
@@ -64,10 +64,6 @@ describe "RbMysql" do
0
       @result.should respond_to(:close)
0
     end
0
 
0
- it "should expose a #set_types method" do
0
- @result.should respond_to(:set_types)
0
- end
0
-
0
   end
0
     
0
 end
0
@@ -113,31 +109,6 @@ describe "A new connection" do
0
     
0
     # HACK: this is a weak test...
0
     it "should typecast all fields to the proper Ruby type" do
0
-
0
- @result.set_types [
0
- Fixnum,
0
- String,
0
- String,
0
- String,
0
- String,
0
- String,
0
- String,
0
- String,
0
- String,
0
- String,
0
- FalseClass,
0
- Fixnum,
0
- Fixnum,
0
- Bignum,
0
- Float,
0
- Float,
0
- Float,
0
- Date,
0
- DateTime,
0
- DateTime,
0
- String
0
- ]
0
-
0
       row = @result.fetch_row
0
 
0
       types = [
0
@@ -167,7 +138,7 @@ describe "A new connection" do
0
       types.each_with_index do |t, idx|
0
         # puts row[idx].class
0
         # puts "Field #{idx} - #{@result.field_names[idx]}/#{@result.field_types[idx]}: #{row[idx].inspect}"
0
- row[idx].class.should == types[idx]
0
+ row[idx].should be_kind_of(types[idx])
0
       end
0
     end
0
     

Comments

    No one has commented yet.