rgarner-csv-mapper 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +16 -0
- data/Rakefile +7 -7
- data/VERSION +1 -1
- data/lib/csv-mapper/row_map.rb +7 -1
- data/lib/csv-mapper.rb +9 -1
- data/spec/csv-mapper_spec.rb +82 -63
- data/spec/spec_helper.rb +0 -6
- data/spec/test_with_pushed_down_header.csv +8 -0
- metadata +46 -73
- data/.gitignore +0 -4
data/README.rdoc
CHANGED
@@ -35,6 +35,22 @@ The following example will import a CSV file to an Array of Struct[http://www.ru
|
|
35
35
|
results.first.last_name # Doe
|
36
36
|
results.first.age # 27
|
37
37
|
|
38
|
+
==== Named Columns Example
|
39
|
+
Columns which aren't mentioned won't appear in the results.
|
40
|
+
|
41
|
+
# Don't mention first name
|
42
|
+
results = CsvMapper.import('/path/to/file_with_header_row.csv') do
|
43
|
+
named_columns
|
44
|
+
|
45
|
+
surname('last_name')
|
46
|
+
age
|
47
|
+
end
|
48
|
+
|
49
|
+
results.first.surname # Doe
|
50
|
+
results.first.age # 27
|
51
|
+
results.first.first_name # nil
|
52
|
+
results.first.last_name # nil
|
53
|
+
|
38
54
|
==== Import to ActiveRecord Example
|
39
55
|
Although CsvMapper has no dependency on ActiveRecord; it's easy to import a CSV file to ActiveRecord models and save them.
|
40
56
|
|
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ begin
|
|
10
10
|
gem.email = "rgarner@zephyros-systems.co.uk"
|
11
11
|
gem.homepage = "http://github.com/rgarner/csv-mapper"
|
12
12
|
gem.authors = ["Luke Pillow", "Russell Garner"]
|
13
|
-
gem.add_development_dependency "rspec", ">=
|
13
|
+
gem.add_development_dependency "rspec", ">= 2.0.0"
|
14
14
|
gem.add_dependency "fastercsv"
|
15
15
|
gem.extra_rdoc_files << "History.txt"
|
16
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
@@ -20,13 +20,13 @@ rescue LoadError
|
|
20
20
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
21
|
end
|
22
22
|
|
23
|
-
require '
|
24
|
-
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
25
25
|
spec.libs << 'lib' << 'spec'
|
26
|
-
spec.
|
26
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
30
30
|
spec.libs << 'lib' << 'spec'
|
31
31
|
spec.pattern = 'spec/**/*_spec.rb'
|
32
32
|
spec.rcov = true
|
@@ -36,8 +36,8 @@ task :spec => :check_dependencies
|
|
36
36
|
|
37
37
|
task :default => :spec
|
38
38
|
|
39
|
-
require '
|
40
|
-
|
39
|
+
require 'rdoc/task'
|
40
|
+
RDoc::Task.new do |rdoc|
|
41
41
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
42
|
|
43
43
|
rdoc.rdoc_dir = 'rdoc'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/lib/csv-mapper/row_map.rb
CHANGED
@@ -184,8 +184,14 @@ module CsvMapper
|
|
184
184
|
end
|
185
185
|
|
186
186
|
def iterate_headers
|
187
|
-
attributes = FasterCSV.new(@csv_data, @parser_options).readline
|
188
187
|
@start_at_row = [ @start_at_row, 1 ].max
|
188
|
+
|
189
|
+
csv = FasterCSV.new(@csv_data, @parser_options)
|
190
|
+
|
191
|
+
# Header is for now assumed to be one row above data
|
192
|
+
(@start_at_row - 1).times { csv.readline } if @start_at_row > 1
|
193
|
+
|
194
|
+
attributes = csv.readline
|
189
195
|
@csv_data.rewind
|
190
196
|
attributes.each_with_index { |name, index| yield name, index }
|
191
197
|
end
|
data/lib/csv-mapper.rb
CHANGED
@@ -45,7 +45,8 @@ end
|
|
45
45
|
#
|
46
46
|
# ===== The Basics
|
47
47
|
# * +map_to+ - Override the default Struct target. Accepts a class and an optional hash of default attribute names and values.
|
48
|
-
# * +
|
48
|
+
# * +named_columns+ - Enables named columns mode. Headers are required. See "Named Column Mappings" below.
|
49
|
+
# * +start_at_row+ - Specify what row to begin parsing at. Use this to skip headers. When +named_columns+ is used, headers are assumed to be one line above this row.
|
49
50
|
# * +before_row+ - Accepts an Array of method name symbols or lambdas to be invoked before parsing each row.
|
50
51
|
# * +after_row+ - Accepts an Array of method name symbols or lambdas to be invoked after parsing each row.
|
51
52
|
# * +delimited_by+ - Accepts a character to be used to delimit columns. Use this to specify pipe-delimited files.
|
@@ -70,6 +71,13 @@ end
|
|
70
71
|
# Attribute mapping declarations and "modifiers" may be chained
|
71
72
|
# foo.at(4).map :some_transform
|
72
73
|
#
|
74
|
+
# === Named Columns Mappings
|
75
|
+
# When +named_columns+ is called, column names will be read from one row above +start_at_row+. This allows
|
76
|
+
# you to map cell properties to named columns in the CSV. For example:
|
77
|
+
# surname('Last Name') # Where 'Last Name' is the name of a column in the CSV
|
78
|
+
#
|
79
|
+
# Columns which go unmentioned will be omitted from the results.
|
80
|
+
#
|
73
81
|
# === Create Reusable Mappings
|
74
82
|
# The +import+ method accepts an instance of RowMap as an optional mapping parameter.
|
75
83
|
# The easiest way to create an instance of a RowMap is by using +map_csv+.
|
data/spec/csv-mapper_spec.rb
CHANGED
@@ -2,47 +2,48 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
2
|
|
3
3
|
describe CsvMapper do
|
4
4
|
describe "included" do
|
5
|
-
before(:each) do
|
5
|
+
before(:each) do
|
6
6
|
@mapped_klass = Class.new do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
include CsvMapper
|
8
|
+
|
9
|
+
def upcase_name(row, index)
|
10
|
+
row[index].upcase
|
11
|
+
end
|
12
|
+
end
|
12
13
|
@mapped = @mapped_klass.new
|
13
14
|
end
|
14
15
|
|
15
16
|
it "should allow the creation of CSV mappings" do
|
16
17
|
mapping = @mapped.map_csv do
|
17
|
-
start_at_row 2
|
18
|
+
start_at_row 2
|
18
19
|
end
|
19
|
-
|
20
|
+
|
20
21
|
mapping.should be_instance_of(CsvMapper::RowMap)
|
21
22
|
mapping.start_at_row.should == 2
|
22
23
|
end
|
23
|
-
|
24
|
+
|
24
25
|
it "should import a CSV IO" do
|
25
26
|
io = 'foo,bar,00,01'
|
26
|
-
results = @mapped.import(io, :type => :io) do
|
27
|
+
results = @mapped.import(io, :type => :io) do
|
27
28
|
first
|
28
29
|
second
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
results.should be_kind_of(Enumerable)
|
32
33
|
results.should have(1).things
|
33
34
|
results[0].first.should == 'foo'
|
34
35
|
results[0].second.should == 'bar'
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
it "should import a CSV File IO" do
|
38
39
|
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
39
40
|
start_at_row 1
|
40
41
|
[first_name, last_name, age]
|
41
42
|
end
|
42
|
-
|
43
|
+
|
43
44
|
results.size.should == 3
|
44
|
-
end
|
45
|
-
|
45
|
+
end
|
46
|
+
|
46
47
|
it "should stop importing at a specified row" do
|
47
48
|
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
48
49
|
start_at_row 1
|
@@ -65,64 +66,64 @@ describe CsvMapper do
|
|
65
66
|
|
66
67
|
it "should import non-comma delimited files" do
|
67
68
|
piped_io = 'foo|bar|00|01'
|
68
|
-
|
69
|
+
|
69
70
|
results = @mapped.import(piped_io, :type => :io) do
|
70
71
|
delimited_by '|'
|
71
72
|
[first, second]
|
72
73
|
end
|
73
|
-
|
74
|
+
|
74
75
|
results.should have(1).things
|
75
76
|
results[0].first.should == 'foo'
|
76
77
|
results[0].second.should == 'bar'
|
77
78
|
end
|
78
|
-
|
79
|
+
|
79
80
|
it "should allow named tranformation mappings" do
|
80
81
|
def upcase_name(row)
|
81
82
|
row[0].upcase
|
82
83
|
end
|
83
|
-
|
84
|
+
|
84
85
|
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
85
86
|
start_at_row 1
|
86
|
-
|
87
|
+
|
87
88
|
first_name.map :upcase_name
|
88
89
|
end
|
89
|
-
|
90
|
+
|
90
91
|
results[0].first_name.should == 'JOHN'
|
91
92
|
end
|
92
93
|
end
|
93
|
-
|
94
|
+
|
94
95
|
describe "extended" do
|
95
96
|
it "should allow the creation of CSV mappings" do
|
96
97
|
mapping = CsvMapper.map_csv do
|
97
|
-
start_at_row 2
|
98
|
+
start_at_row 2
|
98
99
|
end
|
99
|
-
|
100
|
+
|
100
101
|
mapping.should be_instance_of(CsvMapper::RowMap)
|
101
102
|
mapping.start_at_row.should == 2
|
102
103
|
end
|
103
|
-
|
104
|
+
|
104
105
|
it "should import a CSV IO" do
|
105
106
|
io = 'foo,bar,00,01'
|
106
|
-
results = CsvMapper.import(io, :type => :io) do
|
107
|
+
results = CsvMapper.import(io, :type => :io) do
|
107
108
|
first
|
108
109
|
second
|
109
110
|
end
|
110
|
-
|
111
|
+
|
111
112
|
results.should be_kind_of(Enumerable)
|
112
113
|
results.should have(1).things
|
113
114
|
results[0].first.should == 'foo'
|
114
115
|
results[0].second.should == 'bar'
|
115
116
|
end
|
116
|
-
|
117
|
+
|
117
118
|
it "should import a CSV File IO" do
|
118
119
|
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
119
120
|
start_at_row 1
|
120
121
|
[first_name, last_name, age]
|
121
122
|
end
|
122
|
-
|
123
|
+
|
123
124
|
results.size.should == 3
|
124
|
-
end
|
125
|
-
|
125
|
+
end
|
126
|
+
|
126
127
|
it "should stop importing at a specified row" do
|
127
128
|
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
128
129
|
start_at_row 1
|
@@ -144,40 +145,58 @@ describe CsvMapper do
|
|
144
145
|
end
|
145
146
|
|
146
147
|
describe "Adding only certain attributes by name or alias" do
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
148
|
+
context "A file with headers and empty column names" do
|
149
|
+
before :all do
|
150
|
+
@results = CsvMapper.import(File.dirname(__FILE__) + '/test_with_empty_column_names.csv') do
|
151
|
+
named_columns
|
152
|
+
surname('Last Name')
|
153
|
+
age.map { |row, index| row[index].to_i }
|
154
|
+
end
|
152
155
|
end
|
153
|
-
end
|
154
156
|
|
155
|
-
|
156
|
-
|
157
|
-
|
157
|
+
it "should have Last name aliased as surname" do
|
158
|
+
@results[1].surname.should == 'Doe'
|
159
|
+
end
|
158
160
|
|
159
|
-
|
160
|
-
|
161
|
-
|
161
|
+
it "should transform age to 26 (a Fixnum)" do
|
162
|
+
@results[1].age.should == 26
|
163
|
+
end
|
162
164
|
|
163
|
-
|
164
|
-
|
165
|
-
|
165
|
+
it "should not have First Name at all" do
|
166
|
+
lambda { @results[1].first_name }.should raise_error(NoMethodError)
|
167
|
+
end
|
166
168
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
169
|
+
it "should raise IndexError when adding non-existent fields" do
|
170
|
+
lambda {
|
171
|
+
@results = CsvMapper.import(File.dirname(__FILE__) + '/test_with_empty_column_names.csv') do
|
172
|
+
add_attributes_by_name('doesnt_exist')
|
173
|
+
end
|
174
|
+
}.should raise_error(IndexError)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should raise IndexError when adding non-existent aliases" do
|
178
|
+
lambda {
|
179
|
+
@results = CsvMapper.import(File.dirname(__FILE__) + '/test_with_empty_column_names.csv') do
|
180
|
+
my_new_field('doesnt_exist')
|
181
|
+
end
|
182
|
+
}.should raise_error(IndexError)
|
183
|
+
end
|
173
184
|
end
|
174
185
|
|
175
|
-
|
176
|
-
|
177
|
-
@results = CsvMapper.import(File.dirname(__FILE__) + '/
|
178
|
-
|
186
|
+
context "A crazy not-really CSV file with some lines to ignore at the top" do
|
187
|
+
before :all do
|
188
|
+
@results = CsvMapper.import(File.dirname(__FILE__) + '/test_with_pushed_down_header.csv') do
|
189
|
+
start_at_row 5
|
190
|
+
named_columns
|
191
|
+
surname('Last Name')
|
192
|
+
age.map { |row, index| row[index].to_i }
|
179
193
|
end
|
180
|
-
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should transform age to 26" do
|
197
|
+
@results[1].age.should == 26
|
198
|
+
end
|
199
|
+
|
181
200
|
end
|
182
201
|
end
|
183
202
|
|
@@ -191,26 +210,26 @@ describe CsvMapper do
|
|
191
210
|
|
192
211
|
it "should import non-comma delimited files" do
|
193
212
|
piped_io = 'foo|bar|00|01'
|
194
|
-
|
213
|
+
|
195
214
|
results = CsvMapper.import(piped_io, :type => :io) do
|
196
215
|
delimited_by '|'
|
197
216
|
[first, second]
|
198
217
|
end
|
199
|
-
|
218
|
+
|
200
219
|
results.should have(1).things
|
201
220
|
results[0].first.should == 'foo'
|
202
221
|
results[0].second.should == 'bar'
|
203
222
|
end
|
204
|
-
|
223
|
+
|
205
224
|
it "should not allow transformation mappings" do
|
206
225
|
def upcase_name(row)
|
207
226
|
row[0].upcase
|
208
227
|
end
|
209
|
-
|
210
|
-
(lambda do
|
228
|
+
|
229
|
+
(lambda do
|
211
230
|
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
212
231
|
start_at_row 1
|
213
|
-
|
232
|
+
|
214
233
|
first_name.map :upcase_name
|
215
234
|
end
|
216
235
|
end).should raise_error(Exception)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,66 +1,50 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rgarner-csv-mapper
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 7
|
9
|
-
- 0
|
10
|
-
version: 0.7.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Luke Pillow
|
14
9
|
- Russell Garner
|
15
10
|
autorequire:
|
16
11
|
bindir: bin
|
17
12
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
dependencies:
|
22
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2012-11-21 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
23
16
|
name: rspec
|
24
|
-
|
25
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
17
|
+
requirement: &2155992480 !ruby/object:Gem::Requirement
|
26
18
|
none: false
|
27
|
-
requirements:
|
28
|
-
- -
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
|
31
|
-
segments:
|
32
|
-
- 1
|
33
|
-
- 2
|
34
|
-
- 9
|
35
|
-
version: 1.2.9
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.0.0
|
36
23
|
type: :development
|
37
|
-
version_requirements: *id001
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: fastercsv
|
40
24
|
prerelease: false
|
41
|
-
|
25
|
+
version_requirements: *2155992480
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: fastercsv
|
28
|
+
requirement: &2156007300 !ruby/object:Gem::Requirement
|
42
29
|
none: false
|
43
|
-
requirements:
|
44
|
-
- -
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
|
47
|
-
segments:
|
48
|
-
- 0
|
49
|
-
version: "0"
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
50
34
|
type: :runtime
|
51
|
-
|
52
|
-
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2156007300
|
37
|
+
description: CSV Mapper makes it easy to import data from CSV files directly to a
|
38
|
+
collection of any type of Ruby object. The simplest way to create mappings is declare
|
39
|
+
the names of the attributes in the order corresponding to the CSV file column order.
|
53
40
|
email: rgarner@zephyros-systems.co.uk
|
54
41
|
executables: []
|
55
|
-
|
56
42
|
extensions: []
|
57
|
-
|
58
|
-
extra_rdoc_files:
|
43
|
+
extra_rdoc_files:
|
59
44
|
- History.txt
|
60
45
|
- LICENSE
|
61
46
|
- README.rdoc
|
62
|
-
files:
|
63
|
-
- .gitignore
|
47
|
+
files:
|
64
48
|
- History.txt
|
65
49
|
- LICENSE
|
66
50
|
- README.rdoc
|
@@ -76,42 +60,31 @@ files:
|
|
76
60
|
- spec/spec_helper.rb
|
77
61
|
- spec/test.csv
|
78
62
|
- spec/test_with_empty_column_names.csv
|
79
|
-
|
63
|
+
- spec/test_with_pushed_down_header.csv
|
80
64
|
homepage: http://github.com/rgarner/csv-mapper
|
81
65
|
licenses: []
|
82
|
-
|
83
66
|
post_install_message:
|
84
|
-
rdoc_options:
|
85
|
-
|
86
|
-
require_paths:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
87
69
|
- lib
|
88
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
71
|
none: false
|
90
|
-
requirements:
|
91
|
-
- -
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
|
94
|
-
|
95
|
-
- 0
|
96
|
-
version: "0"
|
97
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
77
|
none: false
|
99
|
-
requirements:
|
100
|
-
- -
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
|
103
|
-
segments:
|
104
|
-
- 0
|
105
|
-
version: "0"
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
106
82
|
requirements: []
|
107
|
-
|
108
83
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.
|
84
|
+
rubygems_version: 1.8.16
|
110
85
|
signing_key:
|
111
86
|
specification_version: 3
|
112
|
-
summary: rgarner-CsvMapper is a fork of a small library intended to simplify the common
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
- spec/csv-mapper_spec.rb
|
117
|
-
- spec/spec_helper.rb
|
87
|
+
summary: rgarner-CsvMapper is a fork of a small library intended to simplify the common
|
88
|
+
steps involved with importing CSV files to a usable form in Ruby. It has support
|
89
|
+
for null column names. When this is merged, this gem will be removed.
|
90
|
+
test_files: []
|
data/.gitignore
DELETED