Class: CsvMapper::RowMap

Inherits:
Object show all
Defined in:
lib/csv-mapper.rb

Overview

CsvMapper::RowMap provides a simple, DSL-like interface for constructing mappings. A CsvMapper::RowMap provides the main functionality of the library. It will mostly be used indirectly through the CsvMapper API, but may be useful to use directly for the dynamic CSV mappings.

Constant Summary collapse

Infinity =
1.0/0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, csv_data = nil, &map_block) ⇒ RowMap

Create a new instance with access to an evaluation context



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/csv-mapper.rb', line 125

def initialize(context, csv_data = nil, &map_block)
  @context = context
  @csv_data = csv_data
  @before_filters = []
  @after_filters = []
  @parser_options = {}
  @start_at_row = 0
  @stop_at_row = Infinity
  @delimited_by = FasterCSV::DEFAULT_OPTIONS[:col_sep]
  @mapped_attributes = []
  
  self.instance_eval(&map_block) if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object (protected)

The Hacktastic “magic” Used to dynamically create CsvMapper::AttributeMaps based on unknown method calls that should represent the names of mapped attributes.

An optional first argument is used to move this maps cursor position and as the index of the new AttributeMap



259
260
261
262
263
264
265
266
267
268
269
# File 'lib/csv-mapper.rb', line 259

def method_missing(name, *args) # :nodoc:
  
  if index = args[0]
    self.move_cursor(index - self.cursor)
  else
    index = self.cursor
    self.move_cursor
  end
  
  add_attribute(name, index)
end

Instance Attribute Details

#mapped_attributesObject (readonly)

Returns the value of attribute mapped_attributes.



122
123
124
# File 'lib/csv-mapper.rb', line 122

def mapped_attributes
  @mapped_attributes
end

Instance Method Details

#_SKIP_Object

Convenience method to ‘move’ the cursor skipping the current index.



181
182
183
# File 'lib/csv-mapper.rb', line 181

def _SKIP_
  self.move_cursor
end

#add_attribute(name, index = nil) ⇒ Object

Add a new attribute to this map. Mostly used internally, but is useful for dynamic map creation. returns the newly created CsvMapper::AttributeMap



220
221
222
223
224
# File 'lib/csv-mapper.rb', line 220

def add_attribute(name, index=nil)
  attr_mapping = CsvMapper::AttributeMap.new(name.to_sym, index, @context)
  self.mapped_attributes << attr_mapping
  attr_mapping
end

#after_row(*afters) ⇒ Object

Declare method name symbols and/or lambdas to be executed before each row. Each method or lambda must accept to parameters: csv_row, target_object Methods names should refer to methods available within the RowMap’s provided context



214
215
216
# File 'lib/csv-mapper.rb', line 214

def after_row(*afters)
  self.add_filters(@after_filters, *afters)
end

#before_row(*befores) ⇒ Object

Declare method name symbols and/or lambdas to be executed before each row. Each method or lambda must accept to parameters: csv_row, target_object Methods names should refer to methods available within the RowMap’s provided context



207
208
209
# File 'lib/csv-mapper.rb', line 207

def before_row(*befores)
  self.add_filters(@before_filters, *befores)
end

#cursorObject

The current cursor location



227
228
229
# File 'lib/csv-mapper.rb', line 227

def cursor  # :nodoc:
  @cursor ||= 0
end

#delimited_by(delimiter = nil) ⇒ Object

Specify the CSV column delimiter. Defaults to comma.



186
187
188
189
# File 'lib/csv-mapper.rb', line 186

def delimited_by(delimiter=nil)
  @delimited_by = delimiter if delimiter
  @delimited_by
end

#map_to(klass, defaults = {}) ⇒ Object

Each row of a CSV is parsed and mapped to a new instance of a Ruby class; OpenStruct by default. Use this method to change the what class each row is mapped to.

The given class must respond to a parameter-less #new and all attribute mappings defined. Providing a hash of defaults will ensure that each resulting object will have the providing name and attribute values unless overridden by a mapping



144
145
146
147
148
149
150
# File 'lib/csv-mapper.rb', line 144

def map_to(klass, defaults={})
  @map_to_klass = klass
  
  defaults.each do |name, value|
    self.add_attribute(name, -99).map lambda{|row| value}
  end
end

#move_cursor(positions = 1) ⇒ Object

Move the cursor relative to it’s current position



232
233
234
# File 'lib/csv-mapper.rb', line 232

def move_cursor(positions=1) # :nodoc:
  self.cursor += positions
end

#parse(csv_row) ⇒ Object

Given a CSV row return an instance of an object defined by this mapping



237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/csv-mapper.rb', line 237

def parse(csv_row)
  target = self.map_to_class.new
  @before_filters.each {|filter| filter.call(csv_row, target) }
    
  self.mapped_attributes.inject(target) do |result, attr_map|
    result.send("#{attr_map.name}=".to_sym, attr_map.parse(csv_row))
    result
  end
  
  @after_filters.each {|filter| filter.call(csv_row, target) }
  
  return target
end

#parser_options(opts = nil) ⇒ Object

Specify a hash of FasterCSV options to be used for CSV parsing

Can be anything FasterCSV::new() accepts



175
176
177
178
# File 'lib/csv-mapper.rb', line 175

def parser_options(opts=nil)
  @parser_options = opts if opts
  @parser_options.merge :col_sep => @delimited_by 
end

#read_attributes_from_file(aliases = {}) ⇒ Object

Allow us to read the first line of a csv file to automatically generate the attribute names. Spaces are replaced with underscores and non-word characters are removed.

Keep in mind that there is potential for overlap in using this (i.e. you have a field named files+ and one named files- and they both get named ‘files’).

You can specify aliases to rename fields to prevent conflicts and/or improve readability and compatibility.

i.e. read_attributes_from_file(‘files+’ => ‘files_plus’, ‘files-’ => ‘files_minus)



161
162
163
164
165
166
167
168
169
170
# File 'lib/csv-mapper.rb', line 161

def read_attributes_from_file aliases = {}
  attributes = FasterCSV.new(@csv_data, @parser_options).readline
  @start_at_row = [ @start_at_row, 1 ].max
  @csv_data.rewind
  attributes.each_with_index do |name, index|
    name.strip!
    use_name = aliases[name] || name.gsub(/\s+/, '_').gsub(/[\W]+/, '').downcase
    add_attribute use_name, index
  end
end

#start_at_row(row_number = nil) ⇒ Object

Declare what row to begin parsing the CSV. This is useful for skipping headers and such.



193
194
195
196
# File 'lib/csv-mapper.rb', line 193

def start_at_row(row_number=nil)
  @start_at_row = row_number if row_number
  @start_at_row
end

#stop_at_row(row_number = nil) ⇒ Object

Declare the last row to be parsed in a CSV.



199
200
201
202
# File 'lib/csv-mapper.rb', line 199

def stop_at_row(row_number=nil)
  @stop_at_row = row_number if row_number
  @stop_at_row
end