csv-autoparser 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/csv/autoparser.rb +29 -24
- data/lib/csv/autoparser/version.rb +1 -1
- data/test/test_csv/autoparser.rb +33 -3
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44a33cdb6c52087bcfe3667389cdcccab742a9d5
|
4
|
+
data.tar.gz: 1900f568e8c11343e69d8dc05a7ae8ea8bfe0304
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 095abf30f884f42fd177fbf97b17f9aca67cf56fc629854b2dd2a97571c0e2109ce3c6b2d1c717298686ace768de7bd991579db304ba29830c40cdd6329ecd70
|
7
|
+
data.tar.gz: de518ddbd0e8119ce170fad55ad36194d073c746ac8dcbd68fa9237683c679b05fdf950da8549b815d4c2bc14a8ee1063d4fdfdecae0cb51b0bbeaf5b92da220
|
data/lib/csv/autoparser.rb
CHANGED
@@ -4,6 +4,7 @@ require "csv/autoparser/version"
|
|
4
4
|
class CSV
|
5
5
|
|
6
6
|
class Row
|
7
|
+
attr_reader :line_number
|
7
8
|
alias_method :orig_initialize, :initialize
|
8
9
|
# Defines method style accessors based on header row names.
|
9
10
|
def initialize(*args)
|
@@ -27,17 +28,17 @@ class CSV
|
|
27
28
|
# The rows found before the header row are paired with file and line information. These
|
28
29
|
# objects are available through CSV::AutoParser#pre_header_rows.
|
29
30
|
class PreHeaderRow < Array
|
30
|
-
attr_reader :
|
31
|
+
attr_reader :line_number
|
31
32
|
def self.create original_row, file, line
|
32
33
|
row = PreHeaderRow.new(original_row)
|
33
|
-
row.instance_eval { @
|
34
|
+
row.instance_eval { @line_number = line }
|
34
35
|
return row
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
39
|
class HeaderRowNotFound < RuntimeError; end
|
39
40
|
|
40
|
-
attr_reader :pre_header_rows, :header_line_number
|
41
|
+
attr_reader :pre_header_rows, :header_line_number, :file_path
|
41
42
|
|
42
43
|
# +data+ can be path of CSV file in addition to a CSV String or an IO object like CSV.new.
|
43
44
|
# All CSV.new options are supported via +opts+. If an +&is_header+ block is provided, it
|
@@ -48,45 +49,49 @@ class CSV
|
|
48
49
|
@header_line_number = nil
|
49
50
|
@pre_header_rows = []
|
50
51
|
@optional_headers = [opts.delete(:optional_headers)].flatten.compact
|
51
|
-
if data.is_a?(
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
@data_io = if data.is_a?(IO) or data.is_a?(StringIO)
|
53
|
+
data
|
54
|
+
elsif data.is_a?(String)
|
55
|
+
if File.exists?(data)
|
56
|
+
File.open(@file_path = File.expand_path(data), binmode: true)
|
57
|
+
else
|
58
|
+
StringIO.new(data)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise ArgumentError, "data must be a path to a CSV file, a CSV formatted String, or an IO object."
|
62
|
+
end
|
55
63
|
if block_given?
|
56
|
-
|
57
|
-
data
|
58
|
-
elsif data.is_a?(String)
|
59
|
-
StringIO.new(data)
|
60
|
-
else
|
61
|
-
raise ArgumentError, "data must be a path to a CSV file, a CSV formatted String, or an IO object."
|
62
|
-
end
|
63
|
-
header_pos = data_io.pos
|
64
|
+
header_pos = @data_io.pos
|
64
65
|
csv_line_number = 0
|
65
|
-
header_finder = CSV.new(data_io, opts.merge(:headers => false)).each do |row|
|
66
|
+
header_finder = CSV.new(@data_io, opts.merge(:headers => false)).each do |row|
|
66
67
|
csv_line_number += 1
|
67
68
|
if is_header.call(csv_line_number, row)
|
68
69
|
@header_line_number = csv_line_number
|
69
70
|
break
|
70
71
|
else
|
71
|
-
@pre_header_rows << CSV::AutoParser::PreHeaderRow.create(row,
|
72
|
+
@pre_header_rows << CSV::AutoParser::PreHeaderRow.create(row, @file_path, csv_line_number)
|
72
73
|
end
|
73
|
-
header_pos = data_io.pos
|
74
|
+
header_pos = @data_io.pos
|
74
75
|
end
|
75
|
-
raise HeaderRowNotFound, "Could not find header row#{
|
76
|
-
data_io.seek header_pos
|
77
|
-
data_io = StringIO.new(data_io.read)
|
78
|
-
super(data_io, opts.merge(:headers => true))
|
76
|
+
raise HeaderRowNotFound, "Could not find header row#{@file_path ? " in #{@file_path}" : "" }." if @header_line_number.nil?
|
77
|
+
@data_io.seek header_pos
|
78
|
+
@data_io = StringIO.new(@data_io.read)
|
79
|
+
super(@data_io, opts.merge(:headers => true))
|
79
80
|
else
|
80
81
|
@header_line_number = 1 if opts[:headers] == :first_row or opts[:headers] == true
|
81
|
-
super(
|
82
|
+
super(@data_io, opts)
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
85
86
|
alias_method :orig_shift, :shift
|
86
87
|
|
87
|
-
# Overriden to add methods for optional headers which were not present in the CSV.
|
88
|
+
# Overriden to add methods for optional headers which were not present in the CSV. Also,
|
89
|
+
# sets a rows @file_path and @line_number.
|
88
90
|
def shift
|
89
91
|
row = orig_shift
|
92
|
+
if row and row.is_a?(Row) and row.line_number.nil? # sometimes nil. sometimes not a row. sometimes row is repeated.
|
93
|
+
row.instance_variable_set(:@line_number, @data_io.lineno + (@header_line_number || 1) - 1) #if @data_io
|
94
|
+
end
|
90
95
|
[@optional_headers].flatten.compact.each do |h|
|
91
96
|
method_name = self.class.convert_header_to_method_name(h)
|
92
97
|
unless row.respond_to? method_name
|
data/test/test_csv/autoparser.rb
CHANGED
@@ -30,8 +30,25 @@ describe CSV::AutoParser do
|
|
30
30
|
parser.header_line_number.must_equal 3
|
31
31
|
parser.pre_header_rows.first.last.must_equal "years of age"
|
32
32
|
parser.pre_header_rows.last.first.must_equal "bob"
|
33
|
-
|
34
|
-
|
33
|
+
end
|
34
|
+
|
35
|
+
it "it will give file path and line number" do
|
36
|
+
csv_path = fixture_file_path('persons.csv')
|
37
|
+
parser = CSV::AutoParser.new(csv_path) do |line_num, header_row|
|
38
|
+
["name", "Job title"].all? {|cell| header_row.include?(cell) }
|
39
|
+
end
|
40
|
+
line_number = 1
|
41
|
+
parser.file_path.must_equal csv_path
|
42
|
+
parser.pre_header_rows.each do |row|
|
43
|
+
row.line_number.must_equal line_number
|
44
|
+
line_number += 1
|
45
|
+
end
|
46
|
+
table = parser.read
|
47
|
+
line_number = parser.header_line_number + 1
|
48
|
+
table.each do |row|
|
49
|
+
row.line_number.must_equal line_number
|
50
|
+
line_number += 1
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
it "will raise an exception if it can't find the header row" do
|
@@ -95,7 +112,12 @@ describe CSV::AutoParser do
|
|
95
112
|
end
|
96
113
|
|
97
114
|
it "should work just like CSV.new when not passed a block except that it can now take a file path as data too" do
|
98
|
-
input_objects = [
|
115
|
+
input_objects = [
|
116
|
+
fixture_file_path('persons.csv'),
|
117
|
+
File.open(fixture_file_path('persons.csv')),
|
118
|
+
File.read(fixture_file_path('persons.csv')),
|
119
|
+
StringIO.new(File.read(fixture_file_path('persons.csv')))
|
120
|
+
]
|
99
121
|
input_objects.each do |obj|
|
100
122
|
parser = CSV::AutoParser.new(obj, header_converters: :symbol, headers: :first_row)
|
101
123
|
parser.header_line_number.must_equal 1
|
@@ -105,14 +127,22 @@ describe CSV::AutoParser do
|
|
105
127
|
table.first[:fullname].must_equal "bob"
|
106
128
|
# method names are based off of converted header names!
|
107
129
|
table.first.fullname.must_equal "bob"
|
130
|
+
if obj.is_a?(String) and File.exists?(obj)
|
131
|
+
parser.file_path.must_equal fixture_file_path('persons.csv')
|
132
|
+
else
|
133
|
+
parser.file_path.must_be_nil
|
134
|
+
end
|
135
|
+
table.first.line_number.must_equal 2
|
108
136
|
end
|
109
137
|
parser = CSV::AutoParser.new(fixture_file_path('persons.csv'), header_converters: :symbol, headers: "first_col,second_col,third_col,fourth_col")
|
110
138
|
parser.header_line_number.must_equal nil
|
111
139
|
parser.pre_header_rows.must_be_empty
|
140
|
+
parser.file_path.must_equal fixture_file_path('persons.csv')
|
112
141
|
table = parser.read
|
113
142
|
table.length.must_equal 6
|
114
143
|
table[0].first_col.must_equal "full-name"
|
115
144
|
table[1].first_col.must_equal "bob"
|
145
|
+
table[1].line_number.must_equal 2
|
116
146
|
end
|
117
147
|
|
118
148
|
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv-autoparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Delsol
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description: ''
|
@@ -44,7 +44,7 @@ executables: []
|
|
44
44
|
extensions: []
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
|
-
-
|
47
|
+
- .gitignore
|
48
48
|
- LICENSE.txt
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
@@ -66,17 +66,17 @@ require_paths:
|
|
66
66
|
- lib
|
67
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- -
|
69
|
+
- - '>='
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0'
|
72
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - '>='
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
78
|
rubyforge_project:
|
79
|
-
rubygems_version: 2.
|
79
|
+
rubygems_version: 2.0.3
|
80
80
|
signing_key:
|
81
81
|
specification_version: 4
|
82
82
|
summary: Can parse a CSV file automatically given a user specified header row.
|