csvrecord 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +6 -1
- data/README.md +16 -9
- data/lib/csvrecord.rb +1 -0
- data/lib/csvrecord/base.rb +53 -15
- data/lib/csvrecord/reader.rb +188 -0
- data/lib/csvrecord/version.rb +1 -1
- data/test/data/beer11.csv +51 -0
- data/test/test_reader.rb +119 -0
- data/test/{test_beer.rb → test_record.rb} +32 -12
- data/test/test_record_auto.rb +34 -0
- data/test/test_version.rb +20 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f60b9d15d91c6d20b625bb343d895ad750f43a81
|
4
|
+
data.tar.gz: 5510e7921a509fe40906de7f533f185bf244e8bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9853722319a53c73469238440eb2c249cd8c3829711acbd35efc60996316f8d8e763d5f61decdc4e3186baebf13536c46c72217fd92f08e6d675b7a6c088568b
|
7
|
+
data.tar.gz: bd2922bf8c44a458abe70b5dceee9b405904f878eacdaf34a1454a2313c43517329d31841c2bc4faca5c6d3492220d9c5c3091115171046c087a2889a4415339
|
data/Manifest.txt
CHANGED
@@ -6,7 +6,12 @@ Rakefile
|
|
6
6
|
lib/csvrecord.rb
|
7
7
|
lib/csvrecord/base.rb
|
8
8
|
lib/csvrecord/builder.rb
|
9
|
+
lib/csvrecord/reader.rb
|
9
10
|
lib/csvrecord/version.rb
|
10
11
|
test/data/beer.csv
|
12
|
+
test/data/beer11.csv
|
11
13
|
test/helper.rb
|
12
|
-
test/
|
14
|
+
test/test_reader.rb
|
15
|
+
test/test_record.rb
|
16
|
+
test/test_record_auto.rb
|
17
|
+
test/test_version.rb
|
data/README.md
CHANGED
@@ -74,11 +74,18 @@ pretty prints (pp):
|
|
74
74
|
Or loop over the records. Example:
|
75
75
|
|
76
76
|
``` ruby
|
77
|
-
Beer.read(
|
77
|
+
Beer.read( 'beer.csv' ).each do |rec|
|
78
|
+
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# -or-
|
82
|
+
|
83
|
+
Beer.foreach( 'beer.csv' ) do |rec|
|
78
84
|
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
79
85
|
end
|
80
86
|
```
|
81
87
|
|
88
|
+
|
82
89
|
printing:
|
83
90
|
|
84
91
|
```
|
@@ -97,11 +104,11 @@ Or create new records from scratch. Example:
|
|
97
104
|
beer = Beer.new( 'Andechser Klosterbrauerei',
|
98
105
|
'Andechs',
|
99
106
|
'Doppelbock Dunkel',
|
100
|
-
'7
|
107
|
+
'7%' )
|
101
108
|
|
102
109
|
# -or-
|
103
110
|
|
104
|
-
values = ['Andechser Klosterbrauerei', 'Andechs', 'Doppelbock Dunkel', '7
|
111
|
+
values = ['Andechser Klosterbrauerei', 'Andechs', 'Doppelbock Dunkel', '7%']
|
105
112
|
beer = Beer.new( values )
|
106
113
|
|
107
114
|
# -or-
|
@@ -109,14 +116,14 @@ beer = Beer.new( values )
|
|
109
116
|
beer = Beer.new( brewery: 'Andechser Klosterbrauerei',
|
110
117
|
city: 'Andechs',
|
111
118
|
name: 'Doppelbock Dunkel',
|
112
|
-
abv: '7
|
119
|
+
abv: '7%' )
|
113
120
|
|
114
121
|
# -or-
|
115
122
|
|
116
123
|
hash = { brewery: 'Andechser Klosterbrauerei',
|
117
124
|
city: 'Andechs',
|
118
125
|
name: 'Doppelbock Dunkel',
|
119
|
-
abv: '7
|
126
|
+
abv: '7%' }
|
120
127
|
beer = Beer.new( hash )
|
121
128
|
|
122
129
|
|
@@ -126,24 +133,24 @@ beer = Beer.new
|
|
126
133
|
beer.update( brewery: 'Andechser Klosterbrauerei',
|
127
134
|
city: 'Andechs',
|
128
135
|
name: 'Doppelbock Dunkel' )
|
129
|
-
beer.update( abv:
|
136
|
+
beer.update( abv: 7.0 )
|
130
137
|
|
131
138
|
# -or-
|
132
139
|
|
133
140
|
beer = Beer.new
|
134
|
-
beer.parse( ['Andechser Klosterbrauerei', 'Andechs', 'Doppelbock Dunkel', '7
|
141
|
+
beer.parse( ['Andechser Klosterbrauerei', 'Andechs', 'Doppelbock Dunkel', '7%'] )
|
135
142
|
|
136
143
|
# -or-
|
137
144
|
|
138
145
|
beer = Beer.new
|
139
|
-
beer.parse( 'Andechser Klosterbrauerei,Andechs,Doppelbock Dunkel,7
|
146
|
+
beer.parse( 'Andechser Klosterbrauerei,Andechs,Doppelbock Dunkel,7%' )
|
140
147
|
|
141
148
|
# -or-
|
142
149
|
|
143
150
|
beer = Beer.new
|
144
151
|
beer.brewery = 'Andechser Klosterbrauerei'
|
145
152
|
beer.name = 'Doppelbock Dunkel'
|
146
|
-
beer.abv =
|
153
|
+
beer.abv = 7.0
|
147
154
|
```
|
148
155
|
|
149
156
|
|
data/lib/csvrecord.rb
CHANGED
data/lib/csvrecord/base.rb
CHANGED
@@ -100,6 +100,40 @@ module CsvRecord
|
|
100
100
|
end
|
101
101
|
|
102
102
|
|
103
|
+
###########################################
|
104
|
+
## "magic" lazy auto-build schema from headers versions
|
105
|
+
|
106
|
+
def self.build_class( headers ) ## check: find a better name - why? why not?
|
107
|
+
## (auto-)build record class from an array of headers
|
108
|
+
## add fields (all types will be string for now)
|
109
|
+
clazz = Class.new(Base)
|
110
|
+
headers.each do |header|
|
111
|
+
## downcase and remove all non-ascii chars etc.
|
112
|
+
## todo/fix: remove all non-ascii chars!!!
|
113
|
+
## todo: check if header starts with a number too!!
|
114
|
+
name = header.downcase.gsub( ' ', '_' )
|
115
|
+
name = name.to_sym ## symbol-ify
|
116
|
+
clazz.field( name )
|
117
|
+
end
|
118
|
+
clazz
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.read( path, sep: Csv.config.sep )
|
122
|
+
headers = CsvReader.header( path, sep: sep )
|
123
|
+
|
124
|
+
clazz = build_class( headers )
|
125
|
+
clazz.read( path, sep: sep )
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.foreach( path, sep: Csv.config.sep, &block )
|
129
|
+
headers = CsvReader.header( path, sep: sep )
|
130
|
+
|
131
|
+
clazz = build_class( headers )
|
132
|
+
clazz.foreach( path, sep: sep, &block )
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
|
103
137
|
|
104
138
|
class Base
|
105
139
|
|
@@ -234,11 +268,21 @@ def to_csv ## use/rename/alias to to_row too - why? why not?
|
|
234
268
|
end
|
235
269
|
|
236
270
|
|
271
|
+
def self.foreach( path, sep: Csv.config.sep, headers: true )
|
272
|
+
CsvReader.foreach( path, sep: sep, headers: headers ) do |row|
|
273
|
+
rec = new
|
274
|
+
values = CsvReader.unwrap( row )
|
275
|
+
rec.parse( values )
|
276
|
+
|
277
|
+
yield( rec ) ## check: use block.class( rec ) - why? why not?
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
237
281
|
|
238
|
-
def self.parse( txt_or_rows ) ## note: returns an (lazy) enumarator
|
282
|
+
def self.parse( txt_or_rows, sep: Csv.config.sep, headers: true ) ## note: returns an (lazy) enumarator
|
239
283
|
if txt_or_rows.is_a? String
|
240
284
|
txt = txt_or_rows
|
241
|
-
rows =
|
285
|
+
rows = CsvReader.parse( txt, sep: sep, headers: headers )
|
242
286
|
else
|
243
287
|
### todo/fix: use only self.create( array-like ) for array-like data - why? why not?
|
244
288
|
rows = txt_or_rows ## assume array-like records that responds to :each
|
@@ -248,25 +292,19 @@ def self.parse( txt_or_rows ) ## note: returns an (lazy) enumarator
|
|
248
292
|
|
249
293
|
Enumerator.new do |yielder|
|
250
294
|
rows.each do |row|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
## fix/todo!!!!!!!!!!!!!
|
257
|
-
## check for CSV::Row etc. - use row.to_hash ?
|
258
|
-
rec = new
|
259
|
-
rec.parse( row.fields )
|
260
|
-
## end
|
261
|
-
yielder.yield( rec )
|
295
|
+
rec = new
|
296
|
+
values = CsvReader.unwrap( row )
|
297
|
+
rec.parse( values )
|
298
|
+
|
299
|
+
yielder.yield( rec )
|
262
300
|
end
|
263
301
|
end
|
264
302
|
end
|
265
303
|
|
266
304
|
|
267
|
-
def self.read( path ) ## not returns an enumarator
|
305
|
+
def self.read( path, sep: Csv.config.sep, headers: true ) ## not returns an enumarator
|
268
306
|
txt = File.open( path, 'r:utf-8' ).read
|
269
|
-
parse( txt )
|
307
|
+
parse( txt, sep: sep, headers: headers )
|
270
308
|
end
|
271
309
|
|
272
310
|
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module Csv ## check: rename to CsvSettings / CsvPref / CsvGlobals or similar - why? why not???
|
5
|
+
|
6
|
+
## STD_CSV_ENGINE = CSV ## to avoid name confusion use longer name - why? why not? find a better name?
|
7
|
+
## use __CSV__ or similar? or just ::CSV ??
|
8
|
+
|
9
|
+
class Configuration
|
10
|
+
|
11
|
+
puts "CSV::VERSION:"
|
12
|
+
puts CSV::VERSION
|
13
|
+
|
14
|
+
puts "builtin CSV::Converters:"
|
15
|
+
pp CSV::Converters
|
16
|
+
|
17
|
+
puts "CSV::DEFAULT_OPTIONS:"
|
18
|
+
pp CSV::DEFAULT_OPTIONS
|
19
|
+
|
20
|
+
## register our own converters
|
21
|
+
## check if strip gets called for nil values too?
|
22
|
+
CSV::Converters[:strip] = ->(field) { field.strip }
|
23
|
+
|
24
|
+
|
25
|
+
attr_accessor :sep ## col_sep (column separator)
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@sep = ','
|
29
|
+
## note: do NOT add headers as global - should ALWAYS be explicit
|
30
|
+
## headers (true/false) - changes resultset and requires different processing!!!
|
31
|
+
|
32
|
+
self ## return self for chaining
|
33
|
+
end
|
34
|
+
|
35
|
+
def blank?( line )
|
36
|
+
## note: blank line does NOT include "blank" with spaces only!!
|
37
|
+
## use BLANK_REGEX in skip_lines to clean-up/skip/remove/ignore
|
38
|
+
## see skip_blanks in default_options
|
39
|
+
line.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
## lines starting with # (note: only leading spaces allowed)
|
43
|
+
COMMENTS_REGEX = /^\s*#/
|
44
|
+
BLANK_REGEX = /^\s*$/ ## skip all whitespace lines - note: use "" or , for a blank record!!!
|
45
|
+
SKIP_REGEX = Regexp.union( COMMENTS_REGEX, BLANK_REGEX )
|
46
|
+
|
47
|
+
def skip?( line )
|
48
|
+
## check if comment line - skip comments
|
49
|
+
## see skip_lines in default_options
|
50
|
+
line =~ SKIP_REGEX
|
51
|
+
end
|
52
|
+
|
53
|
+
## built-in (default) options
|
54
|
+
## todo: find a better name?
|
55
|
+
def default_options
|
56
|
+
## note:
|
57
|
+
## do NOT include sep character and
|
58
|
+
## do NOT include headers true/false here
|
59
|
+
##
|
60
|
+
## make default sep its own "global" default config
|
61
|
+
## e.g. Csv.config.sep =
|
62
|
+
|
63
|
+
## common options
|
64
|
+
## skip comments starting with #
|
65
|
+
## skip blank lines
|
66
|
+
## strip leading and trailing spaces
|
67
|
+
## NOTE/WARN: leading and trailing spaces NOT allowed/working with double quoted values!!!!
|
68
|
+
defaults = {
|
69
|
+
skip_blanks: true, ## note: skips lines with no whitespaces only!! (e.g. line with space is NOT blank!!)
|
70
|
+
skip_lines: SKIP_REGEX,
|
71
|
+
:converters => :strip
|
72
|
+
}
|
73
|
+
defaults
|
74
|
+
end
|
75
|
+
end # class Configuration
|
76
|
+
|
77
|
+
|
78
|
+
## lets you use
|
79
|
+
## Csv.configure do |config|
|
80
|
+
## config.sep = ',' ## or "/t"
|
81
|
+
## end
|
82
|
+
|
83
|
+
def self.configure
|
84
|
+
yield( config )
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.config
|
88
|
+
@config ||= Configuration.new
|
89
|
+
end
|
90
|
+
end # module Csvv
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
####
|
95
|
+
## use our own wrapper
|
96
|
+
|
97
|
+
class CsvReader
|
98
|
+
|
99
|
+
####################
|
100
|
+
# helper methods
|
101
|
+
def self.unwrap( row_or_array ) ## unwrap row - find a better name? why? why not?
|
102
|
+
## return row values as array of strings
|
103
|
+
if row_or_array.is_a?( CSV::Row )
|
104
|
+
row = row_or_array
|
105
|
+
row.fields ## gets array of string of field values
|
106
|
+
else ## assume "classic" array of strings
|
107
|
+
array = row_or_array
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
def self.foreach( path, sep: Csv.config.sep, headers: true )
|
114
|
+
csv_options = Csv.config.default_options.merge(
|
115
|
+
headers: headers,
|
116
|
+
col_sep: sep,
|
117
|
+
external_encoding: 'utf-8' ## note: always (auto-)add utf-8 external encoding for now!!!
|
118
|
+
)
|
119
|
+
|
120
|
+
CSV.foreach( path, csv_options ) do |row|
|
121
|
+
yield( row ) ## check/todo: use block.call( row ) ## why? why not?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def self.read( path, sep: Csv.config.sep, headers: true )
|
127
|
+
## note: use our own file.open
|
128
|
+
## always use utf-8 for now
|
129
|
+
## check/todo: add skip option bom too - why? why not?
|
130
|
+
txt = File.open( path, 'r:utf-8' )
|
131
|
+
parse( txt, sep: sep, headers: headers )
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.parse( txt, sep: Csv.config.sep, headers: true )
|
135
|
+
csv_options = Csv.config.default_options.merge(
|
136
|
+
headers: headers,
|
137
|
+
col_sep: sep
|
138
|
+
)
|
139
|
+
## pp csv_options
|
140
|
+
CSV.parse( txt, csv_options )
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.parse_line( txt, sep: Csv.config.sep )
|
144
|
+
## note: do NOT include headers option (otherwise single row gets skipped as first header row :-)
|
145
|
+
csv_options = Csv.config.default_options.merge(
|
146
|
+
headers: false, ## note: always turn off headers!!!!!!
|
147
|
+
col_sep: sep
|
148
|
+
)
|
149
|
+
## pp csv_options
|
150
|
+
CSV.parse_line( txt, csv_options )
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
def self.header( path, sep: Csv.config.sep ) ## use header or headers - or use both (with alias)?
|
156
|
+
# read first lines (only)
|
157
|
+
# and parse with csv to get header from csv library itself
|
158
|
+
#
|
159
|
+
# check - if there's an easier or built-in way for the csv library
|
160
|
+
|
161
|
+
## readlines until
|
162
|
+
## - NOT a comments line or
|
163
|
+
## - NOT a blank line
|
164
|
+
|
165
|
+
lines = ''
|
166
|
+
File.open( path, 'r:utf-8' ) do |f|
|
167
|
+
|
168
|
+
## todo/fix: how to handle empty files or files without headers?!
|
169
|
+
|
170
|
+
## todo/check if readline includes \n\r too??
|
171
|
+
## yes! - line include \n e.g.
|
172
|
+
## "Brewery,City,Name,Abv\n" or
|
173
|
+
## "#######\n# try with some comments\n# and blank lines even before header\n\nBrewery,City,Name,Abv\n"
|
174
|
+
loop do
|
175
|
+
line = f.readline
|
176
|
+
lines << line
|
177
|
+
break unless Csv.config.skip?( line ) || Csv.config.blank?( line )
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
## puts "lines:"
|
182
|
+
## pp lines
|
183
|
+
|
184
|
+
## note: do NOT use headers: true to get "plain" data array (no hash records)
|
185
|
+
## hash record does NOT work for single line/row
|
186
|
+
parse_line( lines, sep: sep )
|
187
|
+
end # method self.header
|
188
|
+
end # class CsvReader
|
data/lib/csvrecord/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
#######
|
2
|
+
# try with some comments
|
3
|
+
# and blank lines even before header
|
4
|
+
|
5
|
+
Brewery,City,Name,Abv
|
6
|
+
Andechser Klosterbrauerei,Andechs,Doppelbock Dunkel,7%
|
7
|
+
Augustiner Bräu München,München,Edelstoff,5.6%
|
8
|
+
Bayerische Staatsbrauerei Weihenstephan,Freising,Hefe Weissbier,5.4%
|
9
|
+
|
10
|
+
Brauerei Spezial, Bamberg, Rauchbier Märzen, 5.1%
|
11
|
+
|
12
|
+
Hacker-Pschorr Bräu, München, Münchner Dunkel, 5.0%
|
13
|
+
|
14
|
+
## some more comments here
|
15
|
+
|
16
|
+
Staatliches Hofbräuhaus München,München,Hofbräu Oktoberfestbier,6.3%
|
17
|
+
|
18
|
+
## check for nil
|
19
|
+
"", ,,"",
|
20
|
+
|
21
|
+
## check for blank line with spaces
|
22
|
+
## yes, will get added as a record!! e.g. ["", nil, nil, nil]
|
23
|
+
## use regex to skip blank lines with spaces!!!!
|
24
|
+
|
25
|
+
|
26
|
+
## test double quotes and double quotes escaped
|
27
|
+
## note: double quotes do NOT work with leading AND/OR trailing spaces
|
28
|
+
## leads to:
|
29
|
+
## CSV::MalformedCSVError - Missing or stray quote in line xxx
|
30
|
+
##
|
31
|
+
## note: for now double quote does not accept leading AND/OR trailing spaces!!!!
|
32
|
+
##
|
33
|
+
## todo/fix: check liberal_quote option starting in csv ruby 2.4 ???
|
34
|
+
##
|
35
|
+
## examples:
|
36
|
+
## "value with comma, comma","some ""hello""","some ""hello""",
|
37
|
+
## works - but does NOT work (note the leading and trailing spaces for double quotes):
|
38
|
+
## "value with comma, comma" ,"some ""hello""", "some ""hello""",
|
39
|
+
##
|
40
|
+
## check for "multi-line":
|
41
|
+
## "hello
|
42
|
+
## and another line
|
43
|
+
## and another",two,three,
|
44
|
+
|
45
|
+
|
46
|
+
"value with comma, comma","some ""hello""","some ""hello""",
|
47
|
+
|
48
|
+
## check for "multi-line"
|
49
|
+
"hello
|
50
|
+
and another line
|
51
|
+
and another",two,three,
|
data/test/test_reader.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_reader.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestReader < MiniTest::Test
|
11
|
+
|
12
|
+
def test_read
|
13
|
+
puts "== read: beer.csv:"
|
14
|
+
table = CsvReader.read( "#{CsvRecord.test_data_dir}/beer.csv" ) ## returns CSV::Table
|
15
|
+
|
16
|
+
pp table.class.name
|
17
|
+
pp table
|
18
|
+
pp table.to_a ## note: includes header (first row with column names)
|
19
|
+
|
20
|
+
table.each do |row| ## note: will skip (NOT include) header row!!
|
21
|
+
pp row
|
22
|
+
end
|
23
|
+
puts " #{table.size} rows" ## note: again will skip (NOT include) header row in count!!!
|
24
|
+
assert_equal 6, table.size
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_read_header_false
|
28
|
+
puts "== read (headers: false): beer.csv:"
|
29
|
+
data = CsvReader.read( "#{CsvRecord.test_data_dir}/beer.csv", headers: false )
|
30
|
+
|
31
|
+
pp data.class.name
|
32
|
+
pp data
|
33
|
+
|
34
|
+
data.each do |row|
|
35
|
+
pp row
|
36
|
+
end
|
37
|
+
puts " #{data.size} rows"
|
38
|
+
assert_equal 7, data.size ## note: include header row in count
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def test_read11
|
43
|
+
puts "== read: beer11.csv:"
|
44
|
+
table = CsvReader.read( "#{CsvRecord.test_data_dir}/beer11.csv" )
|
45
|
+
pp table
|
46
|
+
pp table.to_a ## note: includes header (first row with column names)
|
47
|
+
|
48
|
+
assert true
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def test_parse_line
|
53
|
+
puts "== parse_line:"
|
54
|
+
row = CsvReader.parse_line( <<TXT )
|
55
|
+
Augustiner Bräu München, München, Edelstoff, 5.6%
|
56
|
+
Bayerische Staatsbrauerei Weihenstephan, Freising, Hefe Weissbier, 5.4%
|
57
|
+
TXT
|
58
|
+
|
59
|
+
pp row
|
60
|
+
assert_equal ['Augustiner Bräu München', 'München', 'Edelstoff', '5.6%'], row
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_parse_line11
|
64
|
+
puts "== parse_line:"
|
65
|
+
row = CsvReader.parse_line( <<TXT )
|
66
|
+
#######
|
67
|
+
# try with some comments
|
68
|
+
# and blank lines even before header
|
69
|
+
|
70
|
+
Augustiner Bräu München, München, Edelstoff, 5.6%
|
71
|
+
Bayerische Staatsbrauerei Weihenstephan, Freising, Hefe Weissbier, 5.4%
|
72
|
+
TXT
|
73
|
+
|
74
|
+
pp row
|
75
|
+
assert_equal ['Augustiner Bräu München', 'München', 'Edelstoff', '5.6%'], row
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_header
|
79
|
+
puts "== header: beer.csv:"
|
80
|
+
header = CsvReader.header( "#{CsvRecord.test_data_dir}/beer.csv" )
|
81
|
+
pp header
|
82
|
+
assert_equal ['Brewery','City','Name','Abv'], header
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_header11
|
86
|
+
puts "== header: beer11.csv:"
|
87
|
+
header = CsvReader.header( "#{CsvRecord.test_data_dir}/beer11.csv" )
|
88
|
+
pp header
|
89
|
+
assert_equal ['Brewery','City','Name','Abv'], header
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def test_foreach
|
94
|
+
puts "== foreach: beer.csv:"
|
95
|
+
CsvReader.foreach( "#{CsvRecord.test_data_dir}/beer.csv" ) do |row|
|
96
|
+
pp row
|
97
|
+
pp row.fields
|
98
|
+
end
|
99
|
+
assert true
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_foreach11
|
103
|
+
puts "== foreach: beer11.csv:"
|
104
|
+
CsvReader.foreach( "#{CsvRecord.test_data_dir}/beer11.csv" ) do |row|
|
105
|
+
pp row
|
106
|
+
pp row.fields
|
107
|
+
end
|
108
|
+
assert true
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_foreach_header_false
|
112
|
+
puts "== foreach (headers: false): beer11.csv:"
|
113
|
+
CsvReader.foreach( "#{CsvRecord.test_data_dir}/beer11.csv", headers: false ) do |row|
|
114
|
+
pp row ## note: is Array (no .fields available!!!!!)
|
115
|
+
end
|
116
|
+
assert true
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class TestReader
|
@@ -2,21 +2,12 @@
|
|
2
2
|
|
3
3
|
###
|
4
4
|
# to run use
|
5
|
-
# ruby -I ./lib -I ./test test/
|
5
|
+
# ruby -I ./lib -I ./test test/test_record.rb
|
6
6
|
|
7
7
|
|
8
8
|
require 'helper'
|
9
9
|
|
10
|
-
class
|
11
|
-
|
12
|
-
def test_version
|
13
|
-
pp CsvRecord::VERSION
|
14
|
-
pp CsvRecord.banner
|
15
|
-
pp CsvRecord.root
|
16
|
-
|
17
|
-
assert true ## assume ok if we get here
|
18
|
-
end
|
19
|
-
|
10
|
+
class TestRecord < MiniTest::Test
|
20
11
|
|
21
12
|
def test_class_style1
|
22
13
|
clazz1 = CsvRecord.define do
|
@@ -104,6 +95,35 @@ class TestBeer < MiniTest::Test
|
|
104
95
|
assert true ## assume ok if we get here
|
105
96
|
end
|
106
97
|
|
98
|
+
|
99
|
+
def test_foreach
|
100
|
+
puts "== foreach:"
|
101
|
+
Beer.foreach( "#{CsvRecord.test_data_dir}/beer.csv" ) do |rec|
|
102
|
+
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
103
|
+
end
|
104
|
+
|
105
|
+
assert true ## assume ok if we get here
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def test_parse_array_or_arrays
|
110
|
+
data=[
|
111
|
+
['Brewery','City','Name','Abv'],
|
112
|
+
['Andechser Klosterbrauerei','Andechs','Doppelbock Dunkel','7%'],
|
113
|
+
['Augustiner Bräu München','München','Edelstoff','5.6%'],
|
114
|
+
['Bayerische Staatsbrauerei Weihenstephan','Freising','Hefe Weissbier','5.4%'],
|
115
|
+
['Brauerei Spezial','Bamberg','Rauchbier Märzen','5.1%'],
|
116
|
+
['Hacker-Pschorr Bräu','München','Münchner Dunkel','5.0%'],
|
117
|
+
['Staatliches Hofbräuhaus München','München','Hofbräu Oktoberfestbier','6.3%']]
|
118
|
+
|
119
|
+
beers = Beer.parse( data ).to_a
|
120
|
+
puts "#{beers.size} beers:"
|
121
|
+
pp beers
|
122
|
+
|
123
|
+
assert true ## assume ok if we get here
|
124
|
+
end
|
125
|
+
|
126
|
+
|
107
127
|
def test_classic
|
108
128
|
puts "== read( data ).to_a:"
|
109
129
|
beers = BeerClassic.read( "#{CsvRecord.test_data_dir}/beer.csv" ).to_a
|
@@ -227,4 +247,4 @@ class TestBeer < MiniTest::Test
|
|
227
247
|
assert_equal values[3].to_f, beer['Abv']
|
228
248
|
end
|
229
249
|
|
230
|
-
end # class
|
250
|
+
end # class TestRecord
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_record_auto.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestRecordAuto < MiniTest::Test
|
11
|
+
|
12
|
+
|
13
|
+
def test_read
|
14
|
+
beers = CsvRecord.read( "#{CsvRecord.test_data_dir}/beer.csv" ).to_a
|
15
|
+
pp beers
|
16
|
+
|
17
|
+
assert_equal 6, beers.size
|
18
|
+
assert_equal 'Andechser Klosterbrauerei', beers[0].brewery
|
19
|
+
assert_equal 'Andechs', beers[0].city
|
20
|
+
assert_equal 'Doppelbock Dunkel', beers[0].name
|
21
|
+
assert_equal '7%', beers[0].abv
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def test_foreach
|
26
|
+
CsvRecord.foreach( "#{CsvRecord.test_data_dir}/beer.csv" ) do |rec|
|
27
|
+
pp rec
|
28
|
+
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
29
|
+
end
|
30
|
+
|
31
|
+
assert true
|
32
|
+
end
|
33
|
+
|
34
|
+
end # class TestRecordAuto
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_version.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestVersion < MiniTest::Test
|
11
|
+
|
12
|
+
def test_version
|
13
|
+
pp CsvRecord::VERSION
|
14
|
+
pp CsvRecord.banner
|
15
|
+
pp CsvRecord.root
|
16
|
+
|
17
|
+
assert true ## assume ok if we get here
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class TestVersion
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csvrecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -57,10 +57,15 @@ files:
|
|
57
57
|
- lib/csvrecord.rb
|
58
58
|
- lib/csvrecord/base.rb
|
59
59
|
- lib/csvrecord/builder.rb
|
60
|
+
- lib/csvrecord/reader.rb
|
60
61
|
- lib/csvrecord/version.rb
|
61
62
|
- test/data/beer.csv
|
63
|
+
- test/data/beer11.csv
|
62
64
|
- test/helper.rb
|
63
|
-
- test/
|
65
|
+
- test/test_reader.rb
|
66
|
+
- test/test_record.rb
|
67
|
+
- test/test_record_auto.rb
|
68
|
+
- test/test_version.rb
|
64
69
|
homepage: https://github.com/csv11/csvrecord
|
65
70
|
licenses:
|
66
71
|
- Public Domain
|