rcsv 0.0.9 → 0.1.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.
- 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:
|