scottmotte-csvmapper 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 scottmotte
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,36 @@
1
+ = CsvMapper
2
+
3
+ Taken from Luke Pillow (pillowfactory's) gem and adjusted for my own needs very very slightly.
4
+
5
+ == INSTALL:
6
+
7
+ sudo gem install scottmotte-csvmapper
8
+
9
+ == DESCRIPTION:
10
+
11
+ CsvMapper is a small library intended to simplify the common steps involved with importing CSV files to a usable form in Ruby.
12
+
13
+ == EXAMPLES:
14
+
15
+ The following example will import a CSV file to an Array of OpenStruct[http://ruby-doc.org/core/classes/OpenStruct.html] instances.
16
+
17
+ ==== Example CSV File Structure
18
+
19
+ First Name,Last Name,Age
20
+ John,Doe,27
21
+ Jane,Doe,26
22
+ Bat,Man,52
23
+
24
+ ==== Simple Usage Example
25
+ include CsvMapper
26
+
27
+ results = CsvMapper.import('/path/to/file.csv') do
28
+ start_at_row 1
29
+ [first_name, last_name, age]
30
+ end
31
+
32
+ results.first.first_name # John
33
+ results.first.last_name # Doe
34
+ results.first.age # 27
35
+
36
+
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "csvmapper"
8
+ gem.summary = %Q{Stolen from pillowfactory's csv-mapper and adjusted slightly}
9
+ gem.email = "scott@scottmotte.com"
10
+ gem.homepage = "http://github.com/scottmotte/csvmapper"
11
+ gem.authors = ["scottmotte"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+
14
+ gem.add_dependency('right_aws')
15
+ gem.add_dependency('mini_magick')
16
+ end
17
+
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION.yml')
40
+ config = YAML.load(File.read('VERSION.yml'))
41
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
42
+ else
43
+ version = ""
44
+ end
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "csvmapper #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
data/lib/csvmapper.rb ADDED
@@ -0,0 +1,237 @@
1
+ require 'ostruct'
2
+ require 'fastercsv'
3
+
4
+ # this is stolen from csv-mapper gem with slight modifcations by me and a cleaner module name
5
+ module CsvMapper
6
+ # Create a new RowMap instance from the definition in the given block.
7
+ def self.map_csv(&map_block)
8
+ CsvMapper::RowMap.new(self, &map_block)
9
+ end
10
+
11
+ # Load CSV data and map the values according to the definition in the given block.
12
+ # Accepts either a file path, String, or IO as +data+. Defaults to file path.
13
+ #
14
+ # The following +options+ may be used:
15
+ # <tt>:type</tt>:: defaults to <tt>:file_path</tt>. Use <tt>:io</tt> to specify data as String or IO.
16
+ # <tt>:map</tt>:: Specify an instance of a RowMap to take presidence over a given block defintion.
17
+ #
18
+ def self.import(data, options={}, &map_block)
19
+ config = { :type => :file_path,
20
+ :map => map_csv(&map_block) }.merge!(options)
21
+
22
+ csv_data = config[:type] == :io ? data : File.new(data, 'r')
23
+ map = config[:map]
24
+
25
+ results = []
26
+ FasterCSV.new(csv_data, map.parser_options ).each_with_index do |row, i|
27
+ results << map.parse(row) if i >= map.start_at_row && i <= map.stop_at_row
28
+ end
29
+
30
+ results
31
+ end
32
+
33
+ # CsvMapper::RowMap provides a simple, DSL-like interface for constructing mappings.
34
+ # A CsvMapper::RowMap provides the main functionality of the library. It will mostly be used indirectly through the CsvMapper API,
35
+ # but may be useful to use directly for the dynamic CSV mappings.
36
+ class RowMap
37
+ #Start with a 'blank slate'
38
+ instance_methods.each { |m| undef_method m unless m =~ /^__||instance_eval/ }
39
+
40
+ Infinity = 1.0/0
41
+ attr_reader :mapped_attributes
42
+
43
+ # Create a new instance with access to an evaluation context
44
+ def initialize(context, &map_block)
45
+ @context = context
46
+ @before_filters = []
47
+ @after_filters = []
48
+ @parser_options = {}
49
+ @start_at_row = 0
50
+ @stop_at_row = Infinity
51
+ @delimited_by = FasterCSV::DEFAULT_OPTIONS[:col_sep]
52
+ @mapped_attributes = []
53
+
54
+ self.instance_eval(&map_block) if block_given?
55
+ end
56
+
57
+ # Each row of a CSV is parsed and mapped to a new instance of a Ruby class; OpenStruct by default.
58
+ # Use this method to change the what class each row is mapped to.
59
+ # The given class must respond to a parameter-less #new and all attribute mappings defined.
60
+ # Providing a hash of defaults will ensure that each resulting object will have the providing name and attribute values
61
+ # unless overridden by a mapping
62
+ def map_to(klass, defaults={})
63
+ @map_to_klass = klass
64
+
65
+ defaults.each do |name, value|
66
+ self.add_attribute(name, -99).map lambda{|row| value}
67
+ end
68
+ end
69
+
70
+ # Specify a hash of FasterCSV options to be used for CSV parsing
71
+ #
72
+ # Can be anything FasterCSV::new()[http://fastercsv.rubyforge.org/classes/FasterCSV.html#M000018] accepts
73
+ def parser_options(opts=nil)
74
+ @parser_options = opts if opts
75
+ @parser_options.merge :col_sep => @delimited_by
76
+ end
77
+
78
+ # Convenience method to 'move' the cursor skipping the current index.
79
+ def _SKIP_
80
+ self.move_cursor
81
+ end
82
+
83
+ # Specify the CSV column delimiter. Defaults to comma.
84
+ def delimited_by(delimiter=nil)
85
+ @delimited_by = delimiter if delimiter
86
+ @delimited_by
87
+ end
88
+
89
+ # Declare what row to begin parsing the CSV.
90
+ # This is useful for skipping headers and such.
91
+ def start_at_row(row_number=nil)
92
+ @start_at_row = row_number if row_number
93
+ @start_at_row
94
+ end
95
+
96
+ # Declare the last row to be parsed in a CSV.
97
+ def stop_at_row(row_number=nil)
98
+ @stop_at_row = row_number if row_number
99
+ @stop_at_row
100
+ end
101
+
102
+ # Declare method name symbols and/or lambdas to be executed before each row.
103
+ # Each method or lambda must accept to parameters: +csv_row+, +target_object+
104
+ # Methods names should refer to methods available within the RowMap's provided context
105
+ def before_row(*befores)
106
+ self.add_filters(@before_filters, *befores)
107
+ end
108
+
109
+ # Declare method name symbols and/or lambdas to be executed before each row.
110
+ # Each method or lambda must accept to parameters: +csv_row+, +target_object+
111
+ # Methods names should refer to methods available within the RowMap's provided context
112
+ def after_row(*afters)
113
+ self.add_filters(@after_filters, *afters)
114
+ end
115
+
116
+ # Add a new attribute to this map. Mostly used internally, but is useful for dynamic map creation.
117
+ # returns the newly created CsvMapper::AttributeMap
118
+ def add_attribute(name, index=nil)
119
+ attr_mapping = CsvMapper::AttributeMap.new(name.to_sym, index, @context)
120
+ self.mapped_attributes << attr_mapping
121
+ attr_mapping
122
+ end
123
+
124
+ # The current cursor location
125
+ def cursor # :nodoc:
126
+ @cursor ||= 0
127
+ end
128
+
129
+ # Move the cursor relative to it's current position
130
+ def move_cursor(positions=1) # :nodoc:
131
+ self.cursor += positions
132
+ end
133
+
134
+ # Given a CSV row return an instance of an object defined by this mapping
135
+ def parse(csv_row)
136
+ target = self.map_to_class.new
137
+ @before_filters.each {|filter| filter.call(csv_row, target) }
138
+
139
+ self.mapped_attributes.inject(target) do |result, attr_map|
140
+ result.send("#{attr_map.name}=".to_sym, attr_map.parse(csv_row))
141
+ result
142
+ end
143
+
144
+ @after_filters.each {|filter| filter.call(csv_row, target) }
145
+
146
+ return target
147
+ end
148
+
149
+ protected # :nodoc:
150
+
151
+ # The Hacktastic "magic"
152
+ # Used to dynamically create CsvMapper::AttributeMaps based on unknown method calls that
153
+ # should represent the names of mapped attributes.
154
+ #
155
+ # An optional first argument is used to move this maps cursor position and as the index of the
156
+ # new AttributeMap
157
+ def method_missing(name, *args) # :nodoc:
158
+
159
+ if index = args[0]
160
+ self.move_cursor(index - self.cursor)
161
+ else
162
+ index = self.cursor
163
+ self.move_cursor
164
+ end
165
+
166
+ add_attribute(name, index)
167
+ end
168
+
169
+ def add_filters(to_hook, *filters) # :nodoc:
170
+ (to_hook << filters.collect do |filter|
171
+ filter.is_a?(Symbol) ? lambda{|row, target| @context.send(filter, row, target)} : filter
172
+ end).flatten!
173
+ end
174
+
175
+ def map_to_class # :nodoc:
176
+ @map_to_klass || OpenStruct
177
+ end
178
+
179
+ def cursor=(value) # :nodoc:
180
+ @cursor=value
181
+ end
182
+
183
+
184
+ end
185
+
186
+ # A CsvMapper::AttributeMap contains the instructions to parse a value from a CSV row and to know the
187
+ # name of the attribute it is targeting.
188
+ class AttributeMap
189
+ attr_reader :name, :index
190
+
191
+ # Creates a new instance using the provided attribute +name+, CSV row +index+, and evaluation +map_context+
192
+ def initialize(name, index, map_context)
193
+ @name, @index, @map_context = name, index, map_context
194
+ end
195
+
196
+ # Set the index that this map is targeting.
197
+ #
198
+ # Returns this AttributeMap for chainability
199
+ def at(index)
200
+ @index = index
201
+ self
202
+ end
203
+
204
+ # Provide a lambda or the symbol name of a method on this map's evaluation context to be used when parsing
205
+ # the value from a CSV row.
206
+ # Both the lambda or the method provided should accept a single +row+ parameter
207
+ #
208
+ # Returns this AttributeMap for chainability
209
+ def map(transform)
210
+ @transformer = transform
211
+ self
212
+ end
213
+
214
+ # Given a CSV row, return the value at this AttributeMap's index using any provided map transforms (see map)
215
+ def parse(csv_row)
216
+ @transformer ? parse_transform(csv_row) : csv_row[self.index]
217
+ end
218
+
219
+ # Access the raw value of the CSV row without any map transforms applied.
220
+ def raw_value(csv_row)
221
+ csv_row[self.index]
222
+ end
223
+
224
+ private
225
+
226
+ def parse_transform(csv_row)
227
+ if @transformer.is_a? Symbol
228
+ transform_name = @transformer
229
+ @transformer = lambda{|row| @map_context.send(transform_name, row) }
230
+ end
231
+
232
+ @transformer.call(csv_row)
233
+ end
234
+
235
+ end
236
+ end
237
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Csvmapper" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'csvmapper'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scottmotte-csvmapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - scottmotte
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-20 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: right_aws
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mini_magick
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description:
36
+ email: scott@scottmotte.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/csvmapper.rb
52
+ - spec/csvmapper_spec.rb
53
+ - spec/spec_helper.rb
54
+ has_rdoc: false
55
+ homepage: http://github.com/scottmotte/csvmapper
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.2.0
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Stolen from pillowfactory's csv-mapper and adjusted slightly
80
+ test_files:
81
+ - spec/csvmapper_spec.rb
82
+ - spec/spec_helper.rb