csv-mapper 0.0.4 → 0.5.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/.gitignore +2 -0
- data/History.txt +9 -0
- data/LICENSE +22 -0
- data/README.rdoc +17 -34
- data/Rakefile +47 -28
- data/VERSION +1 -0
- data/lib/csv-mapper.rb +9 -231
- data/lib/csv-mapper/attribute_map.rb +55 -0
- data/lib/csv-mapper/row_map.rb +179 -0
- data/spec/{csv-mapper_attribute_map_spec.rb → csv-mapper/csv-mapper_attribute_map_spec.rb} +19 -4
- data/spec/{csv-mapper_row_map_spec.rb → csv-mapper/csv-mapper_row_map_spec.rb} +9 -7
- data/spec/csv-mapper_spec.rb +150 -53
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +8 -9
- metadata +47 -56
- data/Manifest.txt +0 -25
- data/PostInstall.txt +0 -5
- data/config/website.yml +0 -2
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -71
- data/tasks/rspec.rake +0 -21
- data/test/test_csv-mapper.rb +0 -11
- data/test/test_helper.rb +0 -3
- data/website/index.html +0 -137
- data/website/index.txt +0 -110
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -159
- data/website/template.html.erb +0 -59
@@ -0,0 +1,55 @@
|
|
1
|
+
module CsvMapper
|
2
|
+
# A CsvMapper::AttributeMap contains the instructions to parse a value from a CSV row and to know the
|
3
|
+
# name of the attribute it is targeting.
|
4
|
+
class AttributeMap
|
5
|
+
attr_reader :name, :index
|
6
|
+
|
7
|
+
# Creates a new instance using the provided attribute +name+, CSV row +index+, and evaluation +map_context+
|
8
|
+
def initialize(name, index, map_context)
|
9
|
+
@name, @index, @map_context = name, index, map_context
|
10
|
+
end
|
11
|
+
|
12
|
+
# Set the index that this map is targeting.
|
13
|
+
#
|
14
|
+
# Returns this AttributeMap for chainability
|
15
|
+
def at(index)
|
16
|
+
@index = index
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
# Provide a lambda or the symbol name of a method on this map's evaluation context to be used when parsing
|
21
|
+
# the value from a CSV row.
|
22
|
+
# Both the lambda or the method provided should accept a single +row+ parameter
|
23
|
+
#
|
24
|
+
# Returns this AttributeMap for chainability
|
25
|
+
def map(transform=nil, &block_transform)
|
26
|
+
@transformer = block_transform || transform
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Given a CSV row, return the value at this AttributeMap's index using any provided map transforms (see map)
|
31
|
+
def parse(csv_row)
|
32
|
+
@transformer ? parse_transform(csv_row) : raw_value(csv_row)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Access the raw value of the CSV row without any map transforms applied.
|
36
|
+
def raw_value(csv_row)
|
37
|
+
csv_row[self.index]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def parse_transform(csv_row)
|
43
|
+
if @transformer.is_a? Symbol
|
44
|
+
transform_name = @transformer
|
45
|
+
@transformer = lambda{|row, index| @map_context.send(transform_name, row, index) }
|
46
|
+
end
|
47
|
+
|
48
|
+
if @transformer.arity == 1
|
49
|
+
@transformer.call(csv_row)
|
50
|
+
else
|
51
|
+
@transformer.call(csv_row, @index)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'csv-mapper/attribute_map'
|
2
|
+
|
3
|
+
module CsvMapper
|
4
|
+
# CsvMapper::RowMap provides a simple, DSL-like interface for constructing mappings.
|
5
|
+
# A CsvMapper::RowMap provides the main functionality of the library. It will mostly be used indirectly through the CsvMapper API,
|
6
|
+
# but may be useful to use directly for the dynamic CSV mappings.
|
7
|
+
class RowMap
|
8
|
+
#Start with a 'blank slate'
|
9
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__||instance_eval/ }
|
10
|
+
|
11
|
+
Infinity = 1.0/0
|
12
|
+
attr_reader :mapped_attributes
|
13
|
+
|
14
|
+
# Create a new instance with access to an evaluation context
|
15
|
+
def initialize(context, csv_data = nil, &map_block)
|
16
|
+
@context = context
|
17
|
+
@csv_data = csv_data
|
18
|
+
@before_filters = []
|
19
|
+
@after_filters = []
|
20
|
+
@parser_options = {}
|
21
|
+
@start_at_row = 0
|
22
|
+
@stop_at_row = Infinity
|
23
|
+
@delimited_by = FasterCSV::DEFAULT_OPTIONS[:col_sep]
|
24
|
+
@mapped_attributes = []
|
25
|
+
|
26
|
+
self.instance_eval(&map_block) if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Each row of a CSV is parsed and mapped to a new instance of a Ruby class; Struct by default.
|
30
|
+
# Use this method to change the what class each row is mapped to.
|
31
|
+
# The given class must respond to a parameter-less #new and all attribute mappings defined.
|
32
|
+
# Providing a hash of defaults will ensure that each resulting object will have the providing name and attribute values
|
33
|
+
# unless overridden by a mapping
|
34
|
+
def map_to(klass, defaults={})
|
35
|
+
@map_to_klass = klass
|
36
|
+
|
37
|
+
defaults.each do |name, value|
|
38
|
+
self.add_attribute(name, -99).map lambda{|row, index| value}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Allow us to read the first line of a csv file to automatically generate the attribute names.
|
43
|
+
# Spaces are replaced with underscores and non-word characters are removed.
|
44
|
+
#
|
45
|
+
# Keep in mind that there is potential for overlap in using this (i.e. you have a field named
|
46
|
+
# files+ and one named files- and they both get named 'files').
|
47
|
+
#
|
48
|
+
# You can specify aliases to rename fields to prevent conflicts and/or improve readability and compatibility.
|
49
|
+
#
|
50
|
+
# i.e. read_attributes_from_file('files+' => 'files_plus', 'files-' => 'files_minus)
|
51
|
+
def read_attributes_from_file aliases = {}
|
52
|
+
attributes = FasterCSV.new(@csv_data, @parser_options).readline
|
53
|
+
@start_at_row = [ @start_at_row, 1 ].max
|
54
|
+
@csv_data.rewind
|
55
|
+
attributes.each_with_index do |name, index|
|
56
|
+
name.strip!
|
57
|
+
use_name = aliases[name] || name.gsub(/\s+/, '_').gsub(/[\W]+/, '').downcase
|
58
|
+
add_attribute use_name, index
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Specify a hash of FasterCSV options to be used for CSV parsing
|
63
|
+
#
|
64
|
+
# Can be anything FasterCSV::new()[http://fastercsv.rubyforge.org/classes/FasterCSV.html#M000018] accepts
|
65
|
+
def parser_options(opts=nil)
|
66
|
+
@parser_options = opts if opts
|
67
|
+
@parser_options.merge :col_sep => @delimited_by
|
68
|
+
end
|
69
|
+
|
70
|
+
# Convenience method to 'move' the cursor skipping the current index.
|
71
|
+
def _SKIP_
|
72
|
+
self.move_cursor
|
73
|
+
end
|
74
|
+
|
75
|
+
# Specify the CSV column delimiter. Defaults to comma.
|
76
|
+
def delimited_by(delimiter=nil)
|
77
|
+
@delimited_by = delimiter if delimiter
|
78
|
+
@delimited_by
|
79
|
+
end
|
80
|
+
|
81
|
+
# Declare what row to begin parsing the CSV.
|
82
|
+
# This is useful for skipping headers and such.
|
83
|
+
def start_at_row(row_number=nil)
|
84
|
+
@start_at_row = row_number if row_number
|
85
|
+
@start_at_row
|
86
|
+
end
|
87
|
+
|
88
|
+
# Declare the last row to be parsed in a CSV.
|
89
|
+
def stop_at_row(row_number=nil)
|
90
|
+
@stop_at_row = row_number if row_number
|
91
|
+
@stop_at_row
|
92
|
+
end
|
93
|
+
|
94
|
+
# Declare method name symbols and/or lambdas to be executed before each row.
|
95
|
+
# Each method or lambda must accept to parameters: +csv_row+, +target_object+
|
96
|
+
# Methods names should refer to methods available within the RowMap's provided context
|
97
|
+
def before_row(*befores)
|
98
|
+
self.add_filters(@before_filters, *befores)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Declare method name symbols and/or lambdas to be executed before each row.
|
102
|
+
# Each method or lambda must accept to parameters: +csv_row+, +target_object+
|
103
|
+
# Methods names should refer to methods available within the RowMap's provided context
|
104
|
+
def after_row(*afters)
|
105
|
+
self.add_filters(@after_filters, *afters)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Add a new attribute to this map. Mostly used internally, but is useful for dynamic map creation.
|
109
|
+
# returns the newly created CsvMapper::AttributeMap
|
110
|
+
def add_attribute(name, index=nil)
|
111
|
+
attr_mapping = CsvMapper::AttributeMap.new(name.to_sym, index, @context)
|
112
|
+
self.mapped_attributes << attr_mapping
|
113
|
+
attr_mapping
|
114
|
+
end
|
115
|
+
|
116
|
+
# The current cursor location
|
117
|
+
def cursor # :nodoc:
|
118
|
+
@cursor ||= 0
|
119
|
+
end
|
120
|
+
|
121
|
+
# Move the cursor relative to it's current position
|
122
|
+
def move_cursor(positions=1) # :nodoc:
|
123
|
+
self.cursor += positions
|
124
|
+
end
|
125
|
+
|
126
|
+
# Given a CSV row return an instance of an object defined by this mapping
|
127
|
+
def parse(csv_row)
|
128
|
+
target = self.map_to_class.new
|
129
|
+
@before_filters.each {|filter| filter.call(csv_row, target) }
|
130
|
+
|
131
|
+
self.mapped_attributes.each do |attr_map|
|
132
|
+
target.send("#{attr_map.name}=", attr_map.parse(csv_row))
|
133
|
+
end
|
134
|
+
|
135
|
+
@after_filters.each {|filter| filter.call(csv_row, target) }
|
136
|
+
|
137
|
+
return target
|
138
|
+
end
|
139
|
+
|
140
|
+
protected # :nodoc:
|
141
|
+
|
142
|
+
# The Hacktastic "magic"
|
143
|
+
# Used to dynamically create CsvMapper::AttributeMaps based on unknown method calls that
|
144
|
+
# should represent the names of mapped attributes.
|
145
|
+
#
|
146
|
+
# An optional first argument is used to move this maps cursor position and as the index of the
|
147
|
+
# new AttributeMap
|
148
|
+
def method_missing(name, *args) # :nodoc:
|
149
|
+
|
150
|
+
if index = args[0]
|
151
|
+
self.move_cursor(index - self.cursor)
|
152
|
+
else
|
153
|
+
index = self.cursor
|
154
|
+
self.move_cursor
|
155
|
+
end
|
156
|
+
|
157
|
+
add_attribute(name, index)
|
158
|
+
end
|
159
|
+
|
160
|
+
def add_filters(to_hook, *filters) # :nodoc:
|
161
|
+
(to_hook << filters.collect do |filter|
|
162
|
+
filter.is_a?(Symbol) ? lambda{|row, target| @context.send(filter, row, target)} : filter
|
163
|
+
end).flatten!
|
164
|
+
end
|
165
|
+
|
166
|
+
def map_to_class # :nodoc:
|
167
|
+
unless @map_to_klass
|
168
|
+
attrs = mapped_attributes.collect {|attr_map| attr_map.name}
|
169
|
+
@map_to_klass = Struct.new(nil, *attrs)
|
170
|
+
end
|
171
|
+
|
172
|
+
@map_to_klass
|
173
|
+
end
|
174
|
+
|
175
|
+
def cursor=(value) # :nodoc:
|
176
|
+
@cursor=value
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
3
|
describe CsvMapper::AttributeMap do
|
4
4
|
|
5
5
|
class TestContext
|
6
|
-
def transform_it(row)
|
6
|
+
def transform_it(row, index)
|
7
7
|
:transform_it_success
|
8
8
|
end
|
9
9
|
end
|
@@ -34,11 +34,26 @@ describe CsvMapper::AttributeMap do
|
|
34
34
|
@row_attr.parse(@csv_row).should == @csv_row[1]
|
35
35
|
end
|
36
36
|
|
37
|
-
it "should parse values using mapped
|
37
|
+
it "should parse values using a mapped lambda transformers" do
|
38
|
+
@row_attr.map( lambda{|row, index| :success } )
|
39
|
+
@row_attr.parse(@csv_row).should == :success
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should parse values using a mapped lambda transformer that only accepts the row" do
|
38
43
|
@row_attr.map( lambda{|row| :success } )
|
39
44
|
@row_attr.parse(@csv_row).should == :success
|
40
45
|
end
|
41
46
|
|
47
|
+
it "should parse values using a mapped block transformers" do
|
48
|
+
@row_attr.map {|row, index| :success }
|
49
|
+
@row_attr.parse(@csv_row).should == :success
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should parse values using a mapped block transformer that only accepts the row" do
|
53
|
+
@row_attr.map {|row, index| :success }
|
54
|
+
@row_attr.parse(@csv_row).should == :success
|
55
|
+
end
|
56
|
+
|
42
57
|
it "should parse values using a named method on the context" do
|
43
58
|
@row_attr.map(:transform_it).parse(@csv_row).should == :transform_it_success
|
44
59
|
end
|
@@ -47,4 +62,4 @@ describe CsvMapper::AttributeMap do
|
|
47
62
|
@row_attr.raw_value(@csv_row).should be(@csv_row[@row_attr.index])
|
48
63
|
end
|
49
64
|
|
50
|
-
end
|
65
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
3
|
describe CsvMapper::RowMap do
|
4
4
|
|
@@ -7,7 +7,7 @@ describe CsvMapper::RowMap do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
class TestMapContext
|
10
|
-
def transform(row)
|
10
|
+
def transform(row, index)
|
11
11
|
:transform_success
|
12
12
|
end
|
13
13
|
|
@@ -25,8 +25,8 @@ describe CsvMapper::RowMap do
|
|
25
25
|
@row_map.parse(@csv_row).should_not be_nil
|
26
26
|
end
|
27
27
|
|
28
|
-
it "should map to a
|
29
|
-
@row_map.parse(@csv_row).should
|
28
|
+
it "should map to a Struct by default" do
|
29
|
+
@row_map.parse(@csv_row).should be_kind_of(Struct)
|
30
30
|
end
|
31
31
|
|
32
32
|
it "should parse a CSV row returning the mapped result" do
|
@@ -78,9 +78,11 @@ describe CsvMapper::RowMap do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it "should allow after row processing" do
|
81
|
-
|
81
|
+
filter_var = nil
|
82
|
+
@row_map.after_row lambda{|row, target| filter_var = :woot}
|
82
83
|
|
83
|
-
@row_map.parse(@csv_row)
|
84
|
+
@row_map.parse(@csv_row)
|
85
|
+
filter_var.should == :woot
|
84
86
|
end
|
85
87
|
|
86
88
|
it "should have a moveable cursor" do
|
@@ -124,7 +126,7 @@ describe CsvMapper::RowMap do
|
|
124
126
|
@row_map.cursor.should be(1)
|
125
127
|
end
|
126
128
|
|
127
|
-
it "should share
|
129
|
+
it "should share its context with its mappings" do
|
128
130
|
@row_map.first_name.map(:transform)
|
129
131
|
@row_map.parse(@csv_row).first_name.should == :transform_success
|
130
132
|
end
|
data/spec/csv-mapper_spec.rb
CHANGED
@@ -1,76 +1,173 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
include CsvMapper
|
4
|
-
|
5
3
|
describe CsvMapper do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
describe "included" do
|
5
|
+
before(:each) do
|
6
|
+
@mapped_klass = Class.new do
|
7
|
+
include CsvMapper
|
8
|
+
def upcase_name(row, index)
|
9
|
+
row[index].upcase
|
10
|
+
end
|
11
|
+
end
|
12
|
+
@mapped = @mapped_klass.new
|
13
|
+
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
it "should allow the creation of CSV mappings" do
|
16
|
+
mapping = @mapped.map_csv do
|
17
|
+
start_at_row 2
|
18
|
+
end
|
19
|
+
|
20
|
+
mapping.should be_instance_of(CsvMapper::RowMap)
|
21
|
+
mapping.start_at_row.should == 2
|
15
22
|
end
|
23
|
+
|
24
|
+
it "should import a CSV IO" do
|
25
|
+
io = 'foo,bar,00,01'
|
26
|
+
results = @mapped.import(io, :type => :io) do
|
27
|
+
first
|
28
|
+
second
|
29
|
+
end
|
16
30
|
|
17
|
-
|
18
|
-
|
19
|
-
|
31
|
+
results.should be_kind_of(Enumerable)
|
32
|
+
results.should have(1).things
|
33
|
+
results[0].first.should == 'foo'
|
34
|
+
results[0].second.should == 'bar'
|
35
|
+
end
|
20
36
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
it "should import a CSV File IO" do
|
38
|
+
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
39
|
+
start_at_row 1
|
40
|
+
[first_name, last_name, age]
|
41
|
+
end
|
42
|
+
|
43
|
+
results.size.should == 3
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should stop importing at a specified row" do
|
47
|
+
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
48
|
+
start_at_row 1
|
49
|
+
stop_at_row 2
|
50
|
+
[first_name, last_name, age]
|
51
|
+
end
|
52
|
+
|
53
|
+
results.size.should == 2
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should be able to read attributes from a csv file" do
|
57
|
+
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
58
|
+
# we'll alias age here just as an example
|
59
|
+
read_attributes_from_file('Age' => 'number_of_years_old')
|
60
|
+
end
|
61
|
+
results[1].first_name.should == 'Jane'
|
62
|
+
results[1].last_name.should == 'Doe'
|
63
|
+
results[1].number_of_years_old.should == '26'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should import non-comma delimited files" do
|
67
|
+
piped_io = 'foo|bar|00|01'
|
68
|
+
|
69
|
+
results = @mapped.import(piped_io, :type => :io) do
|
70
|
+
delimited_by '|'
|
71
|
+
[first, second]
|
72
|
+
end
|
73
|
+
|
74
|
+
results.should have(1).things
|
75
|
+
results[0].first.should == 'foo'
|
76
|
+
results[0].second.should == 'bar'
|
26
77
|
end
|
27
78
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
79
|
+
it "should allow named tranformation mappings" do
|
80
|
+
def upcase_name(row)
|
81
|
+
row[0].upcase
|
82
|
+
end
|
83
|
+
|
84
|
+
results = @mapped.import(File.dirname(__FILE__) + '/test.csv') do
|
85
|
+
start_at_row 1
|
86
|
+
|
87
|
+
first_name.map :upcase_name
|
88
|
+
end
|
89
|
+
|
90
|
+
results[0].first_name.should == 'JOHN'
|
91
|
+
end
|
32
92
|
end
|
33
93
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
94
|
+
describe "extended" do
|
95
|
+
it "should allow the creation of CSV mappings" do
|
96
|
+
mapping = CsvMapper.map_csv do
|
97
|
+
start_at_row 2
|
98
|
+
end
|
39
99
|
|
40
|
-
|
41
|
-
|
100
|
+
mapping.should be_instance_of(CsvMapper::RowMap)
|
101
|
+
mapping.start_at_row.should == 2
|
102
|
+
end
|
42
103
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
104
|
+
it "should import a CSV IO" do
|
105
|
+
io = 'foo,bar,00,01'
|
106
|
+
results = CsvMapper.import(io, :type => :io) do
|
107
|
+
first
|
108
|
+
second
|
109
|
+
end
|
110
|
+
|
111
|
+
results.should be_kind_of(Enumerable)
|
112
|
+
results.should have(1).things
|
113
|
+
results[0].first.should == 'foo'
|
114
|
+
results[0].second.should == 'bar'
|
48
115
|
end
|
116
|
+
|
117
|
+
it "should import a CSV File IO" do
|
118
|
+
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
119
|
+
start_at_row 1
|
120
|
+
[first_name, last_name, age]
|
121
|
+
end
|
122
|
+
|
123
|
+
results.size.should == 3
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should stop importing at a specified row" do
|
127
|
+
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
128
|
+
start_at_row 1
|
129
|
+
stop_at_row 2
|
130
|
+
[first_name, last_name, age]
|
131
|
+
end
|
49
132
|
|
50
|
-
|
51
|
-
|
133
|
+
results.size.should == 2
|
134
|
+
end
|
52
135
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
136
|
+
it "should be able to read attributes from a csv file" do
|
137
|
+
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
138
|
+
# we'll alias age here just as an example
|
139
|
+
read_attributes_from_file('Age' => 'number_of_years_old')
|
140
|
+
end
|
141
|
+
results[1].first_name.should == 'Jane'
|
142
|
+
results[1].last_name.should == 'Doe'
|
143
|
+
results[1].number_of_years_old.should == '26'
|
57
144
|
end
|
58
|
-
results[1].first_name.should == 'Jane'
|
59
|
-
results[1].last_name.should == 'Doe'
|
60
|
-
results[1].number_of_years_old.should == '26'
|
61
|
-
end
|
62
145
|
|
63
|
-
|
64
|
-
|
146
|
+
it "should import non-comma delimited files" do
|
147
|
+
piped_io = 'foo|bar|00|01'
|
65
148
|
|
66
|
-
|
67
|
-
|
68
|
-
|
149
|
+
results = CsvMapper.import(piped_io, :type => :io) do
|
150
|
+
delimited_by '|'
|
151
|
+
[first, second]
|
152
|
+
end
|
153
|
+
|
154
|
+
results.should have(1).things
|
155
|
+
results[0].first.should == 'foo'
|
156
|
+
results[0].second.should == 'bar'
|
69
157
|
end
|
70
158
|
|
71
|
-
|
72
|
-
|
73
|
-
|
159
|
+
it "should not allow tranformation mappings" do
|
160
|
+
def upcase_name(row)
|
161
|
+
row[0].upcase
|
162
|
+
end
|
163
|
+
|
164
|
+
(lambda do
|
165
|
+
results = CsvMapper.import(File.dirname(__FILE__) + '/test.csv') do
|
166
|
+
start_at_row 1
|
167
|
+
|
168
|
+
first_name.map :upcase_name
|
169
|
+
end
|
170
|
+
end).should raise_error(Exception)
|
171
|
+
end
|
74
172
|
end
|
75
|
-
|
76
173
|
end
|