csvrecord 0.2.0 → 0.3.0
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 +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
|