data_maps 0.1.0 → 0.2.0
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/Gemfile.lock +1 -1
- data/README.md +144 -66
- data/data_maps.gemspec +1 -1
- data/lib/data_maps/statement.rb +5 -1
- data/lib/data_maps/version.rb +1 -1
- data/spec/data_maps/statement_spec.rb +16 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0db231594073294046bbb5835c640cf80e9104a2
|
4
|
+
data.tar.gz: 061514db19e260ce0d6920d08c00bfbaaa3b676a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d164afd5020057a201912e236cda59b9262d02df41872bed00dfbc56c932aabcdbbcd7c517f777eddc63f9d73ac63f3cb0e07634c8077c09c31445103674d45d
|
7
|
+
data.tar.gz: cf4b01f7a57cc76b881ef7ae7ff9fd69f279d35b7b813f02ab8b3196a7e1219b7f41492131921448ee1f3dd489b8cb9f76686518824ac87540f357e2beeadfed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,10 +3,10 @@ DataMaps
|
|
3
3
|
[](https://travis-ci.org/dino115/data_maps)
|
4
4
|
[](https://codeclimate.com/github/dino115/data_maps)
|
5
5
|
[](https://codeclimate.com/github/dino115/data_maps)
|
6
|
+
[](http://badge.fury.io/rb/data_maps)
|
6
7
|
|
7
|
-
Create
|
8
|
-
|
9
|
-
**Attention:** This gem is currently under development and can't be used yet!
|
8
|
+
Create mappings to convert structured data into another format!
|
9
|
+
This is useful for dynamic generated or serializable (e.g. to save) mappings.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -27,7 +27,7 @@ Or install it yourself as:
|
|
27
27
|
## Usage
|
28
28
|
|
29
29
|
### Mapper
|
30
|
-
The DataMaps::Mapper converts data from a ruby hash to
|
30
|
+
The DataMaps::Mapper converts data only from a ruby hash. So you have to import your data and create a ruby hash of it.
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
mapper = DataMaps::Mapper.new(mapping)
|
@@ -40,7 +40,9 @@ end
|
|
40
40
|
### Mapping
|
41
41
|
Create mappings.
|
42
42
|
|
43
|
-
####
|
43
|
+
#### Field mapping
|
44
|
+
You can map a single field, a nested field or multiple fields.
|
45
|
+
|
44
46
|
```ruby
|
45
47
|
mapping = DataMaps::Mapping.new({
|
46
48
|
'field' => {
|
@@ -48,6 +50,82 @@ mapping = DataMaps::Mapping.new({
|
|
48
50
|
}
|
49
51
|
# or simple: 'field' => 'source'
|
50
52
|
})
|
53
|
+
|
54
|
+
# Example:
|
55
|
+
#
|
56
|
+
# source_data = {
|
57
|
+
# 'familyname' => 'Smith'
|
58
|
+
# }
|
59
|
+
#
|
60
|
+
# mapping = {
|
61
|
+
# 'last_name' => {
|
62
|
+
# from: 'familyname'
|
63
|
+
# }
|
64
|
+
# }
|
65
|
+
#
|
66
|
+
# destination_data = {
|
67
|
+
# 'last_name' => 'Smith'
|
68
|
+
# }
|
69
|
+
```
|
70
|
+
|
71
|
+
For nested fields you can specify the field chain as `Array`.
|
72
|
+
```ruby
|
73
|
+
mapping = DataMaps::Mapping.new({
|
74
|
+
'field' => {
|
75
|
+
from: ['level1', 'level2']
|
76
|
+
}
|
77
|
+
})
|
78
|
+
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# source_data = {
|
82
|
+
# company_relation: {
|
83
|
+
# id: '123',
|
84
|
+
# name: 'My Company'
|
85
|
+
# }
|
86
|
+
# }
|
87
|
+
#
|
88
|
+
# mapping = {
|
89
|
+
# 'company' => {
|
90
|
+
# from: ['company_relation', 'name']
|
91
|
+
# }
|
92
|
+
# }
|
93
|
+
#
|
94
|
+
# destination_data = {
|
95
|
+
# 'company' => 'My Company'
|
96
|
+
# }
|
97
|
+
```
|
98
|
+
|
99
|
+
You can select multiple fields from your data. By pass them as `Hash`. You can pass directly a new field name.
|
100
|
+
```ruby
|
101
|
+
mapping = DataMaps::Mapping.new({
|
102
|
+
'field' => {
|
103
|
+
from: { field: true, other_field: true } # or map them directly to a new field name by passing the new name instead of true
|
104
|
+
}
|
105
|
+
})
|
106
|
+
|
107
|
+
# Example:
|
108
|
+
#
|
109
|
+
# source_data = {
|
110
|
+
# customer_street: 'My street 5',
|
111
|
+
# customer_city: 'Cologne'
|
112
|
+
# }
|
113
|
+
#
|
114
|
+
# mapping = {
|
115
|
+
# 'address' => {
|
116
|
+
# from: {
|
117
|
+
# customer_street: 'street',
|
118
|
+
# customer_city: 'city'
|
119
|
+
# }
|
120
|
+
# }
|
121
|
+
# }
|
122
|
+
#
|
123
|
+
# destination_data = {
|
124
|
+
# 'address' => {
|
125
|
+
# 'street' => 'My street 5',
|
126
|
+
# 'city' => 'Cologne'
|
127
|
+
# }
|
128
|
+
# }
|
51
129
|
```
|
52
130
|
|
53
131
|
#### Conditions
|
@@ -55,13 +133,13 @@ Conditions must always have a when and then command. All condition statements ar
|
|
55
133
|
The only exception is when using `then: { filter: true }`, then the execution breaks immediately and removes the whole field from result data.
|
56
134
|
|
57
135
|
```ruby
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
136
|
+
'field' => {
|
137
|
+
from: 'source'
|
138
|
+
conditions: [
|
139
|
+
{ when: { empty: true }, then: { set: 'something' },
|
140
|
+
{ when: { regex: /[1-9]{5}/i }, then: { convert: { numeric: 'Integer' } } }
|
141
|
+
]
|
142
|
+
}
|
65
143
|
```
|
66
144
|
|
67
145
|
##### Possible when's
|
@@ -71,42 +149,42 @@ The only exception is when using `then: { filter: true }`, then the execution br
|
|
71
149
|
The condition is true when `data.empty? == result`
|
72
150
|
|
73
151
|
```ruby
|
74
|
-
|
152
|
+
empty: true # or false
|
75
153
|
```
|
76
154
|
- **When: regex**
|
77
155
|
Define a regular expression condition.
|
78
156
|
The condition is true when `data.match regex`. Only works with strings.
|
79
157
|
|
80
158
|
```ruby
|
81
|
-
|
159
|
+
regex: /[a-z]/i
|
82
160
|
```
|
83
161
|
- **When: gt, gte**
|
84
162
|
Check if data is *greater* or *greater or equal* than the given value. Only works with comparable objects.
|
85
163
|
|
86
164
|
```ruby
|
87
|
-
|
88
|
-
|
165
|
+
gt: 5
|
166
|
+
gte 5
|
89
167
|
```
|
90
168
|
- **When: lt, lte**
|
91
169
|
Check if data is *lower* or *lower or equal* than the given value. Only works with comparable objects.
|
92
170
|
|
93
171
|
```ruby
|
94
|
-
|
95
|
-
|
172
|
+
lt: 5
|
173
|
+
lte: 5
|
96
174
|
```
|
97
175
|
- **When: eq, neq**
|
98
176
|
Check if data is *equal* or *not equal* to the given value. Only works with comparable objects.
|
99
177
|
|
100
178
|
```ruby
|
101
|
-
|
102
|
-
|
179
|
+
eq: 10
|
180
|
+
neq: 'a-value'
|
103
181
|
```
|
104
182
|
- **When: in, nin**
|
105
183
|
Check if data is *in* or *not in* the set of given values. Doesn't work for a collection of values.
|
106
184
|
|
107
185
|
```ruby
|
108
|
-
|
109
|
-
|
186
|
+
in: ['a', 'b', 'c']
|
187
|
+
nin: ['x', 'y', 'z']
|
110
188
|
```
|
111
189
|
- **When: custom**
|
112
190
|
Define your own condition class by defining them in the `DataMaps::When` module.
|
@@ -114,15 +192,15 @@ The only exception is when using `then: { filter: true }`, then the execution br
|
|
114
192
|
You have to extend the `DataMaps::When::Base` class. Then all options are available via the `option` attribute reader.
|
115
193
|
|
116
194
|
```ruby
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
195
|
+
class DataMaps::When::IsZip < DataMaps::When::Base
|
196
|
+
def execute(data)
|
197
|
+
!!data.match(/\d{5}/)
|
121
198
|
end
|
199
|
+
end
|
122
200
|
```
|
123
201
|
|
124
202
|
```ruby
|
125
|
-
|
203
|
+
is_zip: true # option isn't used, you can pass anything, for example and readability true
|
126
204
|
```
|
127
205
|
|
128
206
|
##### Possible then's
|
@@ -131,21 +209,21 @@ The only exception is when using `then: { filter: true }`, then the execution br
|
|
131
209
|
Set the value to given value.
|
132
210
|
|
133
211
|
```ruby
|
134
|
-
|
212
|
+
set: 'to this value'
|
135
213
|
```
|
136
214
|
- **Then: convert**
|
137
215
|
Apply the configured converter. See converter section for more information.
|
138
216
|
|
139
217
|
```ruby
|
140
|
-
|
141
|
-
|
142
|
-
|
218
|
+
convert: {
|
219
|
+
numeric: 'Integer'
|
220
|
+
}
|
143
221
|
```
|
144
222
|
- **Then: filter**
|
145
223
|
When this is set to true then the whole field will filtered.
|
146
224
|
|
147
225
|
```ruby
|
148
|
-
|
226
|
+
filter: true
|
149
227
|
```
|
150
228
|
- **Then: custom**
|
151
229
|
Define your own *then* by defining them in the `DataMaps::Then` module.
|
@@ -153,31 +231,31 @@ The only exception is when using `then: { filter: true }`, then the execution br
|
|
153
231
|
You have to extend the `DataMaps::Then::Base` class. Then all options are available via the `option` attribute reader.
|
154
232
|
|
155
233
|
```ruby
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
234
|
+
class DataMaps::Then::SendEmail < DataMaps::Then::Base
|
235
|
+
def execute(data)
|
236
|
+
MyFramework::Email.send(to: option)
|
237
|
+
data
|
161
238
|
end
|
239
|
+
end
|
162
240
|
```
|
163
241
|
|
164
242
|
```ruby
|
165
|
-
|
243
|
+
send_email: me@example.com
|
166
244
|
```
|
167
245
|
|
168
246
|
#### Converter
|
169
247
|
Apply one or many converters to the input data. Converters applied procedural.
|
170
248
|
|
171
249
|
```ruby
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
}
|
250
|
+
'field' => {
|
251
|
+
from: 'source',
|
252
|
+
convert: {
|
253
|
+
map: {
|
254
|
+
1: 'A',
|
255
|
+
2: 'B'
|
179
256
|
}
|
180
257
|
}
|
258
|
+
}
|
181
259
|
```
|
182
260
|
|
183
261
|
##### Possible converter
|
@@ -188,61 +266,61 @@ Apply one or many converters to the input data. Converters applied procedural.
|
|
188
266
|
For arrays and hashes it returns nil if the value is not in the mapping. For flat values it returns the original data.
|
189
267
|
|
190
268
|
```ruby
|
191
|
-
|
192
|
-
|
193
|
-
|
269
|
+
map: {
|
270
|
+
from: to
|
271
|
+
}
|
194
272
|
```
|
195
273
|
- **Converter: numeric**
|
196
274
|
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.
|
197
275
|
Can raise an error if the value is not convertable.
|
198
276
|
|
199
277
|
```ruby
|
200
|
-
|
201
|
-
|
202
|
-
|
278
|
+
numeric: 'Integer'
|
279
|
+
numeric: 'Float'
|
280
|
+
numeric: 2
|
203
281
|
```
|
204
282
|
- **Converter: String**
|
205
283
|
Cast explicit to string. Doesn't work with collections.
|
206
284
|
Can raise error if the value is not convertable.
|
207
285
|
|
208
286
|
```ruby
|
209
|
-
|
287
|
+
string: true
|
210
288
|
```
|
211
289
|
- **Converter: Boolean**
|
212
290
|
Cast explicit to bool (by double negotiation). Doesn't work with collections.
|
213
291
|
Can return unexpected values, e.g. a double negotiated empty array is true! `!![] #=> true`
|
214
292
|
|
215
293
|
```ruby
|
216
|
-
|
294
|
+
bool: true
|
217
295
|
```
|
218
296
|
- **Converter: keys**
|
219
297
|
This maps the hash keys when the input data is a hash or when you select multiple *from* fields. Only works with hashes.
|
220
298
|
Return the original data when the data isn't a hash.
|
221
299
|
|
222
300
|
```ruby
|
223
|
-
|
224
|
-
|
225
|
-
|
301
|
+
keys: {
|
302
|
+
'address1' => 'street'
|
303
|
+
}
|
226
304
|
```
|
227
305
|
- **Converter: Prefix**
|
228
306
|
This prefixes the data with a given value. Call `to_s` on data and always returns a string.
|
229
307
|
|
230
308
|
```ruby
|
231
|
-
|
309
|
+
prefix: '$'
|
232
310
|
```
|
233
311
|
- **Converter: Postfix**
|
234
312
|
This postfixes the data with a given value. Call `to_s` on data and always returns a string.
|
235
313
|
|
236
314
|
```ruby
|
237
|
-
|
315
|
+
postfix: '€'
|
238
316
|
```
|
239
317
|
- **Converter: ruby**
|
240
318
|
Apply any method on the current data object.
|
241
319
|
|
242
320
|
```ruby
|
243
|
-
|
244
|
-
|
245
|
-
|
321
|
+
ruby: :upcase
|
322
|
+
ruby: [:slice, 5]
|
323
|
+
ruby: [:join, ', ']
|
246
324
|
```
|
247
325
|
- **Converter: custom**
|
248
326
|
Define your own *converter* by defining them in the `DataMaps::Converter` module.
|
@@ -250,15 +328,15 @@ Apply one or many converters to the input data. Converters applied procedural.
|
|
250
328
|
You have to extend the `DataMaps::Converter::Base` class. Then all options are available via the `option` attribute reader.
|
251
329
|
|
252
330
|
```ruby
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
end
|
331
|
+
class DataMaps::Converter::ToPersonObject < DataMaps::Converter::Base
|
332
|
+
def execute(data)
|
333
|
+
Person.new(data, option)
|
257
334
|
end
|
335
|
+
end
|
258
336
|
```
|
259
337
|
|
260
338
|
```ruby
|
261
|
-
|
339
|
+
to_person_object: { as: :importer } # passed value are available with option
|
262
340
|
```
|
263
341
|
|
264
342
|
Have fun using the `DataMaps` gem :)
|
data/data_maps.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['axel.wahlen@mixxt.de']
|
11
11
|
spec.summary = %q{ Maps data to another structure through a mapping }
|
12
12
|
spec.description = %q{ Maps data to another structure through a mapping }
|
13
|
-
spec.homepage = ''
|
13
|
+
spec.homepage = 'https://github.com/dino115/data_maps'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
data/lib/data_maps/statement.rb
CHANGED
@@ -45,7 +45,11 @@ module DataMaps
|
|
45
45
|
# @param [mixed] data
|
46
46
|
# @return [Array] key and value of the result
|
47
47
|
def execute(data)
|
48
|
-
data =
|
48
|
+
data = case from
|
49
|
+
when Array then from.reduce(data) { |val, f| val.fetch(f) if val.is_a?(Hash) }
|
50
|
+
when Hash then Hash[from.map { |k,v| [v.is_a?(TrueClass) ? k.to_s : v.to_s, data[k.to_s]] }]
|
51
|
+
else data[from.to_s]
|
52
|
+
end
|
49
53
|
|
50
54
|
data = execute_conditions(data)
|
51
55
|
data = execute_converter(data)
|
data/lib/data_maps/version.rb
CHANGED
@@ -37,20 +37,34 @@ describe DataMaps::Statement do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
describe '#execute' do
|
40
|
-
it 'slices
|
40
|
+
it 'slices the source data from a single field' do
|
41
41
|
statement = DataMaps::Statement.new('from', 'to', [], [])
|
42
42
|
data = { 'from' => 'some value' }
|
43
43
|
|
44
44
|
expect(statement.execute(data)).to eq ['to', 'some value']
|
45
45
|
end
|
46
46
|
|
47
|
-
it 'slices
|
47
|
+
it 'slices source data from a field chain (array of fields)' do
|
48
48
|
statement = DataMaps::Statement.new(%w[ from1 from2 ], 'to', [], [])
|
49
|
+
data = { 'from1' => { 'from2' => 'my value', 'from3' => 'my second value' }, 'from4' => 'another value' }
|
50
|
+
|
51
|
+
expect(statement.execute(data)).to eq ['to', 'my value']
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'slices source data from a collection of fields (hash of fields)' do
|
55
|
+
statement = DataMaps::Statement.new({ from1: true, from2: true }, 'to', [], [])
|
49
56
|
data = { 'from1' => 'first value', 'from2' => 'second value', 'from3' => 'third value' }
|
50
57
|
|
51
58
|
expect(statement.execute(data)).to eq ['to', { 'from1' => 'first value', 'from2' => 'second value' }]
|
52
59
|
end
|
53
60
|
|
61
|
+
it 'slices source data from a collection of fields with mapping (hash of fields)' do
|
62
|
+
statement = DataMaps::Statement.new({ from1: 'field1', from2: 'field2' }, 'to', [], [])
|
63
|
+
data = { 'from1' => 'first value', 'from2' => 'second value', 'from3' => 'third value' }
|
64
|
+
|
65
|
+
expect(statement.execute(data)).to eq ['to', { 'field1' => 'first value', 'field2' => 'second value' }]
|
66
|
+
end
|
67
|
+
|
54
68
|
it 'calls execute_conditions with data' do
|
55
69
|
statement = DataMaps::Statement.new('from', 'to', [], [])
|
56
70
|
data = { 'from' => 'some value' }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: data_maps
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Axel Wahlen
|
@@ -147,7 +147,7 @@ files:
|
|
147
147
|
- spec/data_maps/when/empty_spec.rb
|
148
148
|
- spec/data_maps/when/regex_spec.rb
|
149
149
|
- spec/spec_helper.rb
|
150
|
-
homepage:
|
150
|
+
homepage: https://github.com/dino115/data_maps
|
151
151
|
licenses:
|
152
152
|
- MIT
|
153
153
|
metadata: {}
|