csvrecord 0.0.1 → 0.1.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 +5 -0
- data/README.md +114 -1
- data/lib/csvrecord.rb +7 -0
- data/lib/csvrecord/base.rb +161 -0
- data/lib/csvrecord/builder.rb +37 -0
- data/lib/csvrecord/version.rb +5 -4
- data/test/data/beer.csv +7 -0
- data/test/helper.rb +16 -0
- data/test/test_beer.rb +160 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bec20faf657130128c9a70953bc48160b3e1d99
|
4
|
+
data.tar.gz: 92900195cb8f791286e4d07920b4f5d1ebb4b13a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c02849458381b9dd23643f0a6f9358c04984ef69aa5e2b72c61a791b3da7340a431049dd08a0059a09c0c188c5c5fbafedeaf815fe062d9ab4c61f8f704ce75d
|
7
|
+
data.tar.gz: b79e7e35a4d016ac38cbc281d59a534df054d508d8ccc45130f1b351850622ed0991e59336b564c42aa1919c14da62de973b1e797b44d8ae26e428e80db2a106
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,120 @@
|
|
11
11
|
|
12
12
|
## Usage
|
13
13
|
|
14
|
-
|
14
|
+
[`beer.csv`](test/data/beer.csv):
|
15
|
+
|
16
|
+
```
|
17
|
+
Brewery,City,Name,Abv
|
18
|
+
Andechser Klosterbrauerei,Andechs,Doppelbock Dunkel,7%
|
19
|
+
Augustiner Bräu München,München,Edelstoff,5.6%
|
20
|
+
Bayerische Staatsbrauerei Weihenstephan,Freising,Hefe Weissbier,5.4%
|
21
|
+
Brauerei Spezial,Bamberg,Rauchbier Märzen,5.1%
|
22
|
+
Hacker-Pschorr Bräu,München,Münchner Dunkel,5.0%
|
23
|
+
Staatliches Hofbräuhaus München,München,Hofbräu Oktoberfestbier,6.3%
|
24
|
+
```
|
25
|
+
|
26
|
+
Step 1: Define a (typed) struct for the comma-separated values (csv) records. Example:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
require 'csvrecord'
|
30
|
+
|
31
|
+
Beer = CsvRecord.define do
|
32
|
+
field :brewery ## note: default type is :string
|
33
|
+
field :city
|
34
|
+
field :name
|
35
|
+
field :abv, Float ## allows type specified as class (or use :float)
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
or in "classic" style:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class Beer < CsvRecord::Base
|
43
|
+
field :brewery
|
44
|
+
field :city
|
45
|
+
field :name
|
46
|
+
field :abv, Float
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
Step 2: Read in the comma-separated values (csv) datafile. Example:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
beers = Beer.read( 'beer.csv' ).to_a
|
55
|
+
|
56
|
+
puts "#{beers.size} beers:"
|
57
|
+
pp beers
|
58
|
+
```
|
59
|
+
|
60
|
+
pretty prints (pp):
|
61
|
+
|
62
|
+
```
|
63
|
+
6 beers:
|
64
|
+
[#<Beer:0x302c760
|
65
|
+
@abv = 7.0,
|
66
|
+
@brewery = "Andechser Klosterbrauerei",
|
67
|
+
@city = "Andechs",
|
68
|
+
@name = "Doppelbock Dunkel">,
|
69
|
+
#<Beer:0x3026fe8
|
70
|
+
@abv = 5.6,
|
71
|
+
@brewery = "Augustiner Br\u00E4u M\u00FCnchen",
|
72
|
+
@city = "M\u00FCnchen",
|
73
|
+
@name = "Edelstoff">,
|
74
|
+
#<Beer:0x30257a0
|
75
|
+
@abv = 5.4,
|
76
|
+
@brewery = "Bayerische Staatsbrauerei Weihenstephan",
|
77
|
+
@city = "Freising",
|
78
|
+
@name = "Hefe Weissbier">,
|
79
|
+
...
|
80
|
+
]
|
81
|
+
```
|
82
|
+
|
83
|
+
Or loop over the records. Example:
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
Beer.read( data ).each do |rec|
|
87
|
+
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
printing:
|
92
|
+
|
93
|
+
```
|
94
|
+
Doppelbock Dunkel (7.0%) by Andechser Klosterbrauerei, Andechs
|
95
|
+
Edelstoff (5.6%) by Augustiner Bräu München, München
|
96
|
+
Hefe Weissbier (5.4%) by Bayerische Staatsbrauerei Weihenstephan, Freising
|
97
|
+
Rauchbier Märzen (5.1%) by Brauerei Spezial, Bamberg
|
98
|
+
Münchner Dunkel (5.0%) by Hacker-Pschorr Bräu, München
|
99
|
+
Hofbräu Oktoberfestbier (6.3%) by Staatliches Hofbräuhaus München, München
|
100
|
+
```
|
101
|
+
|
102
|
+
|
103
|
+
Or create new records from scratch. Example:
|
104
|
+
|
105
|
+
``` ruby
|
106
|
+
beer = Beer.new( brewery: 'Andechser Klosterbrauerei',
|
107
|
+
city: 'Andechs',
|
108
|
+
name: 'Doppelbock Dunkel' )
|
109
|
+
pp beer
|
110
|
+
|
111
|
+
# -or-
|
112
|
+
|
113
|
+
beer = Beer.new
|
114
|
+
beer.update( abv: 12.7 )
|
115
|
+
beer.update( brewery: 'Andechser Klosterbrauerei',
|
116
|
+
city: 'Andechs',
|
117
|
+
name: 'Doppelbock Dunkel' )
|
118
|
+
|
119
|
+
# -or-
|
120
|
+
|
121
|
+
beer.abv = 12.7
|
122
|
+
beer.name = 'Doppelbock Dunkel'
|
123
|
+
beer.brewery = 'Andechser Klosterbrauerei'
|
124
|
+
```
|
125
|
+
|
126
|
+
|
127
|
+
And so on. That's it.
|
15
128
|
|
16
129
|
|
17
130
|
|
data/lib/csvrecord.rb
CHANGED
@@ -0,0 +1,161 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module CsvRecord
|
5
|
+
|
6
|
+
## note on naming:
|
7
|
+
## use naming convention from awk and tabular data package/json schema for now
|
8
|
+
## use - records (use rows for "raw" untyped (string) data rows )
|
9
|
+
## - fields (NOT columns or attributes) -- might add an alias later - why? why not?
|
10
|
+
|
11
|
+
class Field ## ruby record class field
|
12
|
+
attr_reader :name, :type
|
13
|
+
|
14
|
+
def initialize( name, type )
|
15
|
+
## todo: always symbol-ify (to_sym) name and type - why? why not?
|
16
|
+
@name = name.to_sym
|
17
|
+
|
18
|
+
if type.is_a?( Class )
|
19
|
+
@type = type ## assign class to its own property - why? why not?
|
20
|
+
else
|
21
|
+
@type = type.to_sym
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end # class Field
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
def self.define( &block )
|
29
|
+
builder = Builder.new
|
30
|
+
builder.instance_eval(&block)
|
31
|
+
builder.to_record
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Base
|
36
|
+
|
37
|
+
def self.fields ## note: use class instance variable (@fields and NOT @@fields)!!!! (derived classes get its own copy!!!)
|
38
|
+
@fields ||= []
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def self.field( name, type=:string )
|
43
|
+
|
44
|
+
fields << Field.new( name, type )
|
45
|
+
|
46
|
+
define_method( name ) do
|
47
|
+
instance_variable_get( "@#{name}" )
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method( "#{name}=" ) do |value|
|
51
|
+
instance_variable_set( "@#{name}", value )
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method( "parse_#{name}") do |value|
|
55
|
+
instance_variable_set( "@#{name}", self.class.typecast( value, type ) )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def self.add_field( name, type ) field( name, type ); end ## add alias for builder
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
def self.typecast( value, type )
|
63
|
+
## convert string value to (field) type
|
64
|
+
if type == :string || type == 'string' || type == String
|
65
|
+
value ## pass through as is
|
66
|
+
elsif type == :float || type == 'float' || type == Float
|
67
|
+
## note: allow/check for nil values
|
68
|
+
float = (value.nil? || value.empty?) ? nil : value.to_f
|
69
|
+
puts "typecast >#{value}< to float number >#{float}<"
|
70
|
+
float
|
71
|
+
elsif type == :number || type == 'number' || type == Integer
|
72
|
+
number = (value.nil? || value.empty?) ? nil : value.to_i(10) ## always use base10 for now (e.g. 010 => 10 etc.)
|
73
|
+
puts "typecast >#{value}< to integer number >#{number}<"
|
74
|
+
number
|
75
|
+
else
|
76
|
+
## raise exception about unknow type
|
77
|
+
puts "!!!! unknown type >#{type}< - don't know how to convert/typecast string value >#{value}<"
|
78
|
+
value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def self.build_hash( values ) ## find a better name - build_attrib? or something?
|
84
|
+
## convert to key-value (attribute) pairs
|
85
|
+
## puts "== build_hash:"
|
86
|
+
## pp values
|
87
|
+
|
88
|
+
h = {}
|
89
|
+
values.each_with_index do |value,i|
|
90
|
+
field = fields[i]
|
91
|
+
## pp field
|
92
|
+
h[ field.name ] = value
|
93
|
+
end
|
94
|
+
h
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def parse( values ) ## use read (from array) or read_values or read_row - why? why not?
|
99
|
+
h = self.build_hash( values )
|
100
|
+
update( h )
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
def self.parse( txt_or_rows ) ## note: returns an (lazy) enumarator
|
106
|
+
if txt_or_rows.is_a? String
|
107
|
+
txt = txt_or_rows
|
108
|
+
rows = CSV.parse( txt, headers: true )
|
109
|
+
else
|
110
|
+
### todo/fix: use only self.create( array-like ) for array-like data - why? why not?
|
111
|
+
rows = txt_or_rows ## assume array-like records that responds to :each
|
112
|
+
end
|
113
|
+
|
114
|
+
pp rows
|
115
|
+
|
116
|
+
Enumerator.new do |yielder|
|
117
|
+
rows.each do |row|
|
118
|
+
## check - check for to_h - why? why not? supported/built-into by CSV::Row??
|
119
|
+
## if row.respond_to?( :to_h )
|
120
|
+
## else
|
121
|
+
## pp row.fields
|
122
|
+
## pp row.to_hash
|
123
|
+
## fix/todo: use row.to_hash
|
124
|
+
h = build_hash( row.fields )
|
125
|
+
## pp h
|
126
|
+
rec = new( h )
|
127
|
+
## end
|
128
|
+
yielder.yield( rec )
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def self.read( path ) ## not returns an enumarator
|
135
|
+
txt = File.open( path, 'r:utf-8' ).read
|
136
|
+
parse( txt )
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
def initialize( **kwargs )
|
142
|
+
update( kwargs )
|
143
|
+
end
|
144
|
+
|
145
|
+
def update( **kwargs )
|
146
|
+
pp kwargs
|
147
|
+
kwargs.each do |name,value|
|
148
|
+
## note: only convert/typecast string values
|
149
|
+
if value.is_a?( String )
|
150
|
+
send( "parse_#{name}", value ) ## note: use parse_<name> setter (for typecasting)
|
151
|
+
else ## use "regular" plain/classic attribute setter
|
152
|
+
send( "#{name}=", value )
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
## todo: check if args.first is an array (init/update from array)
|
157
|
+
self ## return self for chaining
|
158
|
+
end
|
159
|
+
|
160
|
+
end # class Base
|
161
|
+
end # module CsvRecord
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
## (record) builder mini language / domain-specific language (dsl)
|
4
|
+
|
5
|
+
module CsvRecord
|
6
|
+
|
7
|
+
class Builder # check: rename to RecordDefinition or RecordDsl or similar - why? why not?
|
8
|
+
def initialize
|
9
|
+
@clazz = Class.new(Base)
|
10
|
+
end
|
11
|
+
|
12
|
+
def field( name, type=:string ) ## note: type defaults to string
|
13
|
+
puts " adding field >#{name}< with type >#{type}<"
|
14
|
+
@clazz.add_field( name, type ) ## auto-add getter and setter
|
15
|
+
end
|
16
|
+
|
17
|
+
def string( name )
|
18
|
+
puts " adding string field >#{name}<"
|
19
|
+
field( name, 'string' )
|
20
|
+
end
|
21
|
+
|
22
|
+
def number( name ) ## use for alias for integer ???
|
23
|
+
puts " adding number field >#{name}<"
|
24
|
+
field( name, 'number' )
|
25
|
+
end
|
26
|
+
|
27
|
+
def float( name )
|
28
|
+
puts " adding float number field >#{name}<"
|
29
|
+
field( name, 'float' )
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def to_record ## check: rename to just record or obj or finish or end something?
|
34
|
+
@clazz
|
35
|
+
end
|
36
|
+
end # class Builder
|
37
|
+
end # module CsvRecord
|
data/lib/csvrecord/version.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
module CsvRecord
|
5
5
|
|
6
6
|
MAJOR = 0 ## todo: namespace inside version or something - why? why not??
|
7
|
-
MINOR =
|
8
|
-
PATCH =
|
7
|
+
MINOR = 1
|
8
|
+
PATCH = 0
|
9
9
|
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
10
10
|
|
11
|
+
|
11
12
|
def self.version
|
12
13
|
VERSION
|
13
14
|
end
|
data/test/data/beer.csv
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Brewery,City,Name,Abv
|
2
|
+
Andechser Klosterbrauerei,Andechs,Doppelbock Dunkel,7%
|
3
|
+
Augustiner Bräu München,München,Edelstoff,5.6%
|
4
|
+
Bayerische Staatsbrauerei Weihenstephan,Freising,Hefe Weissbier,5.4%
|
5
|
+
Brauerei Spezial,Bamberg,Rauchbier Märzen,5.1%
|
6
|
+
Hacker-Pschorr Bräu,München,Münchner Dunkel,5.0%
|
7
|
+
Staatliches Hofbräuhaus München,München,Hofbräu Oktoberfestbier,6.3%
|
data/test/helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
## $:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
## minitest setup
|
4
|
+
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
|
8
|
+
## our own code
|
9
|
+
require 'csvrecord'
|
10
|
+
|
11
|
+
## add test_data_dir helper
|
12
|
+
module CsvRecord
|
13
|
+
def self.test_data_dir
|
14
|
+
"#{root}/test/data"
|
15
|
+
end
|
16
|
+
end
|
data/test/test_beer.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_beer.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
class TestBeer < 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
|
+
|
21
|
+
def test_class_style1
|
22
|
+
clazz1 = CsvRecord.define do
|
23
|
+
field :brewery, :string # fix: do NOT use 'Brewery' - name SHOULD be a valid variable name
|
24
|
+
field :city, :string
|
25
|
+
field :name ## default type is :string
|
26
|
+
field :abv, Float ## allow type specified as class
|
27
|
+
end
|
28
|
+
pp clazz1
|
29
|
+
pp clazz1.fields
|
30
|
+
|
31
|
+
assert true ## assume ok if we get here
|
32
|
+
end
|
33
|
+
|
34
|
+
Beer = CsvRecord.define do
|
35
|
+
string :brewery # fix: do NOT use 'Brewery' - name SHOULD be a valid variable name
|
36
|
+
string :city
|
37
|
+
string :name
|
38
|
+
float :abv
|
39
|
+
end
|
40
|
+
|
41
|
+
class BeerClassic < CsvRecord::Base
|
42
|
+
field :brewery
|
43
|
+
field :city
|
44
|
+
field :name
|
45
|
+
field :abv, Float
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def test_class_style2
|
50
|
+
clazz2 = CsvRecord.define do
|
51
|
+
string :brewery # fix: do NOT use 'Brewery' - name SHOULD be a valid variable name
|
52
|
+
string :city
|
53
|
+
string :name
|
54
|
+
float :abv
|
55
|
+
end
|
56
|
+
pp clazz2
|
57
|
+
pp clazz2.class.name
|
58
|
+
pp clazz2.fields
|
59
|
+
|
60
|
+
txt = File.open( "#{CsvRecord.test_data_dir}/beer.csv", 'r:utf-8' ).read
|
61
|
+
data = CSV.parse( txt, headers: true )
|
62
|
+
pp data
|
63
|
+
pp data.to_a ## note: includes header (first row with field names)
|
64
|
+
|
65
|
+
puts "== parse( data ).to_a:"
|
66
|
+
pp clazz2.parse( data ).to_a
|
67
|
+
pp Beer.parse( data ).to_a
|
68
|
+
|
69
|
+
puts "== parse( data ).each:"
|
70
|
+
## loop over records
|
71
|
+
clazz2.parse( data ).each do |rec|
|
72
|
+
puts "#{rec.name} (#{rec.abv}%) by #{rec.brewery}, #{rec.city}"
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "== parse( txt ).to_a:"
|
76
|
+
pp Beer.parse( txt ).to_a
|
77
|
+
|
78
|
+
pp clazz2.class.name
|
79
|
+
pp clazz2.class.name
|
80
|
+
pp Beer.class.name
|
81
|
+
|
82
|
+
assert true ## assume ok if we get here
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def test_read
|
87
|
+
puts "== read( data ).to_a:"
|
88
|
+
beers = Beer.read( "#{CsvRecord.test_data_dir}/beer.csv" ).to_a
|
89
|
+
puts "#{beers.size} beers:"
|
90
|
+
pp beers
|
91
|
+
|
92
|
+
pp Beer.fields
|
93
|
+
|
94
|
+
assert true ## assume ok if we get here
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_classic
|
98
|
+
puts "== read( data ).to_a:"
|
99
|
+
beers = BeerClassic.read( "#{CsvRecord.test_data_dir}/beer.csv" ).to_a
|
100
|
+
puts "#{beers.size} beers:"
|
101
|
+
pp beers
|
102
|
+
|
103
|
+
pp BeerClassic.fields
|
104
|
+
|
105
|
+
beer = BeerClassic.new
|
106
|
+
pp beer
|
107
|
+
beer.update( abv: 12.7 )
|
108
|
+
beer.update( brewery: 'Andechser Klosterbrauerei',
|
109
|
+
city: 'Andechs',
|
110
|
+
name: 'Doppelbock Dunkel' )
|
111
|
+
pp beer
|
112
|
+
|
113
|
+
assert_equal 12.7, beer.abv
|
114
|
+
assert_equal 'Andechser Klosterbrauerei', beer.brewery
|
115
|
+
assert_equal 'Andechs', beer.city
|
116
|
+
assert_equal 'Doppelbock Dunkel', beer.name
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def test_new
|
121
|
+
beer = Beer.new
|
122
|
+
pp beer
|
123
|
+
beer.update( abv: 12.7 )
|
124
|
+
beer.update( brewery: 'Andechser Klosterbrauerei',
|
125
|
+
city: 'Andechs',
|
126
|
+
name: 'Doppelbock Dunkel' )
|
127
|
+
pp beer
|
128
|
+
|
129
|
+
assert_equal 12.7, beer.abv
|
130
|
+
assert_equal 'Andechser Klosterbrauerei', beer.brewery
|
131
|
+
assert_equal 'Andechs', beer.city
|
132
|
+
assert_equal 'Doppelbock Dunkel', beer.name
|
133
|
+
|
134
|
+
|
135
|
+
pp beer.abv
|
136
|
+
pp beer.abv = 12.7
|
137
|
+
pp beer.abv
|
138
|
+
assert_equal 12.7, beer.abv
|
139
|
+
|
140
|
+
pp beer.parse_abv( '12.8%' ) ## (auto-)converts/typecasts string to specified type (e.g. float)
|
141
|
+
assert_equal 12.8, beer.abv
|
142
|
+
|
143
|
+
pp beer.name
|
144
|
+
pp beer.name = 'Doppelbock Dunkel'
|
145
|
+
pp beer.name
|
146
|
+
assert_equal 'Doppelbock Dunkel', beer.name
|
147
|
+
|
148
|
+
|
149
|
+
beer2 = Beer.new( brewery: 'Andechser Klosterbrauerei',
|
150
|
+
city: 'Andechs',
|
151
|
+
name: 'Doppelbock Dunkel' )
|
152
|
+
pp beer2
|
153
|
+
|
154
|
+
assert_equal nil, beer2.abv
|
155
|
+
assert_equal 'Andechser Klosterbrauerei', beer2.brewery
|
156
|
+
assert_equal 'Andechs', beer2.city
|
157
|
+
assert_equal 'Doppelbock Dunkel', beer2.name
|
158
|
+
end
|
159
|
+
|
160
|
+
end # class TestBeer
|
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.0
|
4
|
+
version: 0.1.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-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -55,7 +55,12 @@ files:
|
|
55
55
|
- README.md
|
56
56
|
- Rakefile
|
57
57
|
- lib/csvrecord.rb
|
58
|
+
- lib/csvrecord/base.rb
|
59
|
+
- lib/csvrecord/builder.rb
|
58
60
|
- lib/csvrecord/version.rb
|
61
|
+
- test/data/beer.csv
|
62
|
+
- test/helper.rb
|
63
|
+
- test/test_beer.rb
|
59
64
|
homepage: https://github.com/csv11/csvrecord
|
60
65
|
licenses:
|
61
66
|
- Public Domain
|