scottmotte-csvmapper 0.4.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/.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