csvrecord 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7bec20faf657130128c9a70953bc48160b3e1d99
4
- data.tar.gz: 92900195cb8f791286e4d07920b4f5d1ebb4b13a
3
+ metadata.gz: d1458e4244f8a88c335b08c7e4c21ff576ba07fe
4
+ data.tar.gz: c44ca7e8f52667c0a62895c97e86ebe501b18c82
5
5
  SHA512:
6
- metadata.gz: c02849458381b9dd23643f0a6f9358c04984ef69aa5e2b72c61a791b3da7340a431049dd08a0059a09c0c188c5c5fbafedeaf815fe062d9ab4c61f8f704ce75d
7
- data.tar.gz: b79e7e35a4d016ac38cbc281d59a534df054d508d8ccc45130f1b351850622ed0991e59336b564c42aa1919c14da62de973b1e797b44d8ae26e428e80db2a106
6
+ metadata.gz: dabfc7c26f4e69548f2791c9474f4b3579a90c17e14bccfc5f9c0db4285f8e4610b6ef70a4bd6560547a828ff55c4d38235a8201a57f7fb1c615e742c277bf15
7
+ data.tar.gz: 4f5879e6827cf1a1d56c989242bd13b672fe6abf28abff1743219921ed88d55ab238eedf763ff5bf592a07acd9aa0a3e0504673e79fbaeadd931c3e1dd6db7d6
@@ -12,36 +12,94 @@ module CsvRecord
12
12
  attr_reader :name, :type
13
13
 
14
14
  def initialize( name, type )
15
- ## todo: always symbol-ify (to_sym) name and type - why? why not?
15
+ ## note: always symbol-ify (to_sym) name and type
16
+
17
+ ## todo: add title or titles for header field/column title as is e.g. 'Team 1' etc.
18
+ ## incl. numbers only or even an empty header title
16
19
  @name = name.to_sym
17
20
 
18
21
  if type.is_a?( Class )
19
22
  @type = type ## assign class to its own property - why? why not?
20
23
  else
21
- @type = type.to_sym
24
+ @type = Type.registry[type.to_sym]
25
+ if @type.nil?
26
+ puts "!!!! warn unknown type >#{type}< - no class mapping found; add missing type to CsvRecord::Type.registry[]"
27
+ ## todo: fix raise exception!!!!
28
+ end
22
29
  end
23
30
  end
24
31
  end # class Field
25
32
 
26
33
 
27
34
 
35
+ class Type ## todo: use a module - it's just a namespace/module now - why? why not?
36
+
37
+ ## e.g. use Type.registry[:string] = String etc.
38
+ ## note use @@ - there is only one registry
39
+ def self.registry() @@registry ||={} end
40
+
41
+ ## add built-in types:
42
+ registry[:string] = String
43
+ registry[:integer] = Integer ## todo/check: add :number alias for integer? why? why not?
44
+ registry[:float] = Float
45
+ ## todo: add some more
46
+ end # class Type
47
+
48
+
49
+
28
50
  def self.define( &block )
29
51
  builder = Builder.new
30
- builder.instance_eval(&block)
52
+ if block.arity == 1
53
+ block.call( builder )
54
+ ## e.g. allows "yield" dsl style e.g.
55
+ ## CsvRecord.define do |rec|
56
+ ## rec.string :team1
57
+ ## rec.string :team2
58
+ ## end
59
+ ##
60
+ else
61
+ builder.instance_eval( &block )
62
+ ## e.g. shorter "instance eval" dsl style e.g.
63
+ ## CsvRecord.define do
64
+ ## string :team1
65
+ ## string :team2
66
+ ## end
67
+ end
31
68
  builder.to_record
32
69
  end
33
70
 
34
71
 
72
+
35
73
  class Base
36
74
 
37
75
  def self.fields ## note: use class instance variable (@fields and NOT @@fields)!!!! (derived classes get its own copy!!!)
38
76
  @fields ||= []
39
77
  end
40
78
 
79
+ def self.field_names ## rename to header - why? why not?
80
+ ## return header row, that is, all field names in an array
81
+ ## todo: rename to field_names or just names - why? why not?
82
+ ## note: names are (always) symbols!!!
83
+ fields.map {|field| field.name }
84
+ end
85
+
86
+ def self.field_types
87
+ ## note: types are (always) classes!!!
88
+ fields.map {|field| field.type }
89
+ end
90
+
91
+
41
92
 
42
93
  def self.field( name, type=:string )
94
+ field = Field.new( name, type )
95
+ fields << field
96
+
97
+ define_field( field ) ## auto-add getter,setter,parse/typecast
98
+ end
43
99
 
