Class: CsvMapper::RowMap
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
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#mapped_attributes ⇒ Object
readonly
Returns the value of attribute mapped_attributes.
Instance Method Summary collapse
-
#_SKIP_ ⇒ Object
Convenience method to ‘move’ the cursor skipping the current index.
-
#add_attribute(name, index = nil) ⇒ Object
Add a new attribute to this map.
-
#after_row(*afters) ⇒ Object
Declare method name symbols and/or lambdas to be executed before each row.
-
#before_row(*befores) ⇒ Object
Declare method name symbols and/or lambdas to be executed before each row.
-
#cursor ⇒ Object
The current cursor location.
-
#delimited_by(delimiter = nil) ⇒ Object
Specify the CSV column delimiter.
-
#initialize(context, csv_data = nil, &map_block) ⇒ RowMap
constructor
Create a new instance with access to an evaluation context.
-
#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.
-
#move_cursor(positions = 1) ⇒ Object
Move the cursor relative to it’s current position.
-
#named_columns ⇒ Object
Default csv_mapper behaviour is to use the ordinal position of a mapped attribute.
-
#parse(csv_row) ⇒ Object
Given a CSV row return an instance of an object defined by this mapping.
-
#parser_options(opts = nil) ⇒ Object
Specify a hash of FasterCSV options to be used for CSV parsing.
-
#read_attributes_from_file(aliases = {}) ⇒ Object
Allow us to read the first line of a csv file to automatically generate the attribute names.
-
#start_at_row(row_number = nil) ⇒ Object
Declare what row to begin parsing the CSV.
-
#stop_at_row(row_number = nil) ⇒ Object
Declare the last row to be parsed in a CSV.
Constructor Details
#initialize(context, csv_data = nil, &map_block) ⇒ RowMap
Create a new instance with access to an evaluation context
16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/csv-mapper/row_map.rb', line 16 def initialize(context, csv_data = nil, &map_block) @context = context @csv_data = csv_data @before_filters = [] @after_filters = [] @named_columns = false @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
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/csv-mapper/row_map.rb', line 158 def method_missing(name, *args) # :nodoc: existing_map = self.mapped_attributes.find {|attr| attr.name == name} return existing_map if existing_map # Effectively add an alias when we see new_field('With/Aliased/Name') if args[0].is_a? String return add_attribute(name, headers_to_indices.fetch(args[0].downcase)) end if @named_columns return add_attribute(name, headers_to_indices.fetch(name.to_s)) end 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
#context ⇒ Object (readonly)
Returns the value of attribute context.
12 13 14 |
# File 'lib/csv-mapper/row_map.rb', line 12 def context @context end |
#mapped_attributes ⇒ Object (readonly)
Returns the value of attribute mapped_attributes.
13 14 15 |
# File 'lib/csv-mapper/row_map.rb', line 13 def mapped_attributes @mapped_attributes end |
Instance Method Details
#_SKIP_ ⇒ Object
Convenience method to ‘move’ the cursor skipping the current index.
81 82 83 |
# File 'lib/csv-mapper/row_map.rb', line 81 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
120 121 122 123 124 |
# File 'lib/csv-mapper/row_map.rb', line 120 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
114 115 116 |
# File 'lib/csv-mapper/row_map.rb', line 114 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
107 108 109 |
# File 'lib/csv-mapper/row_map.rb', line 107 def before_row(*befores) self.add_filters(@before_filters, *befores) end |
#cursor ⇒ Object
The current cursor location
127 128 129 |
# File 'lib/csv-mapper/row_map.rb', line 127 def cursor # :nodoc: @cursor ||= 0 end |
#delimited_by(delimiter = nil) ⇒ Object
Specify the CSV column delimiter. Defaults to comma.
86 87 88 89 |
# File 'lib/csv-mapper/row_map.rb', line 86 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
36 37 38 39 40 41 42 |
# File 'lib/csv-mapper/row_map.rb', line 36 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
132 133 134 |
# File 'lib/csv-mapper/row_map.rb', line 132 def move_cursor(positions=1) # :nodoc: self.cursor += positions end |
#named_columns ⇒ Object
Default csv_mapper behaviour is to use the ordinal position of a mapped attribute. If you prefer to look for a column with the name of the attribute, use this method.
76 77 78 |
# File 'lib/csv-mapper/row_map.rb', line 76 def named_columns @named_columns = true end |
#parse(csv_row) ⇒ Object
Given a CSV row return an instance of an object defined by this mapping
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/csv-mapper/row_map.rb', line 137 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
69 70 71 72 |
# File 'lib/csv-mapper/row_map.rb', line 69 def (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)
53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/csv-mapper/row_map.rb', line 53 def read_attributes_from_file aliases = {} unnamed_number = 1 iterate_headers do |name, index| if name.nil? name = "_field_#{unnamed_number}" unnamed_number += 1 end name.strip! use_name = aliases[name] || attributize_field_name(name) 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.
93 94 95 96 |
# File 'lib/csv-mapper/row_map.rb', line 93 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.
99 100 101 102 |
# File 'lib/csv-mapper/row_map.rb', line 99 def stop_at_row(row_number=nil) @stop_at_row = row_number if row_number @stop_at_row end |