DataMaps
Create mappings to convert structured data into another format! This is useful when you need a dynamic generated or serializable mapping.
Attention: The API can change before the gem version reach 1.0!
Installation
Add this line to your applications' Gemfile:
gem 'data_maps'
And then execute:
$ bundle
Or install it yourself as:
$ gem install data_maps
Usage
Mapper
The DataMaps::Mapper converts data only from a ruby hash. So you have to import your data and create a ruby hash of it.
mapper = DataMaps::Mapper.new(mapping)
converted_data = your_data.map do |data_row|
mapper.convert(data_row)
end
Mapping
Create mappings.
Field mapping
You can map a single field, a nested field or multiple fields.
mapping = DataMaps::Mapping.new({
'field' => {
from: 'source'
}
# or simple: 'field' => 'source'
})
# Example:
#
# source_data = {
# 'familyname' => 'Smith'
# }
#
# mapping = {
# 'last_name' => {
# from: 'familyname'
# }
# }
#
# destination_data = {
# 'last_name' => 'Smith'
# }
For nested fields you can specify the field chain as Array.
mapping = DataMaps::Mapping.new({
'field' => {
from: ['level1', 'level2']
}
})
# Example:
#
# source_data = {
# company_relation: {
# id: '123',
# name: 'My Company'
# }
# }
#
# mapping = {
# 'company' => {
# from: ['company_relation', 'name']
# }
# }
#
# destination_data = {
# 'company' => 'My Company'
# }
You can select multiple fields from your data. By pass them as Hash. You can pass directly a new field name.
mapping = DataMaps::Mapping.new({
'field' => {
from: { field: true, other_field: true } # or map them directly to a new field name by passing the new name instead of true
}
})
# Example:
#
# source_data = {
# customer_street: 'My street 5',
# customer_city: 'Cologne'
# }
#
# mapping = {
# 'address' => {
# from: {
# customer_street: 'street',
# customer_city: 'city'
# }
# }
# }
#
# destination_data = {
# 'address' => {
# 'street' => 'My street 5',
# 'city' => 'Cologne'
# }
# }
Conditions
Conditions must always have a when and then command. All condition statements are executed procedural.
The only exception is when using then: { filter: true }, then the execution breaks immediately and removes the whole field from result data.
'field' => {
from: 'source'
conditions: [
{ when: { empty: true }, then: { set: 'something' },
{ when: { regex: /[1-9]{5}/i }, then: { convert: { numeric: 'Integer' } } }
]
}
Possible when's
- When: empty
Possible options for the empty conditions are
trueorfalse. The condition is true whendata.empty? == result
empty: true # or false
- When: regex
Define a regular expression condition.
The condition is true when
data.match regex. Only works with strings.
regex: /[a-z]/i
- When: gt, gte Check if data is greater or greater or equal than the given value. Only works with comparable objects.
gt: 5
gte 5
- When: lt, lte Check if data is lower or lower or equal than the given value. Only works with comparable objects.
lt: 5
lte: 5
- When: eq, neq Check if data is equal or not equal to the given value. Only works with comparable objects.
eq: 10
neq: 'a-value'
- When: in, nin Check if data is in or not in the set of given values. Doesn't work for a collection of values.
in: ['a', 'b', 'c']
nin: ['x', 'y', 'z']
- When: custom
Define your own condition class by defining them in the
DataMaps::Whenmodule. YourWhenmust implement aexecutemethod which returnstrueorfalse. You have to extend theDataMaps::When::Baseclass. Then all options are available via theoptionattribute reader.
class DataMaps::When::IsZip < DataMaps::When::Base
def execute(data)
!!data.match(/\d{5}/)
end
end
is_zip: true # option isn't used, you can pass anything, for example and readability true
Possible then's
- Then: set Set the value to given value.
set: 'to this value'
- Then: convert Apply the configured converter. See converter section for more information.
convert: [
{ apply: :numeric, option: 'Integer' }
]
- Then: filter When this is set to true then the whole field will filtered.
filter: true
- Then: custom
Define your own then by defining them in the
DataMaps::Thenmodule. YourThenmust implement aexecutemethod. The return of this method is set as data. You have to extend theDataMaps::Then::Baseclass. Then all options are available via theoptionattribute reader.
class DataMaps::Then::SendEmail < DataMaps::Then::Base
def execute(data)
MyFramework::Email.send(to: option)
data
end
end
send_email: me@example.com
Converter
Apply one or many converters to the input data. Converters applied procedural.
'field' => {
from: 'source',
convert: [
{ apply: :map, option: { 1: 'A', 2: 'B' } }
]
# or in short, when no option is needed
convert: [ :string ]
}
Possible converter
- Converter: map
A simple value mapping. Maps are converted to a
HashWithIndifferentAccess. Works with scalar values, hashes and arrays. For arrays and hashes it returns nil if the value is not in the mapping. For scalar values it returns the original data.
apply: :map,
option: {
from: to
}
- Converter: numeric Cast data to a numeric value. Possible options are 'Integer', 'Float' or a number, then it is casted to float and rounded. Doesn't work with collections. Can raise an error if the value is not convertable.
apply: :numeric,
option 'Integer'
# option: 'Float'
# option: 2
- Converter: String Cast explicit to string. Doesn't work with collections. Can raise error if the value is not convertable.
apply: :string
- Converter: Boolean
Cast explicit to bool (by double negotiation). Doesn't work with collections.
Can return unexpected values, e.g. a double negotiated empty array is true!
!![] #=> true
apply: :bool
- Converter: keys This maps the hash keys when the input data is a hash or when you select multiple from fields. Only works with hashes. Return the original data when the data isn't a hash.
apply: :key,
option: {
'address1' => 'street'
}
- Converter: Prefix
This prefixes the data with a given value. Call
to_son data and always returns a string.
apply: :prefix,
option: '$'
- Converter: Postfix
This postfixes the data with a given value. Call
to_son data and always returns a string.
apply: :postfix,
option: '€'
- Converter: ruby Apply any method on the current data object.
apply: :ruby,
option: :upcase
# option: [:slice, 5]
# option: [:join, ', ']
- Converter: custom
Define your own converter by defining them in the
DataMaps::Convertermodule. YourConvertermust implement anexecutemethod. The return of this method is set as new data. You have to extend theDataMaps::Converter::Baseclass. Then all options are available via theoptionattribute reader.
class DataMaps::Converter::PersonObject < DataMaps::Converter::Base
def execute(data)
Person.new(data, option)
end
end
apply: person_object,
option: { as: :importer } # passed value are available via option
ToDos:
- DSL to describe mappings
- Better error handling during the converter process
Have fun using the DataMaps gem :)