44
- fields << Field.new( name, type )
100
+ def self.define_field( field )
101
+ name = field.name ## note: always assumes a "cleaned-up" (symbol) name
102
+ type = field.type ## note: always assumes a (class) type
45
103
 
46
104
  define_method( name ) do
47
105
  instance_variable_get( "@#{name}" )
@@ -55,27 +113,36 @@ def self.field( name, type=:string )
55
113
  instance_variable_set( "@#{name}", self.class.typecast( value, type ) )
56
114
  end
57
115
  end
58
- def self.add_field( name, type ) field( name, type ); end ## add alias for builder
59
116
 
117
+ ## column/columns aliases for field/fields
118
+ ## use self << with alias_method - possible? works? why? why not?
119
+ def self.column( name, type=:string ) field( name, type ); end
120
+ def self.columns() fields; end
121
+ def self.column_names() field_names; end
122
+ def self.column_types() field_types; end
123
+
124
+
125
+
126
+ def self.typecast( value, type ) ## cast (convert) from string value to type (e.g. float, integer, etc.)
60
127
 
61
128
 
62
- def self.typecast( value, type )
63
129
  ## convert string value to (field) type
64
- if type == :string || type == 'string' || type == String
130
+ if type == String
65
131
  value ## pass through as is
66
- elsif type == :float || type == 'float' || type == Float
67
- ## note: allow/check for nil values
132
+ elsif type == Float
133
+ ## note: allow/check for nil values - why? why not?
68
134
  float = (value.nil? || value.empty?) ? nil : value.to_f
69
135
  puts "typecast >#{value}< to float number >#{float}<"
70
136
  float
71
- elsif type == :number || type == 'number' || type == Integer
137
+ elsif type == Integer
72
138
  number = (value.nil? || value.empty?) ? nil : value.to_i(10) ## always use base10 for now (e.g. 010 => 10 etc.)
73
139
  puts "typecast >#{value}< to integer number >#{number}<"
74
140
  number
75
141
  else
76
- ## raise exception about unknow type
77
- puts "!!!! unknown type >#{type}< - don't know how to convert/typecast string value >#{value}<"
78
- value
142
+ ## raise exception about unknow type
143
+ pp type
144
+ puts "!!!! unknown type >#{type}< - don't know how to convert/typecast string value >#{value}<"
145
+ value
79
146
  end
80
147
  end
81
148
 
@@ -85,21 +152,43 @@ def self.build_hash( values ) ## find a better name - build_attrib? or somethi
85
152
  ## puts "== build_hash:"
86
153
  ## pp values
87
154
 
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
155
+ ## e.g. [[],[]] return zipped pairs in array as (attribute - name/value pair) hash
156
+ Hash[ field_names.zip(values) ]
95
157
  end
96
158
 
97
159
 
160
+
98
161
  def parse( values ) ## use read (from array) or read_values or read_row - why? why not?
99
- h = self.build_hash( values )
162
+
163
+ ## todo/fix:
164
+ ## check if values is a string
165
+ ## use Csv.parse_line to convert to array
166
+ ## otherwise assume array of (string) values
167
+
168
+ h = self.class.build_hash( values )
100
169
  update( h )
101
170
  end
102
171
 
172
+ def to_a
173
+ ## return array of all record values (typed e.g. float, integer, date, ..., that is,
174
+ ## as-is and NOT auto-converted to string
175
+ ## use to_csv or values for all string values)
176
+ self.class.fields.map { |field| send( field.name ) }
177
+ end
178
+
179
+ def to_h ## use to_hash - why? why not? - add attributes alias - why? why not?
180
+ self.class.build_hash( to_a )
181
+ end
182
+
183
+
184
+ def values ## use/rename/alias to to_row too - why? why not?
185
+ ## todo/fix: check for date and use own date to string format!!!!
186
+ to_a.map{ |value| value.to_s }
187
+ end
188
+ ## use values as to_csv alias
189
+ ## - reverse order? e.g. make to_csv an alias of value s- why? why not?
190
+ alias_method :to_csv, :values
191
+
103
192
 
104
193
 
105
194
  def self.parse( txt_or_rows ) ## note: returns an (lazy) enumarator
@@ -120,7 +209,8 @@ def self.parse( txt_or_rows ) ## note: returns an (lazy) enumarator
120
209
  ## else
121
210
  ## pp row.fields
122
211
  ## pp row.to_hash
123
- ## fix/todo: use row.to_hash
212
+ ## fix/todo!!!!!!!!!!!!!
213
+ ## check for CSV::Row etc. - use row.to_hash ?
124
214
  h = build_hash( row.fields )
125
215
  ## pp h
126
216
  rec = new( h )
