Simple ETL
An easy-to-use toolkit to help you with ETL (Extract Transform Load) operations.
Simple ETL 'would be' (:D) framework-agnostic and easy to use.
Source
Source namespace is responsible of input files parsing.
First of all you have to define a "source template" inside a definition file (for example my_template.stl):
define :format_name do
field :name
field :surname
end
Then you will load the template with the following code:
my_template = SimpleEtl::Source.load './etl/my_template.stl'
At this point you can parse a source and process the result as with the following code:
my_template.parse '....', :type => :inline # load data inline
result = my_template.parse 'source.dat' # load from file
if result.valid?
result.rows.each do |row|
puts "|\t#{row.name}\t|\t#{row.surname}\t|"
end
puts "Parse Completed!"
else
result.errors.each do |error|
puts "Error while parsing line #{error.row_index}: #{error.}"
end
end
As you can see the result is valid if there are no errors.
The rows array contains all the parsed rows. Each row contains the parsed attributes as accessors.
The errors array contains all the generated errors. Each error is an object with 'row_index', 'message' and 'exception' properties.
Structure of the template definition
A template definition is composed by three layers:
- raw fields
- transformations
- generators
Fields
field :name
field :surname, :type => :string, :required => true
By default type is 'object'. It means it's not converted in any format. Other possible types are:
string: field is stripped by extra spaces;
integer: field is stripped. If the input value is nil or empty, nil is returned; it's converted in integer if the value contains numbers; a CastError is raised otherwise;
float: field is stripped. If the input value is nil or empty, nil is returned; it's converted in float if the value contains numbers; a CastError is raised otherwise;
boolean field is stripped. If the input value is nil or empty, nil is returned; it's converted in boolean if the input value is true,false,1,0; a CastError is raised otherwise;
The template definition will provide you an helper for each defined type. So you can write:
string :name
integer :age
For each helper, an additional 'required' helper will also be available:
required_string :name
required_integer :age
Remember: every format plugin will define its own field syntax, so remember to read the Wiki
Transformers and generators
They are functions that help you manipulate the parsed raw data:
transform :name { |name| name.downcase } # => name field is transformed in downcase
# a full_name field will be present in the row
generate :full_name do |row|
"#{row.name} #{row.surname}"
end
generate :company do |row|
if cmp = Company.find(row.company_id)
cmp
else
raise ParseError.new "Cannot find a company with id #{row.company_id}"
end
end
A transformer is a code block that transform a particular value. It's executed as soon as the input value is parsed (if it's valid).
A generator is a code block that generates a new property for the current row. All the generators are executed when the entire row as been read and transformed.
Skip rows
If you need to skip some rows before parsing the file you can use the helper 'skip_rows':
define :format do
skip_rows 2
field :name
end
This will start the parsing from the third row.