motion-csv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWEyYmZjNDU5YzI4ZGFmODBjZmQxODc3OWMyMTEyM2VmZmNjMTUyMw==
5
+ data.tar.gz: !binary |-
6
+ ZmY5N2E0MzM0YjZlZDBkMmNiZjZiM2YwZDRjZDljNjg4Nzg3Y2E4OA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Y2E1NmIyM2E4NDFlM2FlMmMwYmQ3ZDEwODEyN2RmMDhiMGQyYzJhMTYyZDA1
10
+ M2U3MTMwNTUxM2I4ZDViNzFjZWY0MDYyNDU1ZGQ2YmY4MGRiN2EyYzBjMDk0
11
+ ZWJlMTVjYWJiMjViNzE5NGJjZmQwMGZlNjhjNmMyN2NmN2RjYzY=
12
+ data.tar.gz: !binary |-
13
+ YjczNjRhM2NjMTJlMTQ1YWFkMzAwZmRlNmJhZjc1ZjU3NjlhYzBjYjBhNTE1
14
+ NmMzMDA4MGNiZWIzZTUzMjExNDc0MTY0MDdlOTk5ZmExNTE4Njk2MWI4ZGZk
15
+ OTQyYTU3Yzk5MTFmM2E0MDhiOGVkNTQ1MDM5MThlMWZjODRiZjc=
@@ -0,0 +1,41 @@
1
+ # motion-csv
2
+
3
+ This is a RubyMotion friendly port of fasterer-csv by Mason: http://rubygems.org/gems/fasterer-csv
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'motion-csv'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install motion-csv
18
+
19
+ ## Usage
20
+
21
+ Check out the `specs` dorectory for usage examples, but here's a brief example:
22
+
23
+ ```ruby
24
+ csv_string = "a,b,c,d
25
+ 1,2,3,4
26
+ 5,6,7,whatever"
27
+
28
+ csv = MotionCSV.parse(csv_string)
29
+
30
+ puts csv.headers # [:a, :b, :c, :d]
31
+ puts csv.first[:b] # 2
32
+ puts csv.last[:d] # "whatever"
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create new Pull Request
@@ -0,0 +1,8 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
6
+ Motion::Project::App.setup do |app|
7
+ app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-csv/**/*.rb")))
8
+ end
@@ -0,0 +1,427 @@
1
+ module MotionCSV
2
+
3
+ class Table < Array
4
+
5
+ class << self
6
+ def format_headers(unformatted)
7
+ unformatted.map { |header| Row.to_key(header) }
8
+ end
9
+ end
10
+
11
+ attr_reader :headers, :lines, :line_block
12
+
13
+ def initialize(headers, fail_on_malformed_columns = true, &line_block)
14
+ @headers = Table.format_headers(headers)
15
+ @fail_on_malformed_columns = fail_on_malformed_columns
16
+ @line_block = line_block
17
+ @lines = 0
18
+ @indexes = {}
19
+ end
20
+
21
+ def <<(row)
22
+ @lines += 1
23
+ if !row.is_a?(Row)
24
+ row = Row.new(self, row, @lines)
25
+ end
26
+ if @headers.length != row.length
27
+ error = "*** WARNING - COLUMN COUNT MISMATCH - WARNING ***\n*** ROW #{size} : EXPECTED #{@headers.length} : FOUND #{row.length}\n\n"
28
+ len = 0
29
+ headers.each do |header|
30
+ len = header.to_s.length if header.to_s.length > len
31
+ end
32
+ headers.each_with_index do |header, i|
33
+ error << sprintf("%-32s : %s\n", header, row[i])
34
+ end
35
+ puts error
36
+ raise error if @fail_on_malformed_columns
37
+ end
38
+ if line_block
39
+ line_block.call(row)
40
+ else
41
+ super(row)
42
+ end
43
+ end
44
+ alias_method :push, :<<
45
+
46
+ def merge(*tables)
47
+
48
+ tables.each do |table|
49
+ matching = self.headers & table.headers
50
+
51
+ key = {}
52
+
53
+ table.each do |row|
54
+ matching.each do |match|
55
+ key[match] = row[match]
56
+ end
57
+
58
+ self.lookup(key) { |r| r.merge(row) }
59
+ end
60
+ end
61
+
62
+ self
63
+
64
+ end
65
+
66
+ def index(columns, reindex = false)
67
+ columns = columns.compact.uniq.sort { |a, b| a.to_s <=> b.to_s }.map { |column| Row.to_key(column) }
68
+
69
+ key = columns.join('|#|')
70
+
71
+ @indexes[key] ||= {}
72
+
73
+ index = @indexes[key]
74
+
75
+ if reindex || index.empty?
76
+
77
+ self.each do |row|
78
+ vkey = columns.map { |column| row[column] }
79
+ index[vkey] ||= []
80
+ index[vkey] << row
81
+ end
82
+ end
83
+ index
84
+ end
85
+
86
+ def lookup(key)
87
+
88
+ values = []
89
+ columns = key.keys.compact.uniq.sort { |a, b| a.to_s <=> b.to_s }.map do |column|
90
+ values << key[column]
91
+ Row.to_key(column)
92
+ end
93
+
94
+ rows = index(columns)[values]
95
+ if rows && block_given?
96
+ rows.each do |row|
97
+ yield(row)
98
+ end
99
+ end
100
+
101
+ rows
102
+ end
103
+
104
+ def write(file, quot = '"', sep = ',')
105
+ MotionCSV.write(file, quot, sep) do |out|
106
+ out << headers
107
+ each do |row|
108
+ out << row
109
+ end
110
+ end
111
+ end
112
+
113
+ alias_method :rows, :to_a
114
+ alias_method :merge!, :merge
115
+
116
+ end
117
+
118
+ class Row < Array
119
+
120
+ class << self
121
+ def to_key(key)
122
+ key = "#{key}".downcase.gsub(/\s+/, '_')
123
+ key.empty? ? :_ : key.to_sym
124
+ end
125
+ end
126
+
127
+ def headers
128
+ @headers ||= @table.headers.dup
129
+ end
130
+
131
+ attr_reader :line
132
+
133
+ def initialize(table, array, line=-1)
134
+ @table = table
135
+ @line = line
136
+ super(array)
137
+ end
138
+
139
+ def [](*is)
140
+ is.each do |i|
141
+ val = if i.is_a? Fixnum
142
+ super
143
+ else
144
+ found = headers.index(Row::to_key(i))
145
+ found ? super(found) : nil
146
+ end
147
+ return val unless val.nil?
148
+ end
149
+ nil
150
+ end
151
+
152
+ def []=(key, val)
153
+ if key.is_a? Fixnum
154
+ super
155
+ else
156
+ key = Row::to_key(key)
157
+ headers << key unless headers.include? key
158
+ found = headers.index(key)
159
+ super(found, val)
160
+ end
161
+ end
162
+
163
+ def pull(*columns)
164
+ columns.map do |column|
165
+ column = [nil] if column.nil?
166
+ self[*column]
167
+ end
168
+ end
169
+
170
+ def merge(row)
171
+ if row.is_a? Row
172
+ row.headers.each do |header|
173
+ self[header] = row[header]
174
+ end
175
+ else
176
+ row.each do |key, value|
177
+ self[key] = value
178
+ end
179
+ end
180
+ self
181
+ end
182
+
183
+ def to_hash
184
+ headers.inject({}) do |memo, h|
185
+ memo[h] = self[h]
186
+ memo
187
+ end
188
+ end
189
+
190
+ def key?(key)
191
+ keys.include?(Row.to_key(key))
192
+ end
193
+
194
+ def value?(value)
195
+ values.include?(value)
196
+ end
197
+
198
+ def method_missing(method, *args, &block)
199
+ to_hash.send(method, *args, &block)
200
+ end
201
+
202
+ alias_method :keys, :headers
203
+ alias_method :values, :to_a
204
+
205
+ alias_method :has_key?, :key?
206
+ alias_method :member?, :key?
207
+ alias_method :include?, :key?
208
+
209
+ alias_method :has_value?, :value?
210
+ alias_method :merge!, :merge
211
+
212
+ end
213
+
214
+ class NumericConversion < Array
215
+
216
+ def initialize
217
+ @int = @float = true
218
+ @dot = false
219
+ end
220
+
221
+ def clear
222
+ @int = @float = true
223
+ @dot = false
224
+ super
225
+ end
226
+
227
+ def <<(ch)
228
+ if ch == ?-.ord
229
+ @float = @int = size == 0
230
+ elsif (ch > ?9.ord || ch < ?0.ord) && ch != ?..ord
231
+ @int = @float = false
232
+ elsif ch == ?..ord && @dot
233
+ @int = @float = false
234
+ elsif ch == ?..ord
235
+ @int = false
236
+ @dot = true
237
+ end
238
+
239
+ super(ch.chr)
240
+ end
241
+
242
+ def convert(as_string = false)
243
+ if as_string
244
+ join
245
+ elsif empty?
246
+ nil
247
+ elsif @int
248
+ join.to_i
249
+ elsif @float
250
+ join.to_f
251
+ else
252
+ join
253
+ end
254
+ end
255
+
256
+ end
257
+
258
+ class NoConversion < Array
259
+
260
+ def <<(ch)
261
+ super(ch.chr)
262
+ end
263
+
264
+ def convert(as_string = false)
265
+ if as_string
266
+ join
267
+ elsif empty?
268
+ nil
269
+ else
270
+ join
271
+ end
272
+ end
273
+
274
+ end
275
+
276
+ class IOWriter
277
+ def initialize(file, quot = '"', sep = ',', quotenum = false)
278
+ @first = true; @io = file; @quot = quot; @sep = sep; @quotenum = quotenum
279
+ end
280
+
281
+ def <<(row)
282
+ raise "can only write arrays! #{row.class} #{row.inspect}" unless row.is_a? Array
283
+ if @first && row.is_a?(Row)
284
+ self.<<(row.headers)
285
+ end
286
+ @first = false
287
+ @io.syswrite MotionCSV::quot_row(row, @quot, @sep, @quotenum)
288
+ row
289
+ end
290
+ end
291
+
292
+ class << self
293
+
294
+ def headers(file, quot = '"', sep = ',', fail_on_malformed = true, column = NoConversion.new, &block)
295
+ parse_headers(File.open(file, 'r') { |io| io.gets }, quot, sep, fail_on_malformed, column, &block)
296
+ end
297
+
298
+ def read(file, quot = '"', sep = ',', fail_on_malformed = true, column = NoConversion.new, &block)
299
+ File.open(file, 'r') do |io|
300
+ parse(io, quot, sep, fail_on_malformed, column, &block)
301
+ end
302
+ end
303
+
304
+ def convread(file, quot = '"', sep = ',', fail_on_malformed = true, column = NumericConversion.new, &block)
305
+ File.open(file, 'r') do |io|
306
+ parse(io, quot, sep, fail_on_malformed, column, &block)
307
+ end
308
+ end
309
+
310
+ def parse_headers(data, quot = '"', sep = ',', fail_on_malformed = true, column = NoConversion.new, &block)
311
+ parse(data, quot, sep, fail_on_malformed, column, &block).headers
312
+ end
313
+
314
+ def parse(io, quot = '"', sep = ',', fail_on_malformed = true, column = NoConversion.new, &block)
315
+ q, s, row, inquot, clean, maybe, table, field, endline = quot.ord, sep.ord, [], false, true, false, nil, true, false
316
+
317
+ io.each_byte do |c|
318
+ next if c == ?\r.ord
319
+
320
+ if maybe && c == s
321
+ row << column.convert(true)
322
+ column.clear
323
+ clean, inquot, maybe, field, endline = true, false, false, true, false
324
+ elsif maybe && c == ?\n.ord && table.nil?
325
+ row << column.convert(true) unless (column.empty? && endline)
326
+ column.clear
327
+ table = Table.new(row, fail_on_malformed, &block) unless row.empty?
328
+ row, clean, inquot, maybe, field, endline = [], true, false, false, false, true
329
+ elsif maybe && c == ?\n.ord
330
+ row << column.convert(true) unless (column.empty? && endline)
331
+ column.clear
332
+ table << row unless row.empty?
333
+ row, clean, inquot, maybe, field, endline = [], true, false, false, false, true
334
+ elsif clean && c == q
335
+ inquot, clean, endline = true, false, false
336
+ elsif maybe && c == q
337
+ column << c
338
+ clean, maybe, endline = false, false, false
339
+ elsif c == q
340
+ maybe, endline = true, false
341
+ elsif inquot
342
+ column << c
343
+ clean, endline = false, false
344
+ elsif c == s
345
+ row << column.convert(false)
346
+ column.clear
347
+ clean, field, endline = true, true, false
348
+ elsif c == ?\n.ord && table.nil?
349
+
350
+ row << column.convert(false) unless column.empty? && endline
351
+
352
+ column.clear
353
+ table = Table.new(row, fail_on_malformed, &block) unless row.empty?
354
+ row, clean, inquot, field, endline = [], true, false, false, true
355
+ elsif c == ?\n.ord
356
+
357
+ row << column.convert(false) unless column.empty? && endline
358
+
359
+ column.clear
360
+ table << row unless row.empty?
361
+ row, clean, inquot, field, endline = [], true, false, false, true
362
+ else
363
+ column << c
364
+ clean, endline = false, false
365
+ end
366
+ end
367
+
368
+ if !clean
369
+ row << column.convert(maybe)
370
+ if table
371
+ table << row unless row.empty?
372
+ else
373
+ table = Table.new(row, fail_on_malformed, &block) unless row.empty?
374
+ end
375
+ elsif field
376
+ row << column.convert(maybe)
377
+ end
378
+
379
+ table
380
+ end
381
+
382
+ def quot_row(row, q = '"', s = ',', numquot = false)
383
+ num_quot = /(?:[#{q}#{s}\n]|^\d+$)/
384
+ need_quot = /[#{q}#{s}\n]/
385
+ row.map do |val|
386
+ if val.nil?
387
+ ""
388
+ elsif val.is_a? Numeric
389
+ val.to_s
390
+ else
391
+ quot = (val.is_a?(Symbol) || !numquot) ? need_quot : num_quot
392
+ val = String(val)
393
+ if val.length == 0
394
+ q * 2
395
+ else
396
+ val[quot] ? q + val.gsub(q, q * 2) + q : val
397
+ end
398
+ end
399
+ end.join(s) + "\n"
400
+ end
401
+
402
+ def generate(quot = '"', sep = ',', &block)
403
+ builder = StringIO.new
404
+ write(builder, quot, sep, &block)
405
+ builder.string
406
+ end
407
+
408
+ def write(data, quot = '"', sep = ',', quotenum = false, &block)
409
+ out(data, 'w', quot, sep, quotenum, &block)
410
+ end
411
+
412
+ def append(data, quot = '"', sep = ',', quotenum = false, &block)
413
+ out(data, 'a', quot, sep, quotenum, &block)
414
+ end
415
+
416
+ def out(data, mode = 'w', quot = '"', sep = ',', quotenum = false, &block)
417
+ if data.class == String
418
+ File.open(data, mode) do |io|
419
+ out(io, mode, quot, sep, quotenum, &block)
420
+ end
421
+ else
422
+ yield(IOWriter.new(data, quot, sep, quotenum))
423
+ end
424
+ end
425
+
426
+ end
427
+ end
@@ -0,0 +1,3 @@
1
+ module MotionCSV
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motion-csv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mark Rickert
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ prerelease: false
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ type: :development
27
+ description: ! 'This is a RubyMotion friendly port of fasterer-csv by Mason: http://rubygems.org/gems/fasterer-csv'
28
+ email:
29
+ - mjar81@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/motion-csv/motion-csv.rb
36
+ - lib/motion-csv/version.rb
37
+ - lib/motion-csv.rb
38
+ homepage:
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.0.7
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: ! 'This is a RubyMotion friendly port of fasterer-csv by Mason: http://rubygems.org/gems/fasterer-csv'
62
+ test_files: []