@@ -11,22 +11,22 @@ class Builder # check: rename to RecordDefinition or RecordDsl or similar - why
11
11
 
12
12
  def field( name, type=:string ) ## note: type defaults to string
13
13
  puts " adding field >#{name}< with type >#{type}<"
14
- @clazz.add_field( name, type ) ## auto-add getter and setter
14
+ @clazz.field( name, type ) ## auto-add getter and setter
15
15
  end
16
16
 
17
17
  def string( name )
18
18
  puts " adding string field >#{name}<"
19
- field( name, 'string' )
19
+ field( name, :string )
20
20
  end
21
21
 
22
- def number( name ) ## use for alias for integer ???
23
- puts " adding number field >#{name}<"
24
- field( name, 'number' )
22
+ def integer( name ) ## use number for alias for integer - why? why not???
23
+ puts " adding integer number field >#{name}<"
24
+ field( name, :integer )
25
25
  end
26
26
 
27
27
  def float( name )
28
28
  puts " adding float number field >#{name}<"
29
- field( name, 'float' )
29
+ field( name, :float )
30
30
  end
31
31
 
32
32
 
@@ -5,7 +5,7 @@ module CsvRecord
5
5
 
6
6
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
7
7
  MINOR = 1
8
- PATCH = 0
8
+ PATCH = 1
9
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
10
10
 
11
11
 
data/test/test_beer.rb CHANGED
@@ -79,6 +79,16 @@ class TestBeer < MiniTest::Test
79
79
  pp clazz2.class.name
80
80
  pp Beer.class.name
81
81
 
82
+ clazz2b = CsvRecord.define do |rec| ## try "yield"-style dsl with block.arity == 1
83
+ rec.string :brewery
84
+ rec.string :city
85
+ rec.string :name
86
+ rec.float :abv
87
+ end
88
+ pp clazz2b
89
+ pp clazz2b.class.name
90
+ pp clazz2b.fields
91
+
82
92
  assert true ## assume ok if we get here
83
93
  end
84
94
 
@@ -101,6 +111,34 @@ class TestBeer < MiniTest::Test
101
111
  pp beers
102
112
 
103
113
  pp BeerClassic.fields
114
+ pp BeerClassic.field_names
115
+ pp BeerClassic.columns ## try fields alias
116
+ pp BeerClassic.column_names ## try field_names alias
117
+
118
+ assert_equal [:brewery, :city, :name, :abv], BeerClassic.field_names
119
+ assert_equal [String, String, String, Float], BeerClassic.field_types
120
+
121
+
122
+ assert_equal ['Andechser Klosterbrauerei',
123
+ 'Andechs',
124
+ 'Doppelbock Dunkel',
125
+ 7.0], beers[0].to_a ## typed values
126
+
127
+ beer_hash = { brewery: 'Andechser Klosterbrauerei',
128
+ city: 'Andechs',
129
+ name: 'Doppelbock Dunkel',
130
+ abv: 7.0 }
131
+ assert_equal beer_hash, beers[0].to_h ## typed name/value pairs (hash)
132
+
133
+ assert_equal ['Andechser Klosterbrauerei',
134
+ 'Andechs',
135
+ 'Doppelbock Dunkel',
136
+ '7.0'], beers[0].values ## all string values
137
+ assert_equal ['Andechser Klosterbrauerei',
138
+ 'Andechs',
139
+ 'Doppelbock Dunkel',
140
+ '7.0'], beers[0].to_csv ## try to_csv alias
141
+
104
142
 
105
143
  beer = BeerClassic.new
106
144
  pp beer
@@ -151,10 +189,26 @@ class TestBeer < MiniTest::Test
151
189
  name: 'Doppelbock Dunkel' )
152
190
  pp beer2
153
191
 
154
- assert_equal nil, beer2.abv
192
+ assert_nil beer2.abv
155
193
  assert_equal 'Andechser Klosterbrauerei', beer2.brewery
156
194
  assert_equal 'Andechs', beer2.city
157
195
  assert_equal 'Doppelbock Dunkel', beer2.name
158
196
  end
159
197
 
198
+ def test_parse
199
+ values = ['Andechser Klosterbrauerei',
200
+ 'Andechs',
201
+ 'Doppelbock Dunkel',
202
+ '7.0']
203
+
204
+ beer = Beer.new
205
+ beer.parse( values )
206
+
207
+ assert_equal values, beer.values
208
+ assert_equal values[0], beer.brewery
209
+ assert_equal values[1], beer.city
210
+ assert_equal values[2], beer.name
211
+ assert_equal values[3].to_f, beer.abv
212
+ end
213
+
160
214
  end # class TestBeer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csvrecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer