Class: CsvMapper::RowMap

Inherits:
Object show all
Defined in:
lib/csv-mapper/row_map.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



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/csv-mapper/row_map.rb', line 15

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



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/csv-mapper/row_map.rb', line 148

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.



12
13
14
# File 'lib/csv-mapper/row_map.rb', line 12

def mapped_attributes
  @mapped_attributes
end

Instance Method Details

#_SKIP_Object

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



71
72
73
# File 'lib/csv-mapper/row_map.rb', line 71

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



110
111
112
113
114
# File 'lib/csv-mapper/row_map.rb', line 110

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



104
105
106
# File 'lib/csv-mapper/row_map.rb', line 104

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



97
98
99
# File 'lib/csv-mapper/row_map.rb', line 97

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

#cursorObject

The current cursor location



117
118
119
# File 'lib/csv-mapper/row_map.rb', line 117

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

#delimited_by(delimiter = nil) ⇒ Object

Specify the CSV column delimiter. Defaults to comma.



76
77
78
79
# File 'lib/csv-mapper/row_map.rb', line 76

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; Struct 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



34
35
36
37
38
39
40
# File 'lib/csv-mapper/row_map.rb', line 34

def map_to(klass, defaults={})
  @map_to_klass = klass

  defaults.each do |name, value|
    self.add_attribute(name, -99).map lambda{|row, index| value}
  end
end

#move_cursor(positions = 1) ⇒ Object

Move the cursor relative to it’s current position



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

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



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

def parse(csv_row)
  target = self.map_to_class.new
  @before_filters.each {|filter| filter.call(csv_row, target) }
  
  self.mapped_attributes.each do |attr_map|
    target.send("#{attr_map.name}=", attr_map.parse(csv_row))
  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



65
66
67
68
# File 'lib/csv-mapper/row_map.rb', line 65

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)



51
52
53
54
55
56
57
58
59
60
# File 'lib/csv-mapper/row_map.rb', line 51

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.



83
84
85
86
# File 'lib/csv-mapper/row_map.rb', line 83

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.



89
90
91
92
# File 'lib/csv-mapper/row_map.rb', line 89

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