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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ff13a28428388b271443275bd30d17178780d23
4
- data.tar.gz: 1e501af25f3bed2e788332d01374440ce2a45286
3
+ metadata.gz: 0db231594073294046bbb5835c640cf80e9104a2
4
+ data.tar.gz: 061514db19e260ce0d6920d08c00bfbaaa3b676a
5
5
  SHA512:
6
- metadata.gz: 83fbf366ee2287b51444a37253b285854d669b4e47279fbc45c16a26593c0533d90bb3f899961c0e26774e0b0d15255dd3ea0ac1fc92ef4ca57c8d56edef3c12
7
- data.tar.gz: 621c5f21a176a058321f76eb758e8d55f71ccfd5851e96bc5d77a71848b652d8d503c7d7e2a42fba5fb32704787d02725da9ff0d77cf34b52726763abaa04842
6
+ metadata.gz: d164afd5020057a201912e236cda59b9262d02df41872bed00dfbc56c932aabcdbbcd7c517f777eddc63f9d73ac63f3cb0e07634c8077c09c31445103674d45d
7
+ data.tar.gz: cf4b01f7a57cc76b881ef7ae7ff9fd69f279d35b7b813f02ab8b3196a7e1219b7f41492131921448ee1f3dd489b8cb9f76686518824ac87540f357e2beeadfed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- data_maps (0.1.0)
4
+ data_maps (0.2.0)
5
5
  activesupport (~> 3.2.21)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -3,10 +3,10 @@ DataMaps
3
3
  [![Build Status](https://travis-ci.org/dino115/data_maps.svg?branch=master)](https://travis-ci.org/dino115/data_maps)
4
4
  [![Code Climate](https://codeclimate.com/github/dino115/data_maps/badges/gpa.svg)](https://codeclimate.com/github/dino115/data_maps)
5
5
  [![Test Coverage](https://codeclimate.com/github/dino115/data_maps/badges/coverage.svg)](https://codeclimate.com/github/dino115/data_maps)
6
+ [![Gem Version](https://badge.fury.io/rb/data_maps.svg)](http://badge.fury.io/rb/data_maps)
6
7
 
7
- Create great mappings to convert structured data into your own format!
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 another.
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
- #### Simple field mapping
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
- 'field' => {
59
- from: 'source' # or array of source fields
60
- conditions: [
61
- { when: { empty: true }, then: { set: 'something' },
62
- { when: { regex: /[1-9]{5}/i }, then: { convert: { numeric: 'Integer' } } }
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
- empty: true # or false
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
- regex: /[a-z]/i
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
- gt: 5
88
- gte 5
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
- lt: 5
95
- lte: 5
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
- eq: 10
102
- neq: 'a-value'
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
- in: ['a', 'b', 'c']
109
- nin: ['x', 'y', 'z']
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
- class DataMaps::When::IsZip < DataMaps::When::Base
118
- def execute(data)
119
- !!data.match(/\d{5}/)
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
- is_zip: true # option isn't used, you can pass anything, for example and readability true
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
- set: 'to this value'
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
- convert: {
141
- numeric: 'Integer'
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
- filter: true
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
- class DataMaps::Then::SendEmail < DataMaps::Then::Base
157
- def execute(data)
158
- MyFramework::Email.send(to: option)
159
- data
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
- send_email: me@example.com
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
- 'field' => {
173
- from: 'source',
174
- convert: {
175
- map: {
176
- 1: 'A',
177
- 2: 'B'
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
- map: {
192
- from: to
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
- numeric: 'Integer'
201
- numeric: 'Float'
202
- numeric: 2
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
- string: true
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
- bool: true
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
- keys: {
224
- 'address1' => 'street'
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
- prefix: '$'
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
- postfix: '€'
315
+ postfix: '€'
238
316
  ```
239
317
  - **Converter: ruby**
240
318
  Apply any method on the current data object.
241
319
 
242
320
  ```ruby
243
- ruby: :upcase
244
- ruby: [:slice, 5]
245
- ruby: [:join, ', ']
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
- class DataMaps::Converter::ToPersonObject < DataMaps::Converter::Base
254
- def execute(data)
255
- Person.new(data, option)
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
- to_person_object: { as: :importer } # passed value are available with option
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")
@@ -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 = from.is_a?(Array) ? Hash[from.map{ |f| [f, data[f.to_s]] }] : data[from.to_s]
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)
@@ -1,3 +1,3 @@
1
1
  module DataMaps
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -37,20 +37,34 @@ describe DataMaps::Statement do
37
37
  end
38
38
 
39
39
  describe '#execute' do
40
- it 'slices source_data from a single field' do
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 source_data from an array of fields' do
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.1.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: {}