morfo 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +40 -4
- data/lib/morfo/version.rb +1 -1
- data/lib/morfo.rb +51 -15
- data/spec/lib/morfo_spec.rb +55 -16
- metadata +16 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 435026b5435920e085878e500852cef28210364a
|
4
|
+
data.tar.gz: 358f66a812811a70b7e7d6f9791d5462a578c4a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 382af73b1c1953b0660c7c255b6f1276bc295448654df87e47f9dca16a2d98743d09544e172917c22ed1034877faffc947e07324da114c6b64960dcf080cc4dc
|
7
|
+
data.tar.gz: 7fce9a8d94b8b1e4d67d27157a9a01a5211b05203f7d072bc28eb0d3c052e52730f30d08d5a600807a86efe1756a6b5b3e8b54676677203b6c34418648976f3c
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Morfo
|
1
|
+
# El Morfo
|
2
2
|
|
3
3
|
[](https://travis-ci.org/leifg/morfo) [](https://coveralls.io/r/leifg/morfo) [](https://codeclimate.com/github/leifg/morfo) [](https://gemnasium.com/leifg/morfo) [](http://badge.fury.io/rb/morfo)
|
4
4
|
|
@@ -30,7 +30,7 @@ In order to morf the hashes you have to provide a class that extends `Morf::Base
|
|
30
30
|
|
31
31
|
Use the `map` method to specify what field you map to another field:
|
32
32
|
|
33
|
-
class
|
33
|
+
class Title < Morfo::Base
|
34
34
|
map :title, :tv_show_title
|
35
35
|
end
|
36
36
|
|
@@ -46,6 +46,23 @@ Afterwards use the `morf` method to morf all hashes in one array to the end resu
|
|
46
46
|
# {tv_show_title: 'Breaking Bad'},
|
47
47
|
# ]
|
48
48
|
|
49
|
+
It is also possible to map fields to multiple other fields
|
50
|
+
|
51
|
+
class MultiTitle < Morfo::Base
|
52
|
+
map :title, :tv_show_title
|
53
|
+
map :title, :show_title
|
54
|
+
end
|
55
|
+
|
56
|
+
MultiTitle.morf([
|
57
|
+
{title: 'The Walking Dead'} ,
|
58
|
+
{title: 'Breaking Bad'},
|
59
|
+
])
|
60
|
+
|
61
|
+
# [
|
62
|
+
# {tv_show_title: 'The Walking Dead', show_title: 'The Walking Dead'},
|
63
|
+
# {tv_show_title: 'Breaking Bad', show_title: 'Breaking Bad'},
|
64
|
+
# ]
|
65
|
+
|
49
66
|
## Transformations
|
50
67
|
|
51
68
|
For each mapping you can define a block, that will be called on every input:
|
@@ -71,7 +88,7 @@ For each mapping you can define a block, that will be called on every input:
|
|
71
88
|
You can directly access nested values in the hashes:
|
72
89
|
|
73
90
|
class Name < Morfo::Base
|
74
|
-
map [:name, :
|
91
|
+
map [:name, :first], :first_name
|
75
92
|
map [:name, :last], :last_name
|
76
93
|
end
|
77
94
|
|
@@ -92,9 +109,28 @@ You can directly access nested values in the hashes:
|
|
92
109
|
|
93
110
|
# [
|
94
111
|
# {first_name: 'Clark',last_name: 'Kent'},
|
95
|
-
# {first_name: 'Bruce',last_name: 'Wayne'}
|
112
|
+
# {first_name: 'Bruce',last_name: 'Wayne'},
|
96
113
|
# ]
|
97
114
|
|
115
|
+
|
116
|
+
It is also possible to store values in a nested hash:
|
117
|
+
|
118
|
+
class Wrapper < Morfo::Base
|
119
|
+
map :first_name, [:superhero, :name, :first]
|
120
|
+
map :last_name, [:superhero, :name, :last]
|
121
|
+
end
|
122
|
+
|
123
|
+
Name.morf([
|
124
|
+
{first_name: 'Clark',last_name: 'Kent'},
|
125
|
+
{first_name: 'Bruce',last_name: 'Wayne'},,
|
126
|
+
])
|
127
|
+
|
128
|
+
# [
|
129
|
+
# { superhero: {name: { first: 'Clark', last: 'Kent'}}},
|
130
|
+
# { superhero: {name: { first: 'Bruce', last: 'Wayne'}}},
|
131
|
+
# ]
|
132
|
+
|
133
|
+
|
98
134
|
## Contributing
|
99
135
|
|
100
136
|
1. Fork it
|
data/lib/morfo/version.rb
CHANGED
data/lib/morfo.rb
CHANGED
@@ -3,35 +3,71 @@ require 'morfo/version'
|
|
3
3
|
module Morfo
|
4
4
|
class Base
|
5
5
|
def self.map from, to, &transformation
|
6
|
-
mapping_actions <<
|
6
|
+
mapping_actions << MapAction.new(from, to, transformation)
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.morf input
|
10
|
-
input.map do |
|
11
|
-
mapping_actions.inject({}) do |output,
|
12
|
-
|
13
|
-
extract_value(value, from),
|
14
|
-
transformation
|
15
|
-
)
|
16
|
-
output.merge!(to => resulting_value) if resulting_value
|
17
|
-
output
|
10
|
+
input.map do |row|
|
11
|
+
mapping_actions.inject({}) do |output, action|
|
12
|
+
deep_merge!(output, action.execute(row))
|
18
13
|
end
|
19
14
|
end
|
20
15
|
end
|
21
16
|
|
22
17
|
private
|
23
|
-
def self.
|
24
|
-
|
18
|
+
def self.mapping_actions
|
19
|
+
@actions ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.deep_merge! hash, other_hash, &block
|
23
|
+
other_hash.each_pair do |k,v|
|
24
|
+
tv = hash[k]
|
25
|
+
if tv.is_a?(Hash) && v.is_a?(Hash)
|
26
|
+
hash[k] = deep_merge!(tv, v, &block)
|
27
|
+
else
|
28
|
+
hash[k] = block && tv ? block.call(k, tv, v) : v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class MapAction
|
36
|
+
attr_reader :from
|
37
|
+
attr_reader :to
|
38
|
+
attr_reader :transformation
|
39
|
+
|
40
|
+
def initialize from, to, transformation
|
41
|
+
@from = from
|
42
|
+
@to = to
|
43
|
+
@transformation = transformation
|
44
|
+
end
|
45
|
+
|
46
|
+
def execute row
|
47
|
+
resulting_value = apply_transformation(extract_value(row))
|
48
|
+
resulting_value ? store_value(to, resulting_value) : {}
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def extract_value row
|
53
|
+
Array(from).inject(row) do |resulting_value, key|
|
25
54
|
resulting_value ? resulting_value[key] : nil
|
26
55
|
end
|
27
56
|
end
|
28
57
|
|
29
|
-
def
|
30
|
-
transformation ? transformation.call(
|
58
|
+
def apply_transformation row
|
59
|
+
transformation ? transformation.call(row) : row
|
31
60
|
end
|
32
61
|
|
33
|
-
def
|
34
|
-
|
62
|
+
def store_value to, value
|
63
|
+
Array(to).reverse.inject({}) do |hash, key|
|
64
|
+
if hash.keys.first.nil?
|
65
|
+
hash.merge!(key => value)
|
66
|
+
else
|
67
|
+
{ key => hash }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
#{ to => value }
|
35
71
|
end
|
36
72
|
end
|
37
73
|
end
|
data/spec/lib/morfo_spec.rb
CHANGED
@@ -67,29 +67,68 @@ describe Morfo::Base do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
context '
|
71
|
-
subject
|
72
|
-
class
|
73
|
-
map
|
70
|
+
context '1 to many conversion' do
|
71
|
+
subject do
|
72
|
+
class MutliTitleMapper < Morfo::Base
|
73
|
+
map :title, :title
|
74
|
+
map :title, :also_title
|
74
75
|
end
|
75
|
-
|
76
|
+
MutliTitleMapper
|
76
77
|
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
InvalidImdbRatingMapper
|
79
|
+
it 'maps title to multiple fields' do
|
80
|
+
expected_output = input.map{|v| {title: v[:title], also_title: v[:title]} }
|
81
|
+
expect(subject.morf(input)).to eq(expected_output)
|
83
82
|
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'nested conversion' do
|
86
|
+
context 'nested source' do
|
87
|
+
subject(:valid_path) do
|
88
|
+
class ImdbRatingMapper < Morfo::Base
|
89
|
+
map [:ratings, :imdb], :rating
|
90
|
+
end
|
91
|
+
ImdbRatingMapper
|
92
|
+
end
|
93
|
+
|
94
|
+
subject(:invalid_path) do
|
95
|
+
class InvalidImdbRatingMapper < Morfo::Base
|
96
|
+
map [:very, :long, :path, :that, :might, :not, :exist], :rating
|
97
|
+
end
|
98
|
+
InvalidImdbRatingMapper
|
99
|
+
end
|
84
100
|
|
85
|
-
|
86
|
-
|
87
|
-
|
101
|
+
it 'maps nested attributes' do
|
102
|
+
expected_output = input.map{|v| {rating: v[:ratings][:imdb]} }
|
103
|
+
expect(valid_path.morf(input)).to eq(expected_output)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'doesn\'t raise error for invalid path' do
|
107
|
+
expected_output = [{},{}]
|
108
|
+
expect(invalid_path.morf(input)).to eq(expected_output)
|
109
|
+
end
|
88
110
|
end
|
89
111
|
|
90
|
-
|
91
|
-
|
92
|
-
|
112
|
+
context 'nested destination' do
|
113
|
+
subject do
|
114
|
+
class WrapperMapper < Morfo::Base
|
115
|
+
map :title, [:tv_show, :title]
|
116
|
+
map :channel, [:tv_show, :channel]
|
117
|
+
end
|
118
|
+
WrapperMapper
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'maps to nested destination' do
|
122
|
+
expected_output = input.map{|v|
|
123
|
+
{
|
124
|
+
tv_show: {
|
125
|
+
title: v[:title],
|
126
|
+
channel: v[:channel],
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
expect(subject.morf(input)).to eq(expected_output)
|
131
|
+
end
|
93
132
|
end
|
94
133
|
end
|
95
134
|
end
|
metadata
CHANGED
@@ -1,61 +1,61 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morfo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leif Gensert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '2.14'
|
48
|
-
- - <
|
48
|
+
- - "<"
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '4.0'
|
51
51
|
type: :development
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- -
|
55
|
+
- - ">="
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: '2.14'
|
58
|
-
- - <
|
58
|
+
- - "<"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '4.0'
|
61
61
|
description: This gem provides a DSL for converting one hash into another
|
@@ -65,9 +65,9 @@ executables: []
|
|
65
65
|
extensions: []
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
|
-
- .gitignore
|
69
|
-
- .rspec
|
70
|
-
- .travis.yml
|
68
|
+
- ".gitignore"
|
69
|
+
- ".rspec"
|
70
|
+
- ".travis.yml"
|
71
71
|
- Gemfile
|
72
72
|
- Guardfile
|
73
73
|
- LICENSE.txt
|
@@ -88,17 +88,17 @@ require_paths:
|
|
88
88
|
- lib
|
89
89
|
required_ruby_version: !ruby/object:Gem::Requirement
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ">="
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- -
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
requirements: []
|
100
100
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.0
|
101
|
+
rubygems_version: 2.2.0
|
102
102
|
signing_key:
|
103
103
|
specification_version: 4
|
104
104
|
summary: Inspired by ActiveImporter, this gem generically converts an array of hashes
|