fasterer-csv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README +0 -0
  2. data/lib/fasterer_csv.rb +223 -0
  3. metadata +63 -0
data/README ADDED
File without changes
@@ -0,0 +1,223 @@
1
+ require 'rubygems'
2
+ require 'stringio'
3
+
4
+ module FastererCSV
5
+
6
+ class Table < Array
7
+
8
+ class << self
9
+ def format_headers(unformatted)
10
+ unformatted.map { |header| Row.to_key(header) }
11
+ end
12
+ end
13
+
14
+ attr_reader :headers
15
+
16
+ def initialize(headers, fail_on_malformed_columns = true)
17
+ @headers = Table.format_headers(headers)
18
+ @fail_on_malformed_columns = fail_on_malformed_columns
19
+ end
20
+
21
+ def <<(row)
22
+ if row.class != Row
23
+ row = Row.new(self, row)
24
+ end
25
+ if @headers.length != row.length
26
+ error = "*** WARNING - COLUMN COUNT MISMATCH - WARNING ***\n*** ROW #{size} : EXPECTED #{@headers.length} : FOUND #{row.length}\n\n"
27
+ len = 0
28
+ headers.each do |header|
29
+ len = header.to_s.length if header.to_s.length > len
30
+ end
31
+ headers.each_with_index do |header, i|
32
+ error << sprintf("%-32s : %s\n", header, row[i])
33
+ end
34
+ puts error
35
+ raise error if @fail_on_malformed_columns
36
+ end
37
+ super(row)
38
+ end
39
+
40
+ end
41
+
42
+ class Row < Array
43
+
44
+ class << self
45
+ def to_key(key)
46
+ key = "#{key}".downcase.gsub(/\s+/, '_')
47
+ key.empty? ? :_ : key.to_sym
48
+ end
49
+ end
50
+
51
+ def headers
52
+ @table.headers
53
+ end
54
+
55
+ def initialize(table, array)
56
+ @table = table
57
+ super(array)
58
+ end
59
+
60
+ def [](i)
61
+ if i.class == Fixnum
62
+ super
63
+ else
64
+ found = headers.index(Row::to_key(i))
65
+ found ? super(found) : nil
66
+ end
67
+ end
68
+
69
+ def []=(key, val)
70
+ if key.class == Fixnum
71
+ super
72
+ else
73
+ key = Row::to_key(key)
74
+ headers << key unless headers.include? key
75
+ found = headers.index(key)
76
+ super(found, val)
77
+ end
78
+ end
79
+
80
+ def in_order(columns)
81
+ columns.map do |column_name|
82
+ self[column_name].nil? ? '\N' : self[column_name]
83
+ end
84
+ end
85
+
86
+ def merge(hsh)
87
+ hsh.each do |key, value|
88
+ self[key] = value
89
+ end
90
+ self
91
+ end
92
+
93
+ def to_hash
94
+ headers.inject({}) do |memo, h|
95
+ memo[h] = self[h]
96
+ memo
97
+ end
98
+ end
99
+ end
100
+
101
+ class << self
102
+
103
+ def headers(file, quot = '~', sep = ',', &block)
104
+ parse_headers(File.open(file, 'r') {|io| io.gets }, quot, sep, &block)
105
+ end
106
+
107
+ def read(file, quot = '~', sep = ',', &block)
108
+ parse(File.open(file, 'r') { |io| io.sysread(File.size(file)) }, quot, sep, &block)
109
+ end
110
+
111
+ def parse_headers(data, quot = '~', sep = ',', &block)
112
+ parse(data, quot, sep, &block).headers
113
+ end
114
+
115
+ def parse(data, quot = '~', sep = ',')
116
+ q, s, row, column, inquot, clean, maybe, table, field = quot[0], sep[0], [], [], false, true, false, nil, true
117
+
118
+ data.each_byte do |c|
119
+ next if c == ?\r
120
+
121
+ if maybe && c == s
122
+ row << column.join
123
+ column.clear
124
+ clean, inquot, maybe, field = true, false, false, true
125
+ elsif maybe && c == ?\n && table.nil?
126
+ row << column.join
127
+ column.clear
128
+ table = Table.new(row)
129
+ row, clean, inquot, maybe, field = [], true, false, false, false
130
+ elsif maybe && c == ?\n
131
+ row << column.join
132
+ column.clear
133
+ table << row
134
+ row, clean, inquot, maybe, field = [], true, false, false, false
135
+ elsif clean && c == q
136
+ inquot, clean = true, false
137
+ elsif maybe && c == q
138
+ column << c.chr
139
+ clean, maybe = false, false
140
+ elsif c == q
141
+ maybe = true
142
+ elsif inquot
143
+ column << c.chr
144
+ clean = false
145
+ elsif c == s
146
+ row << (column.empty? ? nil : column.join)
147
+ column.clear
148
+ clean, field = true, true
149
+ elsif c == ?\n && table.nil?
150
+ row << (column.empty? ? nil : column.join)
151
+ column.clear
152
+ table = Table.new(row)
153
+ row, clean, inquot, field = [], true, false, false
154
+ elsif c == ?\n
155
+ row << (column.empty? ? nil : column.join)
156
+ column.clear
157
+ table << row
158
+ row, clean, inquot, field = [], true, false, false
159
+ else
160
+ column << c.chr
161
+ clean = false
162
+ end
163
+ end
164
+
165
+ if !clean
166
+ if maybe
167
+ row << column.join
168
+ else
169
+ row << (column.empty? ? nil :column.join)
170
+ end
171
+ table << row
172
+ elsif field
173
+ row << (column.empty? ? nil : column.join)
174
+ end
175
+
176
+ table.each do |line|
177
+ yield(line)
178
+ end if block_given?
179
+
180
+ table
181
+ end
182
+
183
+ def quot_row(row, q = '~', s = ',')
184
+ row.map do |val|
185
+ if val.nil?
186
+ ""
187
+ else
188
+ val = String(val)
189
+ if val.length == 0
190
+ q * 2
191
+ else
192
+ val[/[#{q}#{s}\n]/] ? q + val.gsub(q, q * 2) + q : val
193
+ end
194
+ end
195
+ end.join(s) + "\n"
196
+ end
197
+
198
+ class IOWriter
199
+ def initialize(file, quot = '~', sep = ',') @io = file; @quot = quot; @sep = sep end
200
+ def <<(row)
201
+ raise "can only write arrays! #{row.class} #{row.inspect}" unless row.class == Array || row.class == Row
202
+ @io.syswrite FastererCSV::quot_row(row, @quot, @sep);
203
+ row
204
+ end
205
+ end
206
+
207
+ def generate(quot = '~', sep = ',', &block)
208
+ builder = StringIO.new
209
+ write(builder, quot, sep, &block)
210
+ builder.string
211
+ end
212
+
213
+ def write(out, quot = '~', sep = ',', &block)
214
+ if out.class == String
215
+ File.open(out, "w") do |io|
216
+ write(io, quot, sep, &block)
217
+ end
218
+ else
219
+ yield(IOWriter.new(out, quot, sep))
220
+ end
221
+ end
222
+ end
223
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fasterer-csv
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Mason
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-03 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: CSV parsing awesomeness
22
+ email: mason@honk.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ files:
30
+ - README
31
+ - lib/fasterer_csv.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/gnovos/fasterer-csv
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.6
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: A faster implementation fo a CSV parser than FasterCSV
62
+ test_files: []
63
+