rcsv 0.0.9 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.travis.yml +2 -1
- data/Gemfile.lock +1 -1
- data/README.md +8 -7
- data/RELNOTES +9 -0
- data/ext/rcsv/rcsv.c +208 -110
- data/lib/rcsv.rb +28 -5
- data/lib/rcsv/version.rb +1 -1
- data/test/test_rcsv.csv +1 -1
- data/test/test_rcsv_parse.rb +79 -0
- data/test/test_rcsv_raw_parse.rb +69 -61
- data/test/test_rcsv_write.rb +1 -0
- metadata +7 -8
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjlmMDhlZWFkNzk3ZmJkYjc1Zjk4ZjFmM2FjYjc5YTVmNzZhOWYyOA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZjFkZDVmMDc0ZmJkODg2YzMzYzU2MjViODVkNjNhZDJhMjcxZWI0ZQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZmZjZjAzY2VlNjU4MTM0ZDBmZWQwY2NiNTZmNWY4NzBjMzdlNTBhMjJhZjQz
|
10
|
+
ODEzNDc3MjM5ZGJjNjE5MTgxMTQ3MjU3ZTU3YmQwZmQwNWZmNWFkMzlkNzZl
|
11
|
+
ZWExNTVmODFjOGJkNGIwNmFlMDI5MDA5OWY1ODBiNjhiMWNlZmU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjZhOWIwZjI0OGMwOTg3NjY1MzgxMjMwYTU4NmFkMzQ3MGU2MWZmNTFjY2Q4
|
14
|
+
MGU5MzM3M2EwOWEwNjVkMzllYzgzM2U2OTQwYTA5ZWZiMDVkMjBjZDJjYTIy
|
15
|
+
NzJiYTY1MzYyM2I0MzBkYjc3OTRkYmQwNjMwZDUyNGJkZmI1MWU=
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -40,7 +40,7 @@ First, check out the master branch. Then cd there and run:
|
|
40
40
|
|
41
41
|
## Usage
|
42
42
|
|
43
|
-
Currently, Rcsv only supports CSV parsing. CSV write support is
|
43
|
+
Currently, Rcsv only supports CSV parsing. CSV write support is under works.
|
44
44
|
|
45
45
|
Quickstart:
|
46
46
|
|
@@ -89,7 +89,8 @@ If CSV has a header, :columns keys can be strings that are equal to column names
|
|
89
89
|
* :alias - Object of any type (though usually a Symbol) that is used as a key that represents column name when :row_as_hash is set.
|
90
90
|
* :type - A Ruby Symbol that specifies Ruby data type that CSV cell value should be converted into. Supported types: :int, :float, :string, :bool. :string is the default.
|
91
91
|
* :default - Object of any type (though usually of the same type that is specified by :type option). If CSV doesn't have any value for a cell, this default value is used.
|
92
|
-
* :match -
|
92
|
+
* :match - An array of Ruby objects of supported type (see :type). If set, makes Rcsv skip all the rows where any column isn't included in its :match value. Useful for filtering data.
|
93
|
+
* :not_match - An array of Ruby objects of supported type (see :type). If set, makes Rcsv skip all the rows where any column is included in its :not_match value. Useful for skipping data and is an opposite of :match.
|
93
94
|
|
94
95
|
|
95
96
|
### :header
|
@@ -116,13 +117,13 @@ Specifies a number of bytes that are read at once, thus allowing to read drectly
|
|
116
117
|
|
117
118
|
## Examples
|
118
119
|
|
119
|
-
This example parses a 3-column CSV file and only returns parsed rows where "Age" values are
|
120
|
+
This example parses a 3-column CSV file and only returns parsed rows where "Age" values are parsed to 35, 36 or 37.
|
120
121
|
|
121
122
|
Rcsv.parse some_csv, :row_as_hash => true,
|
122
123
|
:columns => {
|
123
124
|
'First Name' => { :alias => :first_name, :default => "Unknown" },
|
124
125
|
'Last Name' => { :alias => :last_name, :default => "Unknown"},
|
125
|
-
'Age' => { :alias => :age, :type => :int, :match =>
|
126
|
+
'Age' => { :alias => :age, :type => :int, :match => [35, 36, 37]]
|
126
127
|
}
|
127
128
|
|
128
129
|
The result would look like this:
|
@@ -170,9 +171,9 @@ This way it is possible to read from a File directly, with a 20MiB buffer and pa
|
|
170
171
|
|
171
172
|
## To do
|
172
173
|
|
173
|
-
* More
|
174
|
-
*
|
175
|
-
*
|
174
|
+
* More tests for boolean values
|
175
|
+
* More tests for Ruby parse
|
176
|
+
* Finish CSV write support
|
176
177
|
|
177
178
|
|
178
179
|
## Contributing
|
data/RELNOTES
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
Version 0.1.1
|
2
|
+
* added :not_match filter that skips rows with values listed in that option
|
3
|
+
* eliminated many potential memory leaks related to exception handling
|
4
|
+
|
5
|
+
Version 0.1.0
|
6
|
+
* backwards-incompatible change in :match option: filtering now happens after creating Ruby objects and thus rows can now be filtered not only as CSV field strings
|
7
|
+
* fixed a bug where field's key was being set to nil if :row_as_hash was enabled and :alias wasn't defined
|
8
|
+
* fixed assert_equal parameters order in tests
|
9
|
+
|
1
10
|
Version 0.0.9
|
2
11
|
* preliminary write support
|
3
12
|
|
data/ext/rcsv/rcsv.c
CHANGED
@@ -12,22 +12,24 @@ static VALUE rcsv_parse_error; /* class Rcsv::ParseError << StandardError; end *
|
|
12
12
|
struct rcsv_metadata {
|
13
13
|
/* Derived from user-specified options */
|
14
14
|
bool row_as_hash; /* Used to return array of hashes rather than array of arrays */
|
15
|
-
bool empty_field_is_nil;
|
15
|
+
bool empty_field_is_nil; /* Do we convert empty fields to nils? */
|
16
16
|
size_t offset_rows; /* Number of rows to skip before parsing */
|
17
17
|
|
18
18
|
char * row_conversions; /* A pointer to string/array of row conversions char specifiers */
|
19
|
-
|
19
|
+
VALUE * only_rows; /* A pointer to array of row filters */
|
20
|
+
VALUE * except_rows; /* A pointer to array of negative row filters */
|
20
21
|
VALUE * row_defaults; /* A pointer to array of row defaults */
|
21
22
|
VALUE * column_names; /* A pointer to array of column names to be used with hashes */
|
22
23
|
|
23
24
|
/* Pointer options lengths */
|
24
25
|
size_t num_row_conversions; /* Number of converter types in row_conversions array */
|
25
26
|
size_t num_only_rows; /* Number of items in only_rows filter */
|
27
|
+
size_t num_except_rows; /* Number of items in except_rows filter */
|
26
28
|
size_t num_row_defaults; /* Number of default values in row_defaults array */
|
27
29
|
size_t num_columns; /* Number of columns detected from column_names.size */
|
28
30
|
|
29
31
|
/* Internal state */
|
30
|
-
bool skip_current_row; /* Used by only_rows
|
32
|
+
bool skip_current_row; /* Used by only_rows and except_rows filters to skip parsing of the row remainder */
|
31
33
|
size_t current_col; /* Current column's index */
|
32
34
|
size_t current_row; /* Current row's index */
|
33
35
|
|
@@ -55,16 +57,6 @@ void end_of_field_callback(void * field, size_t field_size, void * data) {
|
|
55
57
|
return;
|
56
58
|
}
|
57
59
|
|
58
|
-
/* Filter by string row values listed in meta->only_rows */
|
59
|
-
if ((meta->only_rows != NULL) &&
|
60
|
-
(field_str != NULL) && /* TODO: What if we want to filter out NULLs? */
|
61
|
-
(meta->current_col < meta->num_only_rows) &&
|
62
|
-
(meta->only_rows[meta->current_col] != NULL) &&
|
63
|
-
(strcmp(meta->only_rows[meta->current_col], field_str))) {
|
64
|
-
meta->skip_current_row = true;
|
65
|
-
return;
|
66
|
-
}
|
67
|
-
|
68
60
|
/* Get row conversion char specifier */
|
69
61
|
if (meta->current_col < meta->num_row_conversions) {
|
70
62
|
row_conversion = (char)meta->row_conversions[meta->current_col];
|
@@ -130,6 +122,24 @@ void end_of_field_callback(void * field, size_t field_size, void * data) {
|
|
130
122
|
}
|
131
123
|
}
|
132
124
|
|
125
|
+
/* Filter by row values listed in meta->only_rows */
|
126
|
+
if ((meta->only_rows != NULL) &&
|
127
|
+
(meta->current_col < meta->num_only_rows) &&
|
128
|
+
(meta->only_rows[meta->current_col] != Qnil) &&
|
129
|
+
(!rb_ary_includes(meta->only_rows[meta->current_col], parsed_field))) {
|
130
|
+
meta->skip_current_row = true;
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
|
134
|
+
/* Filter out by row values listed in meta->except_rows */
|
135
|
+
if ((meta->except_rows != NULL) &&
|
136
|
+
(meta->current_col < meta->num_except_rows) &&
|
137
|
+
(meta->except_rows[meta->current_col] != Qnil) &&
|
138
|
+
(rb_ary_includes(meta->except_rows[meta->current_col], parsed_field))) {
|
139
|
+
meta->skip_current_row = true;
|
140
|
+
return;
|
141
|
+
}
|
142
|
+
|
133
143
|
/* Assign the value to appropriate hash key if parsing into Hash */
|
134
144
|
if (meta->row_as_hash) {
|
135
145
|
if (meta->current_col >= meta->num_columns) {
|
@@ -187,110 +197,136 @@ void end_of_line_callback(int last_char, void * data) {
|
|
187
197
|
return;
|
188
198
|
}
|
189
199
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
200
|
+
/* Filter rows need to be either nil or Arrays of Strings/bools/nil, so we validate this here */
|
201
|
+
VALUE validate_filter_row(const char * filter_name, VALUE row) {
|
202
|
+
if (row == Qnil) {
|
203
|
+
return Qnil;
|
204
|
+
} else if (TYPE(row) == T_ARRAY) {
|
205
|
+
size_t j;
|
206
|
+
for (j = 0; j < (size_t)RARRAY_LEN(row); j++) {
|
207
|
+
switch (TYPE(rb_ary_entry(row, j))) {
|
208
|
+
case T_NIL:
|
209
|
+
case T_TRUE:
|
210
|
+
case T_FALSE:
|
211
|
+
case T_FLOAT:
|
212
|
+
case T_FIXNUM:
|
213
|
+
case T_BIGNUM:
|
214
|
+
case T_REGEXP:
|
215
|
+
case T_STRING:
|
216
|
+
break;
|
217
|
+
default:
|
218
|
+
rb_raise(rcsv_parse_error,
|
219
|
+
":%s can only accept nil or Array consisting of String, boolean or nil elements, but %s was provided.",
|
220
|
+
filter_name, RSTRING_PTR(rb_inspect(row)));
|
221
|
+
}
|
222
|
+
}
|
223
|
+
return row;
|
224
|
+
} else {
|
225
|
+
rb_raise(rcsv_parse_error,
|
226
|
+
":%s can only accept nil or Array as an element, but %s was provided.",
|
227
|
+
filter_name, RSTRING_PTR(rb_inspect(row)));
|
194
228
|
}
|
195
229
|
}
|
196
230
|
|
197
|
-
/*
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
231
|
+
/* All the possible free()'s should be listed here.
|
232
|
+
This function should be invoked before returning the result to Ruby or raising an exception. */
|
233
|
+
void free_memory(struct csv_parser * cp, struct rcsv_metadata * meta) {
|
234
|
+
if (meta->only_rows != NULL) {
|
235
|
+
free(meta->only_rows);
|
236
|
+
}
|
203
237
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
size_t csv_string_len;
|
208
|
-
int error;
|
209
|
-
size_t i = 0;
|
238
|
+
if (meta->except_rows != NULL) {
|
239
|
+
free(meta->except_rows);
|
240
|
+
}
|
210
241
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
meta.skip_current_row = false;
|
215
|
-
meta.num_columns = 0;
|
216
|
-
meta.current_col = 0;
|
217
|
-
meta.current_row = 0;
|
218
|
-
meta.offset_rows = 0;
|
219
|
-
meta.num_only_rows = 0;
|
220
|
-
meta.num_row_defaults = 0;
|
221
|
-
meta.num_row_conversions = 0;
|
222
|
-
meta.only_rows = NULL;
|
223
|
-
meta.row_defaults = NULL;
|
224
|
-
meta.row_conversions = NULL;
|
225
|
-
meta.column_names = NULL;
|
226
|
-
meta.result = (VALUE[]){rb_ary_new()}; /* [] */
|
242
|
+
if (meta->row_defaults != NULL) {
|
243
|
+
free(meta->row_defaults);
|
244
|
+
}
|
227
245
|
|
228
|
-
|
229
|
-
|
246
|
+
if (meta->column_names != NULL) {
|
247
|
+
free(meta->column_names);
|
248
|
+
}
|
230
249
|
|
231
|
-
|
232
|
-
|
233
|
-
options = rb_hash_new();
|
250
|
+
if (cp != NULL) {
|
251
|
+
csv_free(cp);
|
234
252
|
}
|
253
|
+
}
|
235
254
|
|
236
|
-
|
255
|
+
/* An rb_rescue()-compatible free_memory() wrapper that unpacks C pointers from Ruby's Fixnums */
|
256
|
+
VALUE rcsv_free_memory(VALUE ensure_container) {
|
257
|
+
free_memory(
|
258
|
+
(struct csv_parser *)NUM2LONG(rb_ary_entry(ensure_container, 3)),
|
259
|
+
(struct rcsv_metadata *)NUM2LONG(rb_ary_entry(ensure_container, 2))
|
260
|
+
);
|
237
261
|
|
238
|
-
|
239
|
-
|
240
|
-
if (!option || (option == Qnil)) {
|
241
|
-
csv_options |= CSV_STRICT;
|
242
|
-
}
|
262
|
+
return Qnil;
|
263
|
+
}
|
243
264
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
meta.empty_field_is_nil = false;
|
252
|
-
} else {
|
253
|
-
rb_raise(rcsv_parse_error, "The only valid options for :parse_empty_fields_as are :nil, :string and :nil_or_string, but %s was supplied.", RSTRING_PTR(rb_inspect(option)));
|
254
|
-
}
|
265
|
+
/* An rb_rescue()-compatible Ruby pseudo-method that handles the actual parsing */
|
266
|
+
VALUE rcsv_raw_parse(VALUE ensure_container) {
|
267
|
+
/* Unpacking multiple variables from a single Ruby VALUE */
|
268
|
+
VALUE options = rb_ary_entry(ensure_container, 0);
|
269
|
+
VALUE csvio = rb_ary_entry(ensure_container, 1);
|
270
|
+
struct rcsv_metadata * meta = (struct rcsv_metadata *)NUM2LONG(rb_ary_entry(ensure_container, 2));
|
271
|
+
struct csv_parser * cp = (struct csv_parser *)NUM2LONG(rb_ary_entry(ensure_container, 3));
|
255
272
|
|
256
|
-
/*
|
257
|
-
|
258
|
-
|
259
|
-
|
273
|
+
/* Helper temporary variables */
|
274
|
+
VALUE option, csvstr, buffer_size;
|
275
|
+
|
276
|
+
/* libcsv-related temporary variables */
|
277
|
+
char * csv_string;
|
278
|
+
size_t csv_string_len;
|
279
|
+
int error;
|
280
|
+
|
281
|
+
/* Generic iterator */
|
282
|
+
size_t i = 0;
|
283
|
+
|
284
|
+
/* IO buffer size can be controller via an option */
|
285
|
+
buffer_size = rb_hash_aref(options, ID2SYM(rb_intern("buffer_size")));
|
260
286
|
|
261
287
|
/* By default, parse as Array of Arrays */
|
262
288
|
option = rb_hash_aref(options, ID2SYM(rb_intern("row_as_hash")));
|
263
289
|
if (option && (option != Qnil)) {
|
264
|
-
meta
|
290
|
+
meta->row_as_hash = true;
|
265
291
|
}
|
266
292
|
|
267
293
|
/* :col_sep sets the column separator, default is comma (,) */
|
268
294
|
option = rb_hash_aref(options, ID2SYM(rb_intern("col_sep")));
|
269
295
|
if (option != Qnil) {
|
270
|
-
csv_set_delim(
|
296
|
+
csv_set_delim(cp, (unsigned char)*StringValuePtr(option));
|
271
297
|
}
|
272
298
|
|
273
299
|
/* Specify how many rows to skip from the beginning of CSV */
|
274
300
|
option = rb_hash_aref(options, ID2SYM(rb_intern("offset_rows")));
|
275
301
|
if (option != Qnil) {
|
276
|
-
meta
|
302
|
+
meta->offset_rows = (size_t)NUM2INT(option);
|
277
303
|
}
|
278
304
|
|
279
|
-
/* :only_rows is a
|
305
|
+
/* :only_rows is a list of values where row is only parsed
|
280
306
|
if its fields match those in the passed array.
|
281
|
-
[nil, nil, "ABC"] skips all rows where 3rd column isn't equal to "ABC" */
|
307
|
+
[nil, nil, ["ABC", nil, 1]] skips all rows where 3rd column isn't equal to "ABC", nil or 1 */
|
282
308
|
option = rb_hash_aref(options, ID2SYM(rb_intern("only_rows")));
|
283
309
|
if (option != Qnil) {
|
284
|
-
meta
|
285
|
-
meta
|
310
|
+
meta->num_only_rows = (size_t)RARRAY_LEN(option);
|
311
|
+
meta->only_rows = (VALUE *)malloc(meta->num_only_rows * sizeof(VALUE));
|
286
312
|
|
287
|
-
for (i = 0; i < meta
|
313
|
+
for (i = 0; i < meta->num_only_rows; i++) {
|
288
314
|
VALUE only_row = rb_ary_entry(option, i);
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
315
|
+
meta->only_rows[i] = validate_filter_row("only_rows", only_row);
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
/* :except_rows is a list of values where row is only parsed
|
320
|
+
if its fields don't match those in the passed array.
|
321
|
+
[nil, nil, ["ABC", nil, 1]] skips all rows where 3rd column is equal to "ABC", nil or 1 */
|
322
|
+
option = rb_hash_aref(options, ID2SYM(rb_intern("except_rows")));
|
323
|
+
if (option != Qnil) {
|
324
|
+
meta->num_except_rows = (size_t)RARRAY_LEN(option);
|
325
|
+
meta->except_rows = (VALUE *)malloc(meta->num_except_rows * sizeof(VALUE));
|
326
|
+
|
327
|
+
for (i = 0; i < meta->num_except_rows; i++) {
|
328
|
+
VALUE except_row = rb_ary_entry(option, i);
|
329
|
+
meta->except_rows[i] = validate_filter_row("except_rows", except_row);
|
294
330
|
}
|
295
331
|
}
|
296
332
|
|
@@ -298,12 +334,12 @@ static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
|
298
334
|
according to matching field positions */
|
299
335
|
option = rb_hash_aref(options, ID2SYM(rb_intern("row_defaults")));
|
300
336
|
if (option != Qnil) {
|
301
|
-
meta
|
302
|
-
meta
|
337
|
+
meta->num_row_defaults = RARRAY_LEN(option);
|
338
|
+
meta->row_defaults = (VALUE*)malloc(meta->num_row_defaults * sizeof(VALUE*));
|
303
339
|
|
304
|
-
for (i = 0; i < meta
|
340
|
+
for (i = 0; i < meta->num_row_defaults; i++) {
|
305
341
|
VALUE row_default = rb_ary_entry(option, i);
|
306
|
-
meta
|
342
|
+
meta->row_defaults[i] = row_default;
|
307
343
|
}
|
308
344
|
}
|
309
345
|
|
@@ -311,27 +347,27 @@ static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
|
311
347
|
Each char of row_conversions string represents Ruby type for CSV field with matching position. */
|
312
348
|
option = rb_hash_aref(options, ID2SYM(rb_intern("row_conversions")));
|
313
349
|
if (option != Qnil) {
|
314
|
-
meta
|
315
|
-
meta
|
350
|
+
meta->num_row_conversions = RSTRING_LEN(option);
|
351
|
+
meta->row_conversions = StringValuePtr(option);
|
316
352
|
}
|
317
353
|
|
318
354
|
/* Column names should be declared explicitly when parsing fields as Hashes */
|
319
|
-
if (meta
|
355
|
+
if (meta->row_as_hash) { /* Only matters for hash results */
|
320
356
|
option = rb_hash_aref(options, ID2SYM(rb_intern("column_names")));
|
321
357
|
if (option == Qnil) {
|
322
358
|
rb_raise(rcsv_parse_error, ":row_as_hash requires :column_names to be set.");
|
323
359
|
} else {
|
324
|
-
meta
|
360
|
+
meta->last_entry = rb_hash_new();
|
325
361
|
|
326
|
-
meta
|
327
|
-
meta
|
362
|
+
meta->num_columns = (size_t)RARRAY_LEN(option);
|
363
|
+
meta->column_names = (VALUE*)malloc(meta->num_columns * sizeof(VALUE*));
|
328
364
|
|
329
|
-
for (i = 0; i < meta
|
330
|
-
meta
|
365
|
+
for (i = 0; i < meta->num_columns; i++) {
|
366
|
+
meta->column_names[i] = rb_ary_entry(option, i);
|
331
367
|
}
|
332
368
|
}
|
333
369
|
} else {
|
334
|
-
meta
|
370
|
+
meta->last_entry = rb_ary_new();
|
335
371
|
}
|
336
372
|
|
337
373
|
while(true) {
|
@@ -342,9 +378,9 @@ static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
|
342
378
|
csv_string_len = strlen(csv_string);
|
343
379
|
|
344
380
|
/* Actual parsing and error handling */
|
345
|
-
if (csv_string_len != csv_parse(
|
346
|
-
&end_of_field_callback, &end_of_line_callback,
|
347
|
-
error = csv_error(
|
381
|
+
if (csv_string_len != csv_parse(cp, csv_string, csv_string_len,
|
382
|
+
&end_of_field_callback, &end_of_line_callback, meta)) {
|
383
|
+
error = csv_error(cp);
|
348
384
|
switch(error) {
|
349
385
|
case CSV_EPARSE:
|
350
386
|
rb_raise(rcsv_parse_error, "Error when parsing malformed data");
|
@@ -364,22 +400,85 @@ static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
|
364
400
|
}
|
365
401
|
}
|
366
402
|
|
367
|
-
/* Flushing libcsv's buffer
|
368
|
-
csv_fini(
|
369
|
-
|
403
|
+
/* Flushing libcsv's buffer */
|
404
|
+
csv_fini(cp, &end_of_field_callback, &end_of_line_callback, meta);
|
405
|
+
|
406
|
+
return Qnil;
|
407
|
+
}
|
408
|
+
|
409
|
+
/* C API */
|
410
|
+
|
411
|
+
/* The main method that handles parsing */
|
412
|
+
static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
413
|
+
struct rcsv_metadata meta;
|
414
|
+
VALUE csvio, options, option;
|
415
|
+
VALUE ensure_container = rb_ary_new(); /* [] */
|
416
|
+
|
417
|
+
struct csv_parser cp;
|
418
|
+
unsigned char csv_options = CSV_STRICT_FINI | CSV_APPEND_NULL;
|
419
|
+
|
420
|
+
/* Setting up some sane defaults */
|
421
|
+
meta.row_as_hash = false;
|
422
|
+
meta.empty_field_is_nil = false;
|
423
|
+
meta.skip_current_row = false;
|
424
|
+
meta.num_columns = 0;
|
425
|
+
meta.current_col = 0;
|
426
|
+
meta.current_row = 0;
|
427
|
+
meta.offset_rows = 0;
|
428
|
+
meta.num_only_rows = 0;
|
429
|
+
meta.num_except_rows = 0;
|
430
|
+
meta.num_row_defaults = 0;
|
431
|
+
meta.num_row_conversions = 0;
|
432
|
+
meta.only_rows = NULL;
|
433
|
+
meta.except_rows = NULL;
|
434
|
+
meta.row_defaults = NULL;
|
435
|
+
meta.row_conversions = NULL;
|
436
|
+
meta.column_names = NULL;
|
437
|
+
meta.result = (VALUE[]){rb_ary_new()}; /* [] */
|
438
|
+
|
439
|
+
/* csvio is required, options is optional (pun intended) */
|
440
|
+
rb_scan_args(argc, argv, "11", &csvio, &options);
|
441
|
+
|
442
|
+
/* options ||= {} */
|
443
|
+
if (NIL_P(options)) {
|
444
|
+
options = rb_hash_new();
|
445
|
+
}
|
446
|
+
|
447
|
+
/* First of all, we parse libcsv-related params so that it fails early if something is wrong with them */
|
370
448
|
|
371
|
-
|
372
|
-
|
449
|
+
/* By default, parsing is strict */
|
450
|
+
option = rb_hash_aref(options, ID2SYM(rb_intern("nostrict")));
|
451
|
+
if (!option || (option == Qnil)) {
|
452
|
+
csv_options |= CSV_STRICT;
|
373
453
|
}
|
374
454
|
|
375
|
-
|
376
|
-
|
455
|
+
/* By default, empty strings are treated as Nils and quoted empty strings are treated as empty Ruby strings */
|
456
|
+
option = rb_hash_aref(options, ID2SYM(rb_intern("parse_empty_fields_as")));
|
457
|
+
if ((option == Qnil) || (option == ID2SYM(rb_intern("nil_or_string")))) {
|
458
|
+
csv_options |= CSV_EMPTY_IS_NULL;
|
459
|
+
} else if (option == ID2SYM(rb_intern("nil"))) {
|
460
|
+
meta.empty_field_is_nil = true;
|
461
|
+
} else if (option == ID2SYM(rb_intern("string"))) {
|
462
|
+
meta.empty_field_is_nil = false;
|
463
|
+
} else {
|
464
|
+
rb_raise(rcsv_parse_error, "The only valid options for :parse_empty_fields_as are :nil, :string and :nil_or_string, but %s was supplied.", RSTRING_PTR(rb_inspect(option)));
|
377
465
|
}
|
378
466
|
|
379
|
-
|
380
|
-
|
467
|
+
/* rb_ensure() only expects callback functions to accept and return VALUEs */
|
468
|
+
/* This ugly hack converts C pointers into Ruby Fixnums in order to pass them in Array */
|
469
|
+
rb_ary_push(ensure_container, options); /* [options] */
|
470
|
+
rb_ary_push(ensure_container, csvio); /* [options, csvio] */
|
471
|
+
rb_ary_push(ensure_container, LONG2NUM((long)&meta)); /* [options, csvio, &meta] */
|
472
|
+
rb_ary_push(ensure_container, LONG2NUM((long)&cp)); /* [options, csvio, &meta, &cp] */
|
473
|
+
|
474
|
+
/* Try to initialize libcsv */
|
475
|
+
if (csv_init(&cp, csv_options) == -1) {
|
476
|
+
rb_raise(rcsv_parse_error, "Couldn't initialize libcsv");
|
381
477
|
}
|
382
478
|
|
479
|
+
/* From now on, cp handles allocated data and should be free'd on exit or exception */
|
480
|
+
rb_ensure(rcsv_raw_parse, ensure_container, rcsv_free_memory, ensure_container);
|
481
|
+
|
383
482
|
/* Remove the last row if it's empty. That happens if CSV file ends with a newline. */
|
384
483
|
if (RARRAY_LEN(*(meta.result)) && /* meta.result.size != 0 */
|
385
484
|
RARRAY_LEN(rb_ary_entry(*(meta.result), -1)) == 0) {
|
@@ -393,7 +492,6 @@ static VALUE rb_rcsv_raw_parse(int argc, VALUE * argv, VALUE self) {
|
|
393
492
|
}
|
394
493
|
}
|
395
494
|
|
396
|
-
|
397
495
|
/* Define Ruby API */
|
398
496
|
void Init_rcsv(void) {
|
399
497
|
VALUE klass = rb_define_class("Rcsv", rb_cObject); /* class Rcsv; end */
|
data/lib/rcsv.rb
CHANGED
@@ -59,6 +59,7 @@ class Rcsv
|
|
59
59
|
|
60
60
|
if options[:columns]
|
61
61
|
only_rows = []
|
62
|
+
except_rows = []
|
62
63
|
row_defaults = []
|
63
64
|
column_names = []
|
64
65
|
row_conversions = ''
|
@@ -67,10 +68,29 @@ class Rcsv
|
|
67
68
|
column_options = options[:columns][column_header]
|
68
69
|
if column_options
|
69
70
|
if (options[:row_as_hash])
|
70
|
-
column_names << column_options[:alias] || column_header
|
71
|
+
column_names << (column_options[:alias] || column_header)
|
71
72
|
end
|
73
|
+
|
72
74
|
row_defaults << column_options[:default] || nil
|
73
|
-
|
75
|
+
|
76
|
+
only_rows << case column_options[:match]
|
77
|
+
when Array
|
78
|
+
column_options[:match]
|
79
|
+
when nil
|
80
|
+
nil
|
81
|
+
else
|
82
|
+
[column_options[:match]]
|
83
|
+
end
|
84
|
+
|
85
|
+
except_rows << case column_options[:not_match]
|
86
|
+
when Array
|
87
|
+
column_options[:not_match]
|
88
|
+
when nil
|
89
|
+
nil
|
90
|
+
else
|
91
|
+
[column_options[:not_match]]
|
92
|
+
end
|
93
|
+
|
74
94
|
row_conversions << case column_options[:type]
|
75
95
|
when :int
|
76
96
|
'i'
|
@@ -83,24 +103,27 @@ class Rcsv
|
|
83
103
|
when nil
|
84
104
|
's' # strings by default
|
85
105
|
else
|
86
|
-
fail "Unknown column type"
|
106
|
+
fail "Unknown column type #{column_options[:type].inspect}."
|
87
107
|
end
|
88
108
|
elsif options[:only_listed_columns]
|
89
109
|
column_names << nil
|
90
110
|
row_defaults << nil
|
91
111
|
only_rows << nil
|
112
|
+
except_rows << nil
|
92
113
|
row_conversions << ' '
|
93
114
|
else
|
94
115
|
column_names << column_header
|
95
116
|
row_defaults << nil
|
96
117
|
only_rows << nil
|
118
|
+
except_rows << nil
|
97
119
|
row_conversions << 's'
|
98
120
|
end
|
99
121
|
end
|
100
122
|
|
101
123
|
raw_options[:column_names] = column_names if options[:row_as_hash]
|
102
|
-
raw_options[:only_rows] = only_rows
|
103
|
-
raw_options[:
|
124
|
+
raw_options[:only_rows] = only_rows unless only_rows.compact.empty?
|
125
|
+
raw_options[:except_rows] = except_rows unless except_rows.compact.empty?
|
126
|
+
raw_options[:row_defaults] = row_defaults unless row_defaults.compact.empty?
|
104
127
|
raw_options[:row_conversions] = row_conversions
|
105
128
|
end
|
106
129
|
|
data/lib/rcsv/version.rb
CHANGED
data/test/test_rcsv.csv
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
DSAdsfksjh,iii ooo iii,EDADEDADEDADEDADEDADEDAD,111 333 555,NMLKTF,---==---,//,###,0000000000,Asdad bvd qwert,;
|
1
|
+
DSAdsfksjh,iii ooo iii,EDADEDADEDADEDADEDADEDAD,111 333 555,NMLKTF,---==---,//,###,0000000000,Asdad bvd qwert,;sd,@@@,OCTZ,$$$908080, ,true/false
|
2
2
|
C85A5B9F,85259637,,96,6838,1983-06-14,"""""C4CA-=; **1679; .. 79","210,11",908e,1281-03-09,7257.4654049904275,20efe749-50fe-4b6a-a603-7f9cd1dc6c6d,3,"New York, NY",u,2.228169203286535,t
|
3
3
|
3ABC8DF7,83229030,,79,9134,1987-05-24,"""""8F14-=; **C9F0; .. 74","321,42",b896,1358-04-08,-139.10142026231654,3e18cc6f-8ef0-41ac-bd4c-79a0120f65fd,8,"San Jose, CA",,-9.549296585513721,1
|
4
4
|
F0A83489,69118080,,73,7008,2016-10-03,"""""C81E-=; **ECCB; .. 89","130,86",a3eb,1341-04-10,7612.699237971538,5b5e3fce-2ea5-4ca9-9749-90fd6dc8dd66,9,"Los Angeles, CA",e,6.047887837492023,f
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rcsv'
|
3
|
+
|
4
|
+
class RcsvParseTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@options = {
|
7
|
+
:header => true,
|
8
|
+
:columns => {
|
9
|
+
'Date' => {
|
10
|
+
:type => :string
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_rcsv_parse_unknown_rows
|
17
|
+
csv = "a,b,c,d,e\n1,2,3,4,5"
|
18
|
+
parsed_data = Rcsv.parse(csv,
|
19
|
+
:row_as_hash => true,
|
20
|
+
:columns => {
|
21
|
+
'b' => {
|
22
|
+
:type => :int,
|
23
|
+
:alias => 'B'
|
24
|
+
},
|
25
|
+
'd' => {
|
26
|
+
:type => :int
|
27
|
+
}
|
28
|
+
}
|
29
|
+
)
|
30
|
+
|
31
|
+
assert_equal({
|
32
|
+
'a' => '1',
|
33
|
+
'B' => 2,
|
34
|
+
'c' => '3',
|
35
|
+
'd' => 4,
|
36
|
+
'e' => '5'
|
37
|
+
}, parsed_data.first)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_rcsv_parse_only_rows
|
41
|
+
csv = "a,1,t\nb,2,false\nc,3,0"
|
42
|
+
parsed_data = Rcsv.parse(csv,
|
43
|
+
:header => :none,
|
44
|
+
:columns => [
|
45
|
+
{
|
46
|
+
:match => ['z', 'a', '1']
|
47
|
+
},
|
48
|
+
{},
|
49
|
+
{
|
50
|
+
:match => true,
|
51
|
+
:type => :bool
|
52
|
+
}
|
53
|
+
]
|
54
|
+
)
|
55
|
+
|
56
|
+
assert_equal([["a", "1", true]], parsed_data)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_rcsv_parse_only_rows_not_match
|
60
|
+
csv = "a,1,t\nb,2,false\nc,3,0"
|
61
|
+
parsed_data = Rcsv.parse(csv,
|
62
|
+
:header => :none,
|
63
|
+
:columns => [
|
64
|
+
{
|
65
|
+
:not_match => ['z', 'a', '1']
|
66
|
+
},
|
67
|
+
{
|
68
|
+
:type => :int
|
69
|
+
},
|
70
|
+
{
|
71
|
+
:not_match => true,
|
72
|
+
:type => :bool
|
73
|
+
}
|
74
|
+
]
|
75
|
+
)
|
76
|
+
|
77
|
+
assert_equal([["b", 2, false], ["c", 3, false]], parsed_data)
|
78
|
+
end
|
79
|
+
end
|
data/test/test_rcsv_raw_parse.rb
CHANGED
@@ -9,47 +9,47 @@ class RcsvRawParseTest < Test::Unit::TestCase
|
|
9
9
|
def test_rcsv
|
10
10
|
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data)
|
11
11
|
|
12
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
13
|
-
assert_equal(raw_parsed_csv_data[0][13]
|
14
|
-
assert_equal(raw_parsed_csv_data[0][14]
|
15
|
-
assert_equal(raw_parsed_csv_data[0][15]
|
16
|
-
assert_equal(raw_parsed_csv_data[0][16]
|
17
|
-
assert_equal(raw_parsed_csv_data[9][2]
|
18
|
-
assert_equal(
|
19
|
-
assert_equal(raw_parsed_csv_data[888][13]
|
12
|
+
assert_equal('EDADEDADEDADEDADEDADEDAD', raw_parsed_csv_data[0][2])
|
13
|
+
assert_equal('$$$908080', raw_parsed_csv_data[0][13])
|
14
|
+
assert_equal('"', raw_parsed_csv_data[0][14])
|
15
|
+
assert_equal('true/false', raw_parsed_csv_data[0][15])
|
16
|
+
assert_equal(nil, raw_parsed_csv_data[0][16])
|
17
|
+
assert_equal(nil, raw_parsed_csv_data[9][2])
|
18
|
+
assert_equal('""C81E-=; **ECCB; .. 89', raw_parsed_csv_data[3][6])
|
19
|
+
assert_equal('Dallas, TX', raw_parsed_csv_data[888][13])
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_rcsv_col_sep
|
23
23
|
tsv_data = StringIO.new(@csv_data.read.tr(",", "\t"))
|
24
24
|
raw_parsed_tsv_data = Rcsv.raw_parse(tsv_data, :col_sep => "\t")
|
25
25
|
|
26
|
-
assert_equal(raw_parsed_tsv_data[0][2]
|
27
|
-
assert_equal(raw_parsed_tsv_data[0][13]
|
28
|
-
assert_equal(raw_parsed_tsv_data[0][14]
|
29
|
-
assert_equal(raw_parsed_tsv_data[0][15]
|
30
|
-
assert_equal(raw_parsed_tsv_data[0][16]
|
31
|
-
assert_equal(raw_parsed_tsv_data[9][2]
|
32
|
-
assert_equal(
|
33
|
-
assert_equal(raw_parsed_tsv_data[888][13]
|
26
|
+
assert_equal('EDADEDADEDADEDADEDADEDAD', raw_parsed_tsv_data[0][2])
|
27
|
+
assert_equal('$$$908080', raw_parsed_tsv_data[0][13])
|
28
|
+
assert_equal('"', raw_parsed_tsv_data[0][14])
|
29
|
+
assert_equal('true/false', raw_parsed_tsv_data[0][15])
|
30
|
+
assert_equal(nil, raw_parsed_tsv_data[0][16])
|
31
|
+
assert_equal(nil, raw_parsed_tsv_data[9][2])
|
32
|
+
assert_equal('""C81E-=; **ECCB; .. 89', raw_parsed_tsv_data[3][6])
|
33
|
+
assert_equal("Dallas\t TX", raw_parsed_tsv_data[888][13])
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_buffer_size
|
37
37
|
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :buffer_size => 10)
|
38
38
|
|
39
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
40
|
-
assert_equal(raw_parsed_csv_data[0][13]
|
41
|
-
assert_equal(raw_parsed_csv_data[0][14]
|
42
|
-
assert_equal(raw_parsed_csv_data[0][15]
|
43
|
-
assert_equal(raw_parsed_csv_data[0][16]
|
44
|
-
assert_equal(raw_parsed_csv_data[9][2]
|
45
|
-
assert_equal(
|
46
|
-
assert_equal(raw_parsed_csv_data[888][13]
|
39
|
+
assert_equal('EDADEDADEDADEDADEDADEDAD', raw_parsed_csv_data[0][2])
|
40
|
+
assert_equal('$$$908080', raw_parsed_csv_data[0][13])
|
41
|
+
assert_equal('"', raw_parsed_csv_data[0][14])
|
42
|
+
assert_equal('true/false', raw_parsed_csv_data[0][15])
|
43
|
+
assert_equal(nil, raw_parsed_csv_data[0][16])
|
44
|
+
assert_equal(nil, raw_parsed_csv_data[9][2])
|
45
|
+
assert_equal('""C81E-=; **ECCB; .. 89', raw_parsed_csv_data[3][6])
|
46
|
+
assert_equal('Dallas, TX', raw_parsed_csv_data[888][13])
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_single_item_csv
|
50
50
|
raw_parsed_csv_data = Rcsv.raw_parse(StringIO.new("Foo"))
|
51
51
|
|
52
|
-
assert_equal(
|
52
|
+
assert_equal([["Foo"]], raw_parsed_csv_data)
|
53
53
|
end
|
54
54
|
|
55
55
|
def test_broken_data
|
@@ -68,60 +68,68 @@ class RcsvRawParseTest < Test::Unit::TestCase
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_only_rows
|
71
|
-
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => ['GBP'])
|
71
|
+
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => [['GBP', 'NO SUCH THING']])
|
72
72
|
|
73
|
-
assert_equal(raw_parsed_csv_data[0][0]
|
74
|
-
assert_equal(raw_parsed_csv_data[-1][3]
|
75
|
-
assert_equal(raw_parsed_csv_data.size
|
73
|
+
assert_equal('GBP', raw_parsed_csv_data[0][0])
|
74
|
+
assert_equal('15', raw_parsed_csv_data[-1][3])
|
75
|
+
assert_equal(3, raw_parsed_csv_data.size)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_except_rows
|
79
|
+
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :except_rows => [['GBP', 'NO SUCH THING']])
|
80
|
+
|
81
|
+
assert_equal('C85A5B9F', raw_parsed_csv_data[1][0])
|
82
|
+
assert_equal('4971', raw_parsed_csv_data[28][4])
|
83
|
+
assert_equal(886, raw_parsed_csv_data.size)
|
76
84
|
end
|
77
85
|
|
78
86
|
def test_only_rows_with_nil_and_empty_string_filter
|
79
|
-
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => ['GBP', nil, '', '51'])
|
87
|
+
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => [['GBP'], nil, ['zzz', nil], ['51']])
|
80
88
|
|
81
|
-
assert_equal(raw_parsed_csv_data[0][0]
|
82
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
83
|
-
assert_equal(raw_parsed_csv_data.size
|
89
|
+
assert_equal('GBP', raw_parsed_csv_data[0][0])
|
90
|
+
assert_equal(nil, raw_parsed_csv_data[0][2])
|
91
|
+
assert_equal(1, raw_parsed_csv_data.size)
|
84
92
|
end
|
85
93
|
|
86
94
|
def test_only_rows_with_nil_beginning
|
87
|
-
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => [nil, nil, nil,
|
95
|
+
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :only_rows => [nil, nil, nil, [123, 96], nil], :row_conversions => 'sssi')
|
88
96
|
|
89
|
-
assert_equal(raw_parsed_csv_data[1][0]
|
90
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
91
|
-
assert_equal(raw_parsed_csv_data[3][3]
|
92
|
-
assert_equal(raw_parsed_csv_data.size
|
97
|
+
assert_equal('C3B87A6B', raw_parsed_csv_data[1][0])
|
98
|
+
assert_equal(nil, raw_parsed_csv_data[0][2])
|
99
|
+
assert_equal(96, raw_parsed_csv_data[3][3])
|
100
|
+
assert_equal(5, raw_parsed_csv_data.size)
|
93
101
|
end
|
94
102
|
|
95
103
|
def test_row_defaults
|
96
104
|
raw_parsed_csv_data = Rcsv.raw_parse(@csv_data, :row_defaults => [nil, nil, :booya, nil, 'never ever'])
|
97
105
|
|
98
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
99
|
-
assert_equal(raw_parsed_csv_data[1][2]
|
100
|
-
assert_equal(raw_parsed_csv_data[1][0]
|
101
|
-
assert_equal(raw_parsed_csv_data[2][4]
|
106
|
+
assert_equal('EDADEDADEDADEDADEDADEDAD', raw_parsed_csv_data[0][2])
|
107
|
+
assert_equal(:booya, raw_parsed_csv_data[1][2])
|
108
|
+
assert_equal('C85A5B9F', raw_parsed_csv_data[1][0])
|
109
|
+
assert_equal('9134', raw_parsed_csv_data[2][4])
|
102
110
|
end
|
103
111
|
|
104
112
|
def test_row_conversions
|
105
113
|
raw_parsed_csv_data = Rcsv.raw_parse(StringIO.new(@csv_data.each_line.to_a[1..-1].join), # skipping string headers
|
106
114
|
:row_conversions => 'sisiisssssfsissf')
|
107
115
|
|
108
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
109
|
-
assert_equal(raw_parsed_csv_data[0][3]
|
110
|
-
assert_equal(raw_parsed_csv_data[0][8]
|
111
|
-
assert_equal(raw_parsed_csv_data[1][15]
|
112
|
-
assert_equal(raw_parsed_csv_data[3][5]
|
116
|
+
assert_equal(nil, raw_parsed_csv_data[0][2])
|
117
|
+
assert_equal(96, raw_parsed_csv_data[0][3])
|
118
|
+
assert_equal('908e', raw_parsed_csv_data[0][8])
|
119
|
+
assert_equal(-9.549296585513721, raw_parsed_csv_data[1][15])
|
120
|
+
assert_equal('2015-12-22', raw_parsed_csv_data[3][5])
|
113
121
|
end
|
114
122
|
|
115
123
|
def test_row_conversions_with_column_exclusions
|
116
124
|
raw_parsed_csv_data = Rcsv.raw_parse(StringIO.new(@csv_data.each_line.to_a[1..-1].join), # skipping string headers
|
117
125
|
:row_conversions => 's f issss fsis fb')
|
118
126
|
|
119
|
-
assert_equal(raw_parsed_csv_data[0][1]
|
120
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
121
|
-
assert_equal(
|
122
|
-
assert_equal(raw_parsed_csv_data[0][12]
|
123
|
-
assert_equal(raw_parsed_csv_data[0][13]
|
124
|
-
assert_equal(raw_parsed_csv_data[4][3]
|
127
|
+
assert_equal(nil, raw_parsed_csv_data[0][1])
|
128
|
+
assert_equal(6838, raw_parsed_csv_data[0][2])
|
129
|
+
assert_equal('20efe749-50fe-4b6a-a603-7f9cd1dc6c6d', raw_parsed_csv_data[0][8])
|
130
|
+
assert_equal(true, raw_parsed_csv_data[0][12])
|
131
|
+
assert_equal(nil, raw_parsed_csv_data[0][13])
|
132
|
+
assert_equal('2020-12-09', raw_parsed_csv_data[4][3])
|
125
133
|
end
|
126
134
|
|
127
135
|
def test_offset_rows
|
@@ -181,14 +189,14 @@ class RcsvRawParseTest < Test::Unit::TestCase
|
|
181
189
|
}
|
182
190
|
|
183
191
|
assert_equal(nil, result)
|
184
|
-
assert_equal(raw_parsed_csv_data[0][2]
|
185
|
-
assert_equal(raw_parsed_csv_data[0][13]
|
186
|
-
assert_equal(raw_parsed_csv_data[0][14]
|
187
|
-
assert_equal(raw_parsed_csv_data[0][15]
|
188
|
-
assert_equal(raw_parsed_csv_data[0][16]
|
189
|
-
assert_equal(raw_parsed_csv_data[9][2]
|
190
|
-
assert_equal(
|
191
|
-
assert_equal(raw_parsed_csv_data[888][13]
|
192
|
+
assert_equal('EDADEDADEDADEDADEDADEDAD', raw_parsed_csv_data[0][2])
|
193
|
+
assert_equal('$$$908080', raw_parsed_csv_data[0][13])
|
194
|
+
assert_equal('"', raw_parsed_csv_data[0][14])
|
195
|
+
assert_equal('true/false', raw_parsed_csv_data[0][15])
|
196
|
+
assert_equal(nil, raw_parsed_csv_data[0][16])
|
197
|
+
assert_equal(nil, raw_parsed_csv_data[9][2])
|
198
|
+
assert_equal('""C81E-=; **ECCB; .. 89', raw_parsed_csv_data[3][6])
|
199
|
+
assert_equal('Dallas, TX', raw_parsed_csv_data[888][13])
|
192
200
|
end
|
193
201
|
|
194
202
|
def test_hash_block_streaming
|
data/test/test_rcsv_write.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcsv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Arthur Pirogovski
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-18 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: A libcsv-based CSV parser for Ruby
|
15
14
|
email:
|
@@ -39,36 +38,36 @@ files:
|
|
39
38
|
- lib/rcsv/version.rb
|
40
39
|
- rcsv.gemspec
|
41
40
|
- test/test_rcsv.csv
|
41
|
+
- test/test_rcsv_parse.rb
|
42
42
|
- test/test_rcsv_raw_parse.rb
|
43
43
|
- test/test_rcsv_write.rb
|
44
44
|
homepage: http://github.com/fiksu/rcsv
|
45
45
|
licenses: []
|
46
|
+
metadata: {}
|
46
47
|
post_install_message:
|
47
48
|
rdoc_options: []
|
48
49
|
require_paths:
|
49
50
|
- lib
|
50
51
|
- ext
|
51
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
-
none: false
|
53
53
|
requirements:
|
54
54
|
- - ! '>='
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
58
|
requirements:
|
60
59
|
- - ! '>='
|
61
60
|
- !ruby/object:Gem::Version
|
62
61
|
version: '0'
|
63
62
|
requirements: []
|
64
63
|
rubyforge_project:
|
65
|
-
rubygems_version:
|
64
|
+
rubygems_version: 2.0.3
|
66
65
|
signing_key:
|
67
|
-
specification_version:
|
66
|
+
specification_version: 4
|
68
67
|
summary: Fast CSV parsing library for MRI based on libcsv. Supports type conversion,
|
69
68
|
non-strict parsing and basic filtering.
|
70
69
|
test_files:
|
71
70
|
- test/test_rcsv.csv
|
71
|
+
- test/test_rcsv_parse.rb
|
72
72
|
- test/test_rcsv_raw_parse.rb
|
73
73
|
- test/test_rcsv_write.rb
|
74
|
-
has_rdoc:
|