LightMapper
Simple mapper for hash.
Installation
Add this line to your application's Gemfile:
gem 'light_mapper'
And then execute:
$ bundle
Or install it yourself as:
$ gem install light_mapper
Usage
Basic usage
{
'FirstName' => 'Pawel',
'LastName' => 'Niemczyk'
}.extend(LightMapper).mapping(
'FirstName' => :first_name,
'LastName' => :last_name
)
or
LightMapper.mapping(
{ 'FirstName' => 'Pawel', 'LastName' => 'Niemczyk' },
{ 'FirstName' => :first_name, 'LastName' => :last_name }
)
result is obvious:
{ first_name: 'Pawel', last_name: 'Niemczyk' }
The most popular usage:
PersonMapper = {
'FirstName' => :first_name,
'LastName' => :last_name,
'Age' => 'age'
}
data = {
'FirstName' => 'Pawel',
'LastName' => 'Niemczyk',
'Age' => 5
}
data.extend(LightMapper).mapping(PersonMapper)
# {first_name: 'Pawel', last_name: 'Niemczyk', 'age': 5}
When you require all keys
{ 'FirstName' => 'Pawel' }.extend(LightMapper).mapping({'FirstName' => :first_name, 'LastName' => :last_name}, strict: true)
it will raise LightMapper::KeyMissing: LastName key not found; Full path LastName
When you want to pass string or symbol keys
{
'FirstName' => 'Pawel',
second_name: 'Niemczyk'
}.extend(LightMapper).mapping({
'FirstName' => :first_name,
'second_name' => :last_name
}, any_keys: true)
the result will be:
{ first_name: 'Pawel', last_name: 'Niemczyk' }
Support for nested hashes, arrays, and objects (now we are talking about what it is capable of)
{
'source' => { 'google' => { 'search_word' => 'ruby' } },
'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
'roles' => %w[admin manager user],
'mixed' => { users: [User.new(email: 'max@example.com', name: 'Max', manager: true), User.new(email: 'pawel@example.com', name: 'Pawel', manager: false)] },
'scores' => [ 10, 2, 5, 1000],
'last_4_payments' => [
{ 'amount' => 100, 'currency' => 'USD' },
{ 'amount' => 200, 'currency' => 'USD' },
{ 'amount' => 300, 'currency' => 'USD' },
{ 'amount' => 400, 'currency' => 'USD' }
],
'array' => [
[1,2,3],
[4,5,6],
[
7,
8,
[':D']
],
]
}.extend(LightMapper).mapping(
'source.google.search_word' => :word,
'user.email' => :email,
'user.as_json.name' => :name,
'roles.0' => :first_role,
['roles', 1] => :middle_role,
'roles.last' => :last_role,
(->(source) { source[:mixed][:users].find { |user| user.manager }.email }) => :manager_email,
(->(source) { source[:mixed][:users].find { |user| user.manager }.name }) => :manager_name,
'mixed.users.last.name' => :last_user_name,
(->(source) { source[:last_4_payments].map(&:values).map(&:first).max }) => :quarterly_payment_amount,
'scores.sum' => :final_score,
'array.2.2.first' => :smile
)
the result will be:
{
word: 'ruby',
email: 'pawel@example.com',
name: 'Pawel',
first_role: 'admin',
last_role: 'user',
manager_email: 'max@example.com',
manager_name: 'Max',
last_user_name: 'Pawel',
quarterly_payment_amount: 1000,
final_score: 1017
}
Support for nested output structure and symbolize output keys
{
'source' => { 'google' => { 'private_pool' => true } },
'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
'roles' => %w[admin manager user],
'scores' => [10, 2, 5, 1000],
'last_4_payments' => [
{ 'amount' => 100, 'currency' => 'USD' },
{ 'amount' => 200, 'currency' => 'USD' },
{ 'amount' => 300, 'currency' => 'USD' },
{ 'amount' => 400, 'currency' => 'USD' }
]
}.extend(LightMapper).mapping({
'source.google.private_pool' => 'private',
'user.email' => 'user.email',
'user.name' => 'user.name',
'roles' => 'user.roles',
'scores.max' => 'user.best_score',
'last_4_payments.last' => 'payment.last',
}, keys: :symbol)
the result will be:
{
private: true,
user: {
email: 'pawel@example.com',
name: 'Pawel',
roles: %w[admin manager user],
best_score: 1000,
},
payment: {
last: { 'amount' => 400, 'currency' => 'USD' }
}
}
Support for pushing values to specific key based on the key path
{a: 1}.extend(LightMapper).push('b.c', 2, keys: :symbol)
# result { a: 1, b: { c: 2 } }
{ a: 1, b: { ab: 1}}.extend(LightMapper).push('b.ab', 2, keys: :symbol, override: true)
# result { a: 1, b: { ab: 2 } }
{ a: 1, b: { ab: 1}}.extend(LightMapper).push('b.abc.c', 2, keys: :symbol, build_structure: false)
# result { a: 1, b: { ab: 1 } } # it's not adding anything because provided key path do not exist
Mappers selection via pattern matching
GOOGLE_MAPPER = { 'result.user.name' => :name }
LINKEDIN_MAPPER = { 'result.client.display_name' => :word }
data = { source: 'google', user: { name: 'test'}, 'result' => { 'user' => { 'name' => 'Pawel'} } }
mapper = case data
in source: 'google', user: {name:} then GOOGLE_MAPPER
in source: 'linkedin', client: {display_name:} then LINKEDIN_MAPPER
else
raise 'Unknown mapper'
end
data.extend(LightMapper).mapping(mapper)
# result { name: 'Pawel' }
Contributing
- Fork it ( https://github.com/[my-github-username]/light_mapper/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request