csvrecord 0.0.1 → 0.1.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 +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
|