fastcsv 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +12 -17
- data/TESTS.md +4 -5
- data/ext/fastcsv/fastcsv.c +2494 -153
- data/ext/fastcsv/fastcsv.rl +43 -8
- data/fastcsv.gemspec +5 -6
- data/lib/fastcsv.rb +1 -1
- data/spec/fastcsv_spec.rb +0 -1
- data/test/csv/test_csv_parsing.rb +10 -10
- data/test/csv/test_encodings.rb +23 -23
- data/test/csv/test_features.rb +17 -17
- data/test/csv/test_headers.rb +14 -14
- data/test/csv/test_interface.rb +39 -39
- metadata +9 -11
data/ext/fastcsv/fastcsv.rl
CHANGED
@@ -22,6 +22,9 @@ if (enc2 != NULL) { \
|
|
22
22
|
#define FREE \
|
23
23
|
if (buf != NULL) { \
|
24
24
|
free(buf); \
|
25
|
+
} \
|
26
|
+
if (row_sep != NULL) { \
|
27
|
+
free(row_sep); \
|
25
28
|
}
|
26
29
|
|
27
30
|
static VALUE cClass, cParser, eError;
|
@@ -96,9 +99,23 @@ typedef struct {
|
|
96
99
|
|
97
100
|
action mark_row {
|
98
101
|
d->start = p;
|
102
|
+
|
103
|
+
if (len_row_sep) {
|
104
|
+
if (p - mark_row_sep != len_row_sep || row_sep[0] != *mark_row_sep || (len_row_sep == 2 && row_sep[1] != *(mark_row_sep + 1))) {
|
105
|
+
FREE;
|
106
|
+
|
107
|
+
rb_raise(eError, "Unquoted fields do not allow \\r or \\n (line %d).", curline - 1);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
else {
|
111
|
+
len_row_sep = p - mark_row_sep;
|
112
|
+
row_sep = ALLOC_N(char, len_row_sep);
|
113
|
+
memcpy(row_sep, mark_row_sep, len_row_sep);
|
114
|
+
}
|
99
115
|
}
|
100
116
|
|
101
117
|
action new_row {
|
118
|
+
mark_row_sep = p;
|
102
119
|
curline++;
|
103
120
|
|
104
121
|
if (d->start == 0 || p == d->start) {
|
@@ -118,7 +135,7 @@ typedef struct {
|
|
118
135
|
}
|
119
136
|
|
120
137
|
action last_row {
|
121
|
-
if (d->start == 0 || p == d->start) {
|
138
|
+
if (d->start == 0 || p == d->start) { // same as new_row
|
122
139
|
rb_ivar_set(self, s_row, rb_str_new2(""));
|
123
140
|
}
|
124
141
|
else if (p > d->start) {
|
@@ -135,8 +152,8 @@ typedef struct {
|
|
135
152
|
}
|
136
153
|
|
137
154
|
EOF = 0;
|
138
|
-
quote_char =
|
139
|
-
col_sep =
|
155
|
+
quote_char = any when { fc == quote_char };
|
156
|
+
col_sep = any when { fc == col_sep } >new_field;
|
140
157
|
row_sep = ('\r' '\n'? | '\n');
|
141
158
|
unquoted = (any* -- quote_char -- col_sep -- row_sep - EOF) %read_unquoted;
|
142
159
|
quoted = quote_char >open_quote (any - quote_char - EOF | quote_char quote_char | row_sep)* %read_quoted quote_char >close_quote;
|
@@ -184,18 +201,18 @@ static void rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_enco
|
|
184
201
|
|
185
202
|
static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
186
203
|
int cs, act, have = 0, curline = 1, io = 0;
|
187
|
-
char *ts = 0, *te = 0, *buf = 0, *eof = 0;
|
204
|
+
char *ts = 0, *te = 0, *buf = 0, *eof = 0, *mark_row_sep = 0, *row_sep = 0;
|
188
205
|
|
189
206
|
VALUE port, opts, r_encoding;
|
190
207
|
VALUE row = rb_ary_new(), field = Qnil, bufsize = Qnil;
|
191
|
-
int done = 0, unclosed_line = 0, buffer_size = 0, taint = 0;
|
208
|
+
int done = 0, unclosed_line = 0, buffer_size = 0, taint = 0, len_row_sep = 0;
|
192
209
|
rb_encoding *enc = NULL, *enc2 = NULL, *encoding = NULL;
|
193
210
|
|
194
211
|
Data *d;
|
195
212
|
Data_Get_Struct(self, Data, d);
|
196
213
|
|
197
214
|
VALUE option;
|
198
|
-
char quote_char = '"';
|
215
|
+
char quote_char = '"', col_sep = ',';
|
199
216
|
|
200
217
|
rb_scan_args(argc, argv, "11", &port, &opts);
|
201
218
|
taint = OBJ_TAINTED(port);
|
@@ -217,6 +234,22 @@ static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
|
217
234
|
rb_raise(rb_eArgError, "options has to be a Hash or nil");
|
218
235
|
}
|
219
236
|
|
237
|
+
option = rb_hash_aref(opts, ID2SYM(rb_intern("quote_char")));
|
238
|
+
if (TYPE(option) == T_STRING && RSTRING_LEN(option) == 1) {
|
239
|
+
quote_char = *StringValueCStr(option);
|
240
|
+
}
|
241
|
+
else if (!NIL_P(option)) {
|
242
|
+
rb_raise(rb_eArgError, ":quote_char has to be a single character String");
|
243
|
+
}
|
244
|
+
|
245
|
+
option = rb_hash_aref(opts, ID2SYM(rb_intern("col_sep")));
|
246
|
+
if (TYPE(option) == T_STRING && RSTRING_LEN(option) == 1) {
|
247
|
+
col_sep = *StringValueCStr(option);
|
248
|
+
}
|
249
|
+
else if (!NIL_P(option)) {
|
250
|
+
rb_raise(rb_eArgError, ":col_sep has to be a single character String");
|
251
|
+
}
|
252
|
+
|
220
253
|
// @see rb_io_extract_modeenc
|
221
254
|
/* Set to defaults */
|
222
255
|
rb_io_ext_int_to_encs(NULL, NULL, &enc, &enc2, 0);
|
@@ -351,7 +384,7 @@ static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
|
351
384
|
while (!done) {
|
352
385
|
VALUE str;
|
353
386
|
char *p, *pe;
|
354
|
-
int len, space = buffer_size - have, tokstart_diff, tokend_diff, start_diff;
|
387
|
+
int len, space = buffer_size - have, tokstart_diff, tokend_diff, start_diff, mark_row_sep_diff;
|
355
388
|
|
356
389
|
if (io) {
|
357
390
|
if (space == 0) {
|
@@ -359,6 +392,7 @@ static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
|
359
392
|
tokstart_diff = ts - buf;
|
360
393
|
tokend_diff = te - buf;
|
361
394
|
start_diff = d->start - buf;
|
395
|
+
mark_row_sep_diff = mark_row_sep - buf;
|
362
396
|
|
363
397
|
buffer_size += BUFSIZE;
|
364
398
|
REALLOC_N(buf, char, buffer_size);
|
@@ -368,6 +402,7 @@ static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
|
368
402
|
ts = buf + tokstart_diff;
|
369
403
|
te = buf + tokend_diff;
|
370
404
|
d->start = buf + start_diff;
|
405
|
+
mark_row_sep = buf + mark_row_sep_diff;
|
371
406
|
}
|
372
407
|
p = buf + have;
|
373
408
|
|
@@ -408,7 +443,7 @@ static VALUE raw_parse(int argc, VALUE *argv, VALUE self) {
|
|
408
443
|
%% write exec;
|
409
444
|
|
410
445
|
if (done && cs < raw_parse_first_final) {
|
411
|
-
if (d->start == 0 || p == d->start) {
|
446
|
+
if (d->start == 0 || p == d->start) { // same as new_row
|
412
447
|
rb_ivar_set(self, s_row, rb_str_new2(""));
|
413
448
|
}
|
414
449
|
else if (p > d->start) {
|
data/fastcsv.gemspec
CHANGED
@@ -2,12 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "fastcsv"
|
5
|
-
s.version = '0.0.
|
5
|
+
s.version = '0.0.5'
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
|
-
s.authors = ["
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
s.summary = %q{A fast Ragel-based CSV parser}
|
7
|
+
s.authors = ["James McKinney"]
|
8
|
+
s.homepage = "https://github.com/jpmckinney/fastcsv"
|
9
|
+
s.summary = %q{A fast Ragel-based CSV parser, compatible with Ruby's CSV}
|
11
10
|
s.license = 'MIT'
|
12
11
|
|
13
12
|
s.files = `git ls-files`.split("\n")
|
@@ -17,7 +16,7 @@ Gem::Specification.new do |s|
|
|
17
16
|
s.extensions = ["ext/fastcsv/extconf.rb"]
|
18
17
|
|
19
18
|
s.add_development_dependency('coveralls')
|
20
|
-
s.add_development_dependency('json', '~> 1.
|
19
|
+
s.add_development_dependency('json', '~> 1.8') # to silence coveralls warning
|
21
20
|
s.add_development_dependency('rake')
|
22
21
|
s.add_development_dependency('rake-compiler')
|
23
22
|
s.add_development_dependency('rspec', '~> 3.1')
|
data/lib/fastcsv.rb
CHANGED
data/spec/fastcsv_spec.rb
CHANGED
@@ -147,7 +147,6 @@ RSpec.shared_examples 'a CSV parser' do
|
|
147
147
|
it 'should raise an error on mixed row separators' do
|
148
148
|
csv = "foo\rbar\nbaz\r\n"
|
149
149
|
expect{CSV.parse(csv)}.to raise_error(CSV::MalformedCSVError, 'Unquoted fields do not allow \r or \n (line 2).')
|
150
|
-
skip
|
151
150
|
expect{FastCSV.parse(csv)}.to raise_error(FastCSV::MalformedCSVError, 'Unquoted fields do not allow \r or \n (line 2).')
|
152
151
|
end
|
153
152
|
|
@@ -160,16 +160,16 @@ class TestCSV::Parsing < TestCSV
|
|
160
160
|
assert_equal(6, lines.size)
|
161
161
|
assert_match(/\Aline,4/, lines.find { |l| l =~ /some\rjunk/ })
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
163
|
+
csv = FastCSV.new(bad_data)
|
164
|
+
begin
|
165
|
+
loop do
|
166
|
+
assert_not_nil(csv.shift)
|
167
|
+
assert_send([csv.lineno, :<, 5]) # FIXME 4
|
168
|
+
end
|
169
|
+
rescue FastCSV::MalformedCSVError
|
170
|
+
assert_equal( "Unquoted fields do not allow \\r or \\n (line 4).",
|
171
|
+
$!.message )
|
172
|
+
end
|
173
173
|
|
174
174
|
assert_raise(FastCSV::MalformedCSVError) { FastCSV.parse_line('1,2,"3...') }
|
175
175
|
|
data/test/csv/test_encodings.rb
CHANGED
@@ -72,7 +72,7 @@ class TestCSV::Encodings < TestCSV
|
|
72
72
|
each_encoding do |encoding|
|
73
73
|
begin
|
74
74
|
assert_parses( [ %w[ abc def ],
|
75
|
-
%w[ ghi jkl ] ], encoding, col_sep: "
|
75
|
+
%w[ ghi jkl ] ], encoding, col_sep: "|" )
|
76
76
|
rescue Encoding::ConverterNotFoundError
|
77
77
|
fail("Failed to properly escape #{encoding.name}.")
|
78
78
|
end
|
@@ -112,8 +112,8 @@ class TestCSV::Encodings < TestCSV
|
|
112
112
|
def test_csv_chars_are_transcoded
|
113
113
|
encode_for_tests([%w[abc def]]) do |data|
|
114
114
|
%w[col_sep row_sep quote_char].each do |csv_char|
|
115
|
-
assert_equal( "
|
116
|
-
FastCSV.new(data, csv_char.to_sym => "
|
115
|
+
assert_equal( "|".encode(data.encoding),
|
116
|
+
FastCSV.new(data, csv_char.to_sym => "|").send(csv_char) )
|
117
117
|
end
|
118
118
|
end
|
119
119
|
end
|
@@ -221,15 +221,15 @@ class TestCSV::Encodings < TestCSV
|
|
221
221
|
each_encoding do |encoding|
|
222
222
|
# test generate_line with encoding hint
|
223
223
|
begin
|
224
|
-
csv = %w[abc d
|
225
|
-
to_csv(col_sep: "
|
224
|
+
csv = %w[abc d|ef].map { |f| f.encode(encoding) }.
|
225
|
+
to_csv(col_sep: "|", encoding: encoding.name)
|
226
226
|
rescue Encoding::ConverterNotFoundError
|
227
227
|
next
|
228
228
|
end
|
229
229
|
assert_equal(encoding, csv.encoding)
|
230
230
|
|
231
231
|
# test generate_line with encoding guessing from fields
|
232
|
-
csv = %w[abc d
|
232
|
+
csv = %w[abc d|ef].map { |f| f.encode(encoding) }.to_csv(col_sep: "|")
|
233
233
|
assert_equal(encoding, csv.encoding)
|
234
234
|
|
235
235
|
# writing to files
|
@@ -283,23 +283,23 @@ class TestCSV::Encodings < TestCSV
|
|
283
283
|
end unless encoding == __ENCODING__
|
284
284
|
rescue Encoding::ConverterNotFoundError
|
285
285
|
end
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
286
|
+
options[:encoding] = encoding.name
|
287
|
+
FastCSV.open(@temp_csv_path, options) do |csv|
|
288
|
+
csv.each_with_index do |row, i|
|
289
|
+
assert_equal(fields[i], row)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
options.delete(:encoding)
|
293
|
+
options[:external_encoding] = encoding.name
|
294
|
+
options[:internal_encoding] = __ENCODING__.name
|
295
|
+
begin
|
296
|
+
FastCSV.open(@temp_csv_path, options) do |csv|
|
297
|
+
csv.each_with_index do |row, i|
|
298
|
+
assert_equal(orig_fields[i], row)
|
299
|
+
end
|
300
|
+
end unless encoding == __ENCODING__
|
301
|
+
rescue Encoding::ConverterNotFoundError
|
302
|
+
end
|
303
303
|
end
|
304
304
|
|
305
305
|
def encode_ary(ary, encoding)
|
data/test/csv/test_features.rb
CHANGED
@@ -47,16 +47,16 @@ class TestCSV::Features < TestCSV
|
|
47
47
|
@csv = FastCSV.new(@sample_data)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
50
|
+
def test_col_sep
|
51
|
+
[";", "\t"].each do |sep|
|
52
|
+
TEST_CASES.each do |test_case|
|
53
|
+
assert_equal( test_case.last.map { |t| t.tr(",", sep) unless t.nil? },
|
54
|
+
FastCSV.parse_line( test_case.first.tr(",", sep),
|
55
|
+
col_sep: sep ) )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
assert_equal([",,,", nil], FastCSV.parse_line(",,,;", col_sep: ";"))
|
59
|
+
end
|
60
60
|
|
61
61
|
# def test_row_sep
|
62
62
|
# assert_raise(FastCSV::MalformedCSVError) do
|
@@ -66,13 +66,13 @@ class TestCSV::Features < TestCSV
|
|
66
66
|
# FastCSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n"))
|
67
67
|
# end
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
69
|
+
def test_quote_char
|
70
|
+
TEST_CASES.each do |test_case|
|
71
|
+
assert_equal( test_case.last.map { |t| t.tr('"', "'") unless t.nil? },
|
72
|
+
FastCSV.parse_line( test_case.first.tr('"', "'"),
|
73
|
+
quote_char: "'" ) )
|
74
|
+
end
|
75
|
+
end
|
76
76
|
|
77
77
|
def test_csv_char_readers
|
78
78
|
%w[col_sep row_sep quote_char].each do |reader|
|
data/test/csv/test_headers.rb
CHANGED
@@ -131,20 +131,20 @@ class TestCSV::Headers < TestCSV
|
|
131
131
|
assert(!row.field_row?)
|
132
132
|
end
|
133
133
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
134
|
+
def test_csv_header_string_inherits_separators
|
135
|
+
# parse with custom col_sep
|
136
|
+
csv = nil
|
137
|
+
assert_nothing_raised(Exception) do
|
138
|
+
csv = FastCSV.parse( @data.tr(",", "|"), col_sep: "|",
|
139
|
+
headers: "my|new|headers" )
|
140
|
+
end
|
141
|
+
|
142
|
+
# verify headers were recognized
|
143
|
+
row = csv[0]
|
144
|
+
assert_not_nil(row)
|
145
|
+
assert_instance_of(FastCSV::Row, row)
|
146
|
+
assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a)
|
147
|
+
end
|
148
148
|
|
149
149
|
def test_return_headers
|
150
150
|
# activate headers and request they are returned
|
data/test/csv/test_interface.rb
CHANGED
@@ -20,8 +20,8 @@ class TestCSV::Interface < TestCSV
|
|
20
20
|
@path = @tempfile.path
|
21
21
|
|
22
22
|
File.open(@path, "wb") do |file|
|
23
|
-
file << "1
|
24
|
-
file << "4
|
23
|
+
file << "1\t2\t3\r\n"
|
24
|
+
file << "4\t5\r\n"
|
25
25
|
end
|
26
26
|
|
27
27
|
@expected = [%w{1 2 3}, %w{4 5}]
|
@@ -35,19 +35,19 @@ class TestCSV::Interface < TestCSV
|
|
35
35
|
### Test Read Interface ###
|
36
36
|
|
37
37
|
def test_foreach
|
38
|
-
FastCSV.foreach(@path, col_sep: "
|
38
|
+
FastCSV.foreach(@path, col_sep: "\t", row_sep: "\r\n") do |row|
|
39
39
|
assert_equal(@expected.shift, row)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_foreach_enum
|
44
|
-
FastCSV.foreach(@path, col_sep: "
|
44
|
+
FastCSV.foreach(@path, col_sep: "\t", row_sep: "\r\n").zip(@expected) do |row, exp|
|
45
45
|
assert_equal(exp, row)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_open_and_close
|
50
|
-
csv = FastCSV.open(@path, "r+", col_sep: "
|
50
|
+
csv = FastCSV.open(@path, "r+", col_sep: "\t", row_sep: "\r\n")
|
51
51
|
assert_not_nil(csv)
|
52
52
|
assert_instance_of(FastCSV, csv)
|
53
53
|
assert_equal(false, csv.closed?)
|
@@ -66,21 +66,21 @@ class TestCSV::Interface < TestCSV
|
|
66
66
|
def test_parse
|
67
67
|
data = File.binread(@path)
|
68
68
|
assert_equal( @expected,
|
69
|
-
FastCSV.parse(data, col_sep: "
|
69
|
+
FastCSV.parse(data, col_sep: "\t", row_sep: "\r\n") )
|
70
70
|
|
71
|
-
FastCSV.parse(data, col_sep: "
|
71
|
+
FastCSV.parse(data, col_sep: "\t", row_sep: "\r\n") do |row|
|
72
72
|
assert_equal(@expected.shift, row)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
def test_parse_line
|
77
|
-
row = FastCSV.parse_line("1
|
77
|
+
row = FastCSV.parse_line("1;2;3", col_sep: ";")
|
78
78
|
assert_not_nil(row)
|
79
79
|
assert_instance_of(Array, row)
|
80
80
|
assert_equal(%w{1 2 3}, row)
|
81
81
|
|
82
82
|
# shortcut interface
|
83
|
-
row = "1
|
83
|
+
row = "1;2;3".parse_csv(col_sep: ";")
|
84
84
|
assert_not_nil(row)
|
85
85
|
assert_instance_of(Array, row)
|
86
86
|
assert_equal(%w{1 2 3}, row)
|
@@ -93,29 +93,29 @@ class TestCSV::Interface < TestCSV
|
|
93
93
|
|
94
94
|
def test_read_and_readlines
|
95
95
|
assert_equal( @expected,
|
96
|
-
FastCSV.read(@path, col_sep: "
|
96
|
+
FastCSV.read(@path, col_sep: "\t", row_sep: "\r\n") )
|
97
97
|
assert_equal( @expected,
|
98
|
-
FastCSV.readlines(@path, col_sep: "
|
98
|
+
FastCSV.readlines(@path, col_sep: "\t", row_sep: "\r\n") )
|
99
99
|
|
100
100
|
|
101
|
-
data = FastCSV.open(@path, col_sep: "
|
101
|
+
data = FastCSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
|
102
102
|
csv.read
|
103
103
|
end
|
104
104
|
assert_equal(@expected, data)
|
105
|
-
data = FastCSV.open(@path, col_sep: "
|
105
|
+
data = FastCSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
|
106
106
|
csv.readlines
|
107
107
|
end
|
108
108
|
assert_equal(@expected, data)
|
109
109
|
end
|
110
110
|
|
111
111
|
def test_table
|
112
|
-
table = FastCSV.table(@path, col_sep: "
|
112
|
+
table = FastCSV.table(@path, col_sep: "\t", row_sep: "\r\n")
|
113
113
|
assert_instance_of(FastCSV::Table, table)
|
114
114
|
assert_equal([[:"1", :"2", :"3"], [4, 5, nil]], table.to_a)
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_shift # aliased as gets() and readline()
|
118
|
-
FastCSV.open(@path, "rb+", col_sep: "
|
118
|
+
FastCSV.open(@path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
|
119
119
|
assert_equal(@expected.shift, csv.shift)
|
120
120
|
assert_equal(@expected.shift, csv.shift)
|
121
121
|
assert_equal(nil, csv.shift)
|
@@ -123,7 +123,7 @@ class TestCSV::Interface < TestCSV
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def test_enumerators_are_supported
|
126
|
-
FastCSV.open(@path, col_sep: "
|
126
|
+
FastCSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
|
127
127
|
enum = csv.each
|
128
128
|
assert_instance_of(Enumerator, enum)
|
129
129
|
assert_equal(@expected.shift, enum.next)
|
@@ -149,16 +149,16 @@ class TestCSV::Interface < TestCSV
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def test_generate_line
|
152
|
-
line = FastCSV.generate_line(%w{1 2 3}, col_sep: "
|
152
|
+
line = FastCSV.generate_line(%w{1 2 3}, col_sep: ";")
|
153
153
|
assert_not_nil(line)
|
154
154
|
assert_instance_of(String, line)
|
155
|
-
assert_equal("1
|
155
|
+
assert_equal("1;2;3\n", line)
|
156
156
|
|
157
157
|
# shortcut interface
|
158
|
-
line = %w{1 2 3}.to_csv(col_sep: "
|
158
|
+
line = %w{1 2 3}.to_csv(col_sep: ";")
|
159
159
|
assert_not_nil(line)
|
160
160
|
assert_instance_of(String, line)
|
161
|
-
assert_equal("1
|
161
|
+
assert_equal("1;2;3\n", line)
|
162
162
|
end
|
163
163
|
|
164
164
|
def test_write_header_detection
|
@@ -242,19 +242,19 @@ class TestCSV::Interface < TestCSV
|
|
242
242
|
File.unlink(@path)
|
243
243
|
|
244
244
|
lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
|
245
|
-
FastCSV.open(@path, "wb", headers: "b
|
245
|
+
FastCSV.open(@path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
|
246
246
|
lines.each { |line| csv << line }
|
247
247
|
end
|
248
248
|
|
249
249
|
# test writing fields in the correct order
|
250
250
|
File.open(@path, "rb") do |f|
|
251
|
-
assert_equal("2
|
252
|
-
assert_equal("5
|
251
|
+
assert_equal("2|1|3", f.gets.strip)
|
252
|
+
assert_equal("5|4|6", f.gets.strip)
|
253
253
|
end
|
254
254
|
|
255
255
|
# test reading FastCSV with headers
|
256
|
-
FastCSV.open( @path, "rb", headers: "b
|
257
|
-
col_sep: "
|
256
|
+
FastCSV.open( @path, "rb", headers: "b|a|c",
|
257
|
+
col_sep: "|",
|
258
258
|
converters: :all ) do |csv|
|
259
259
|
csv.each { |line| assert_equal(lines.shift, line.to_hash) }
|
260
260
|
end
|
@@ -264,22 +264,22 @@ class TestCSV::Interface < TestCSV
|
|
264
264
|
File.unlink(@path)
|
265
265
|
|
266
266
|
lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
|
267
|
-
FastCSV.open( @path, "wb", headers: "b
|
267
|
+
FastCSV.open( @path, "wb", headers: "b|a|c",
|
268
268
|
write_headers: true,
|
269
|
-
col_sep: "
|
269
|
+
col_sep: "|" ) do |csv|
|
270
270
|
lines.each { |line| csv << line }
|
271
271
|
end
|
272
272
|
|
273
273
|
# test writing fields in the correct order
|
274
274
|
File.open(@path, "rb") do |f|
|
275
|
-
assert_equal("b
|
276
|
-
assert_equal("2
|
277
|
-
assert_equal("5
|
275
|
+
assert_equal("b|a|c", f.gets.strip)
|
276
|
+
assert_equal("2|1|3", f.gets.strip)
|
277
|
+
assert_equal("5|4|6", f.gets.strip)
|
278
278
|
end
|
279
279
|
|
280
280
|
# test reading FastCSV with headers
|
281
281
|
FastCSV.open( @path, "rb", headers: true,
|
282
|
-
col_sep: "
|
282
|
+
col_sep: "|",
|
283
283
|
converters: :all ) do |csv|
|
284
284
|
csv.each { |line| assert_equal(lines.shift, line.to_hash) }
|
285
285
|
end
|
@@ -288,7 +288,7 @@ class TestCSV::Interface < TestCSV
|
|
288
288
|
def test_append # aliased add_row() and puts()
|
289
289
|
File.unlink(@path)
|
290
290
|
|
291
|
-
FastCSV.open(@path, "wb", col_sep: "
|
291
|
+
FastCSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
|
292
292
|
@expected.each { |row| csv << row }
|
293
293
|
end
|
294
294
|
|
@@ -297,7 +297,7 @@ class TestCSV::Interface < TestCSV
|
|
297
297
|
# same thing using FastCSV::Row objects
|
298
298
|
File.unlink(@path)
|
299
299
|
|
300
|
-
FastCSV.open(@path, "wb", col_sep: "
|
300
|
+
FastCSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
|
301
301
|
@expected.each { |row| csv << FastCSV::Row.new(Array.new, row) }
|
302
302
|
end
|
303
303
|
|
@@ -310,8 +310,8 @@ class TestCSV::Interface < TestCSV
|
|
310
310
|
assert_respond_to(FastCSV, :filter)
|
311
311
|
|
312
312
|
expected = [[1, 2, 3], [4, 5]]
|
313
|
-
FastCSV.filter( "1
|
314
|
-
in_col_sep: "
|
313
|
+
FastCSV.filter( "1;2;3\n4;5\n", (result = String.new),
|
314
|
+
in_col_sep: ";", out_col_sep: ",",
|
315
315
|
converters: :all ) do |row|
|
316
316
|
assert_equal(row, expected.shift)
|
317
317
|
row.map! { |n| n * 2 }
|
@@ -325,20 +325,20 @@ class TestCSV::Interface < TestCSV
|
|
325
325
|
|
326
326
|
first = nil
|
327
327
|
assert_nothing_raised(Exception) do
|
328
|
-
first = FastCSV.instance(csv, col_sep: "
|
328
|
+
first = FastCSV.instance(csv, col_sep: ";")
|
329
329
|
first << %w{a b c}
|
330
330
|
end
|
331
331
|
|
332
|
-
assert_equal("a
|
332
|
+
assert_equal("a;b;c\n", csv)
|
333
333
|
|
334
334
|
second = nil
|
335
335
|
assert_nothing_raised(Exception) do
|
336
|
-
second = FastCSV.instance(csv, col_sep: "
|
336
|
+
second = FastCSV.instance(csv, col_sep: ";")
|
337
337
|
second << [1, 2, 3]
|
338
338
|
end
|
339
339
|
|
340
340
|
assert_equal(first.object_id, second.object_id)
|
341
|
-
assert_equal("a
|
341
|
+
assert_equal("a;b;c\n1;2;3\n", csv)
|
342
342
|
|
343
343
|
# shortcuts
|
344
344
|
assert_equal(STDOUT, FastCSV.instance.instance_eval { @io })
|