README.md in csv2hash-0.6.6 vs README.md in csv2hash-0.6.7

- old
+ new

@@ -29,20 +29,20 @@ Parsing is based on rules, you should defined rule for each cells ### DSL ``` - Csv2hash::Main.generate_definition :name do - set_type { Definition::MAPPING } - set_header_size { 2 } # 0 by default - set_structure_rules {{ max_columns: 2 }} - mapping do - cell position: [0,0], key: 'gender' - cell position: [1,0], key: 'name' - end +Csv2hash::Main.generate_definition :name do + set_type { Definition::MAPPING } + set_header_size { 2 } # 0 by default + set_structure_rules {{ max_columns: 2 }} + mapping do + cell position: [0,0], key: 'gender' + cell position: [1,0], key: 'name' end - Csv2hash::Main[:name] # Access anywhere +end +Csv2hash::Main[:name] # Access anywhere ``` ### Rules You should declared a definition of your CSV file, and then define for each cell what you would expect. @@ -50,41 +50,41 @@ Example : If you want the very first cell, located on the first line and on the first column to be a string with values are either 'yes' either 'no', you can write the following validation rule: ``` - cell name: 'aswering', type: 'string', values: ['yes', 'no'], position: [0,0] +cell name: 'aswering', type: 'string', values: ['yes', 'no'], position: [0,0] ``` `:type` attribute has `String` for default value, therefore you can just write this: ``` - cell name: 'aswering', values: ['yes', 'no'], position: [0,0] +cell name: 'aswering', values: ['yes', 'no'], position: [0,0] ``` You can define you own message but default message is 'undefined :key on :position' ``` - cell name: 'aswering', values: ['yes', 'no'], position: [0,0], message: 'this value is not supported' +cell name: 'aswering', values: ['yes', 'no'], position: [0,0], message: 'this value is not supported' ``` You can also define Range of values ``` - cell name: 'score', values: 0..5, position: [0,0] +cell name: 'score', values: 0..5, position: [0,0] ``` The message is parsed: ``` - cell ..., message: 'value of :name is not supported, please you one of :values' +cell ..., message: 'value of :name is not supported, please you one of :values' ``` It produces : ``` - value of answering is not supported, please use one of [yes, no] +value of answering is not supported, please use one of [yes, no] ``` ### Default values Only position is required: @@ -107,10 +107,28 @@ A definition should be provided. There are 2 types of definitions: * search for data at a precise position in the table: `y,x` * or search for data in a column of rows, where all the rows are the same: `x` (column index) +## Invoke + +``` +Csv2hash::Main.new(:<definition_name>, file_path_or_data, options).parse +``` + +``` +Csv2hash::Main.new(:<definition_name>, options) do + file_path_or_data +end.parse +``` + +### Options + +List of options : + +* ignore_blank_line: :boolean # i think is pretty straightforward to understand + ## Samples ### [MAPPING] Validation of cells with defined precision Consider the following CSV: @@ -124,37 +142,35 @@ ``` Precise position validation sample: ``` - class MyParser +class MyParser - attr_accessor :file_path_or_data + attr_accessor :file_path_or_data - def initialize file_path_or_data - @file_path_or_data = file_path_or_data - end + def initialize file_path_or_data + @file_path_or_data = file_path_or_data + end - def data - @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse - end + def data + @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse + end - private + private - def definition - Main.generate_definition :my_defintion do - set_type { Definition::MAPPING } - set_header_size { 1 } - mapping do - cell position: [2,1], key: 'first_name' - cell position: [3,1], key: 'last_name' - end - end - end - end - + def definition + Main.generate_definition :my_defintion do + set_type { Definition::MAPPING } + set_header_size { 1 } + mapping do + cell position: [2,1], key: 'first_name' + cell position: [3,1], key: 'last_name' + end + end end +end ``` ### Auto discover position This is a special feature for finding the Y index of row where you data start. For instance you have this following data : @@ -175,18 +191,18 @@ You want extract `Employment` information and `Personal info` but we do not know if extra information will not come and break our index. This feature can be useful is this case. You must change Y position (rows) by the column index and regex, the parser will search on this column the index row of this regex, here our rule : -``` - cell position: [4,1], key: 'employment' ``` +cell position: [4,1], key: 'employment' +``` became ``` - cell position: [[0, /Employment/],1], key: 'employment' +cell position: [[0, /Employment/],1], key: 'employment' ``` ### [COLLECTION] Validation of a collection (Regular CSV) Consider the following CSV: @@ -199,115 +215,112 @@ ``` Collection validation sample: ``` - class MyParser +class MyParser - attr_accessor :file_path_or_data + attr_accessor :file_path_or_data - def initialize file_path_or_data - @file_path_or_data = file_path_or_data - end + def initialize file_path_or_data + @file_path_or_data = file_path_or_data + end - def data - @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse - end + def data + @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse + end - private + private - def definition - Csv2Hash::Definition.new(rules, type = Csv2Hash::Definition::COLLECTION, header_size: 1) - end - - def definition - Main.generate_definition :my_defintion do - set_type { Definition::COLLECTION } - set_header_size { 1 } - mapping do - cell position: 0, key: 'nickname' - cell position: 1, key: 'first_name' - cell position: 2, key: 'last_name' - end - end - end - end - + def definition + Csv2Hash::Definition.new(rules, type = Csv2Hash::Definition::COLLECTION, header_size: 1) end + + def definition + Main.generate_definition :my_defintion do + set_type { Definition::COLLECTION } + set_header_size { 1 } + mapping do + cell position: 0, key: 'nickname' + cell position: 1, key: 'first_name' + cell position: 2, key: 'last_name' + end + end + end +end ``` ### Structure validation rules You may want to validate some structure, like min or max number of columns, definition accepts structure_rules as a key for the third parameter. Current validations are: :min_columns, :max_columns ``` - class MyParser +class MyParser - attr_accessor :file_path_or_data + attr_accessor :file_path_or_data - def initialize file_path_or_data - @file_path_or_data = file_path_or_data - end + def initialize file_path_or_data + @file_path_or_data = file_path_or_data + end - def data - @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse - end + def data + @data_wrapper ||= Csv2hash::Main.new(:<definition_name>, file_path_or_data).parse + end - private + private - def definition - Main.generate_definition :my_defintion do - set_type { Definition::COLLECTION } - set_header_size { 1 } - set_structure_rules {{ min_columns: 2, max_columns: 3 }} - mapping do - cell position: 0, key: 'nickname' - cell position: 1, key: 'first_name' - cell position: 2, key: 'last_name' - end - end + def definition + Main.generate_definition :my_defintion do + set_type { Definition::COLLECTION } + set_header_size { 1 } + set_structure_rules {{ min_columns: 2, max_columns: 3 }} + mapping do + cell position: 0, key: 'nickname' + cell position: 1, key: 'first_name' + cell position: 2, key: 'last_name' end end end +end ``` ### CSV Headers You can define the number of rows to skip in the header of the CSV. ``` - set_header_size { 1 } +set_header_size { 1 } ``` ### Parser and configuration Pasrer can take several parameters like that: ``` - definition, file_path_or_data, ignore_blank_line: false +definition, file_path_or_data, ignore_blank_line: false ``` in `file_path_or_data` attribute you can pass directly an `Array` of data (`Array` with 2 dimensions) really useful for testing, if you don't care about blank lines in your CSV you can ignore them. ### Response The parser return values wrapper into `DataWrapper Object`, you can call ```.valid?``` method on this Object and grab either data or errors like that : ``` - response = parser.parse - if response.valid? - response.data - else - response.errors - end +response = parser.parse +if response.valid? + response.data +else + response.errors +end ``` data or errors are Array, but errors can be formatted on csv format with .to_csv call ``` - response.errors.to_csv +response.errors.to_csv ``` ## Exception or Not ! You can choose into 2 differents modes of parsing, either **break_on_failure mode** for throw an exception when rule fail or **csv mode** for get csv original data + errors throwing into added extra column. @@ -321,48 +334,48 @@ You need call `.parse()` return `data_wrapper` if `.parse()` is invalid, you can code your own behavior: in your code ``` - parser = Csv2hash::Main.new(definition, file_path_or_data, ignore_blank_line: false).new - response = parser.parse - return response if response.valid? - # Whatever +parser = Csv2hash::Main.new(definition, file_path_or_data, ignore_blank_line: false).new +response = parser.parse +return response if response.valid? +# Whatever ``` In the same time Csv2hash call **notify(response)** method when CSV parsing fail, you can add your own Notifier: ``` - module Csv2hash - module Plugins - class Notifier - def initialize csv2hash - csv2hash.notifier.extend NotifierWithEmail - end +module Csv2hash + module Plugins + class Notifier + def initialize csv2hash + csv2hash.notifier.extend NotifierWithEmail + end - module NotifierWithEmail - def notify response - filename = 'issues_errors.csv' - tempfile = Tempfile.new [filename, File.extname(filename)] - File.open(tempfile.path, 'wb') { |file| file.write response.errors.to_csv } - # Send mail with csv file + errors and free resource - tempfile.unlink - end + module NotifierWithEmail + def notify response + filename = 'issues_errors.csv' + tempfile = Tempfile.new [filename, File.extname(filename)] + File.open(tempfile.path, 'wb') { |file| file.write response.errors.to_csv } + # Send mail with csv file + errors and free resource + tempfile.unlink end end end end +end ``` Or other implementation ### Errors Format errors is a Array of Hash ``` - { y: 1, x: 0, message: 'message', key: 'key', value: '' } +{ y: 1, x: 0, message: 'message', key: 'key', value: '' } ``` ## Sample ### Csv data @@ -374,44 +387,44 @@ ``` ### Rule ``` - cell position: [1,1], key: 'nickname', allow_blank: false +cell position: [1,1], key: 'nickname', allow_blank: false ``` ### Error ``` - { y: 1, x: 1, message: 'undefined nikcname on [0, 0]', key: 'nickname', value: nil } +{ y: 1, x: 1, message: 'undefined nikcname on [0, 0]', key: 'nickname', value: nil } ``` ## Personal Validator Rule You can define your own Validator For downcase validation ``` - class DowncaseValidator < Csv2hash::ExtraValidator - def valid? value - !!(value.match /^[a-z]+$/) - end +class DowncaseValidator < Csv2hash::ExtraValidator + def valid? value + !!(value.match /^[a-z]+$/) end +end ``` in your rule ``` - cell position: [0,0], key: 'name', extra_validator: DowncaseValidator.new, - message: 'your data should be written in lowercase only.' +cell position: [0,0], key: 'name', extra_validator: DowncaseValidator.new, + message: 'your data should be written in lowercase only.' ``` Csv data ``` - [ [ 'Foo' ] ] +[ [ 'Foo' ] ] ``` # Config file You can defined rules into a yaml file @@ -466,22 +479,22 @@ position: [[0,'LastName'],1] ``` this change is due to Yaml conversion -You can write ERB file, should be named with following convention ```<file name>.erb.yml``` +You can write ERB file, should be named with following convention ```<file name>.yml.erb``` ## YamlLoader You can load your definition with YamlLoader llike that : ``` -loader = Csv2hash::YamlLoader.load!('config/rules.erb.yml') +loader = Csv2hash::YamlLoader.load!('config/rules.yml.erb') loader.definition ``` or ``` -loader = Csv2hash::YamlLoader.new('config/rules.erb.yml') +loader = Csv2hash::YamlLoader.new('config/rules.yml.erb') loader.load! loader.definition ``` # Type conversion