hash_remapper 0.3.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd1eb9b9c2a9a1ed5379213e72f50a3dc5dcbf93d4fd6deedb4e5a90c9cb4e7d
4
- data.tar.gz: c565d0549390a2d4264e170aa74ef1d7e106201f8f38674a627d1c6aa4b3e6d1
3
+ metadata.gz: c683439dd0fa1c9ed4829b996ef023248412faa7c40062da5195e6c7f639b610
4
+ data.tar.gz: 169160ed106c2da575ebbd2e8ee3a09432fdecfcb100af63f899ac08ccafd689
5
5
  SHA512:
6
- metadata.gz: 9ac29a074171eac2b77c34b00a703b800bfaa9e0ceb3a8d1c498a09d1c8db4ecd616cc298ee2cc4e5b52f01183c4d697be4857fd2e3fcd25580eeb6cd822576c
7
- data.tar.gz: b8d7a86a12c5c375031bb3a9a42bc972f94f6d46bb4c0690f52177b1bf2215a496479783a877e069d9522f72346adde9827336fea3001714e69e4f0dbce8f5fc
6
+ metadata.gz: 10b4f4686588c177567f5f7a0653970b71e18f7bd2919166b9bca568c4ab80a020b69b7ecdd96cce2d3291359c02e7ba6ad43447053f577a768ea41c31360ed0
7
+ data.tar.gz: 78fdc8e7fba05285373ac1dd8395b6d5aeec76b892ddffb1508cd79c1a34dda19283da5e7014065e30f96ce7eccbba6758231fb93f0868534f584509da600c8a
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.1
5
- before_install: gem install bundler -v 1.16
4
+ - 2.6.0
5
+ before_install: gem install bundler -v 2.0.1
data/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  </p>
6
6
 
7
7
  > A little lib which maps original keys to the new ones <sup>[*](#features)</sup>
8
+ > [![Build Status](https://travis-ci.org/smileart/hash_remapper.svg?branch=master)](https://travis-ci.org/smileart/hash_remapper)
8
9
 
9
10
  ## Installation
10
11
 
@@ -29,13 +30,13 @@ Or install it yourself as:
29
30
  * preprocess a value with a lambda [3](#lambda)
30
31
  * allows to remap the keys within preprocessing [4](#preprocessing)
31
32
  * allows to keep data subsets only [5](#subset)
32
- * allows to include data with the original keynames [6](#originals)
33
+ * allows to include data with the original key names [6](#originals)
33
34
  * allows to use global context to create composite fields [7](#composite)
34
35
  * merges values if the key already exists and supports #merge [8](#merge)
35
36
  * replaces values if the key already exists and doesn't support #merge [9](#replace)
36
37
  * allows to assign static defaults through lambdas [10](#defaults)
37
38
  * allows to remap to the deep values within the context [11](#deep)
38
- * allows to create completely new keys [12](#new_keys)
39
+ * allows to create completely new keys (including nested ones) [12](#new_keys)
39
40
 
40
41
  ## Usage
41
42
 
@@ -167,7 +168,7 @@ HashRemapper.remap(
167
168
  # }
168
169
  ```
169
170
 
170
- ## <a name="originals">6</a>: Include data with the original keyname
171
+ ## <a name="originals">6</a>: Include data with the original key name
171
172
 
172
173
  ```rb
173
174
  HashRemapper.remap(
@@ -266,7 +267,7 @@ HashRemapper.remap(
266
267
  # }
267
268
  ```
268
269
 
269
- ## <a name="new_keys">12</a>: Create completely new keys
270
+ ## <a name="new_keys">12</a>: Create completely new keys (including nested ones)
270
271
 
271
272
  ```rb
272
273
  HashRemapper.remap(
@@ -280,6 +281,54 @@ HashRemapper.remap(
280
281
  # magic_number: 42,
281
282
  # absolutely_new_key: 'shiny new value'
282
283
  # }
284
+
285
+ HashRemapper.remap(
286
+ original_hash,
287
+ _: [[:nested, :new, :key], :test]
288
+ )
289
+
290
+ # =>
291
+ # {
292
+ # nested: {
293
+ # new: {
294
+ # key: 42
295
+ # }
296
+ # }
297
+ # }
298
+
299
+
300
+ # mapping a deep target from a deep source (BEWARE an old digging API <= v0.1.0)
301
+ HashRemapper.remap(
302
+ original_hash,
303
+ _: [[:nested, :new, :key], [:nested, :really, :deep]]
304
+ )
305
+
306
+ # =>
307
+ # {
308
+ # nested: {
309
+ # new: {
310
+ # key: true
311
+ # }
312
+ # }
313
+ # }
314
+
315
+
316
+ # mapping a deep target from a deep source (new digging API >= v0.2.0)
317
+ HashRemapper.remap(
318
+ original_hash,
319
+ _: [[:new, :deeply, :nested, :value], {path: 'recursive.*.number', strict: false, default: 3.14}]
320
+ )
321
+
322
+ # =>
323
+ # {
324
+ # new: {
325
+ # deeply: {
326
+ # nested: {
327
+ # value: [21, 42, 3.14]
328
+ # }
329
+ # }
330
+ # }
331
+ # }
283
332
  ```
284
333
 
285
334
  ## Development
@@ -0,0 +1,42 @@
1
+ require 'json'
2
+ require_relative '../lib/hash_remapper'
3
+
4
+ # https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22
5
+ weather = JSON.parse('{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],"base":"stations","main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15},"visibility":10000,"wind":{"speed":4.1,"deg":80},"clouds":{"all":90},"dt":1485789600,"sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875},"id":2643743,"name":"London","cod":200}')
6
+
7
+ k_to_c = ->(kelvin) { "#{(kelvin - 273.15).floor}˚C" } # notice the difference with the next example
8
+
9
+ remapped_weather = HashRemapper.remap(
10
+ weather,
11
+ # deep keys creation instead of automerge
12
+ _conditions: [[:current_conditions, :description], {path: 'weather.0', lambda: ->(res){ "#{res[:description]}".capitalize }} ],
13
+ _temperature: [[:current_conditions, :temperature], {path: 'main.temp', lambda: k_to_c}],
14
+
15
+ _clouds: [[:clouds_coverage, :percentage], {path: 'clouds.all'}],
16
+ 'visibility' => ->(vis, _) { [:visibility, "#{vis / 1000}km"] },
17
+ ['sys', 'country'] => ->(country, _) { [[:place, :country], country] },
18
+ 'name' => ->(city, _) { [[:place, :city], city] },
19
+ 'coord' => :geo_coordinates
20
+ )
21
+
22
+ p remapped_weather
23
+
24
+ # =================================================================================================================================
25
+
26
+ k_to_c = ->(kelvin) { { temperature: "#{(kelvin - 273.15).floor}˚C" } } # notice the difference with the previous example
27
+
28
+ remapped_weather = HashRemapper.remap(
29
+ weather,
30
+ # automerge on "conflict"
31
+ _conditions: [:current_conditions, {path: 'weather.0', lambda: ->(res){ { description: "#{res[:description]}".capitalize }}} ],
32
+ _temperature: [:current_conditions, {path: 'main.temp', lambda: k_to_c}], # automerge
33
+ _clouds: [[:clouds_coverage, :percentage], {path: 'clouds.all'}],
34
+ 'visibility' => ->(vis, _) { [:visibility, "#{vis / 1000}km"] },
35
+ ['sys', 'country'] => ->(country, _) { [[:place, :country], country] },
36
+ 'name' => ->(city, _) { [[:place, :city], city] },
37
+
38
+ # notice the nested HashRemapper (alternatively https://devdocs.io/rails~5.0/hash#method-i-deep_symbolize_keys could be used on overall result)
39
+ 'coord' => ->(coord, _) { [:geo_coordinates, HashRemapper.remap(coord, 'lon' => :lon, 'lat' => :lat)] },
40
+ )
41
+
42
+ p remapped_weather
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  # coding: utf-8
3
3
 
4
- lib = File.expand_path('./lib', __FILE__)
4
+ lib = File.expand_path('../lib', __FILE__)
5
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
 
7
- # to avoid double const init warning
8
- require_relative './lib/hash_remapper'
7
+ require 'hash_remapper/version'
9
8
 
10
- Gem::Specification.new do |spec|
9
+ HashRemapper::GEMSPEC = Gem::Specification.new do |spec|
11
10
  spec.name = 'hash_remapper'
12
11
  spec.version = HashRemapper::VERSION
13
12
  spec.authors = ['Serge Bedzhyk']
@@ -26,6 +25,8 @@ Gem::Specification.new do |spec|
26
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
26
  spec.require_paths = ['lib']
28
27
 
28
+ spec.add_dependency 'hash_digger', '~> 0.0', '>= 0.0.4'
29
+
29
30
  spec.add_development_dependency 'bundler', '~> 2.0'
30
31
  spec.add_development_dependency 'byebug', '~> 11.0'
31
32
  spec.add_development_dependency 'inch', '>= 0.9.0.rc1'
@@ -36,6 +37,4 @@ Gem::Specification.new do |spec|
36
37
  spec.add_development_dependency 'rubygems-tasks', '~> 0.2'
37
38
  spec.add_development_dependency 'simplecov', '~> 0.15'
38
39
  spec.add_development_dependency 'yard', '~> 0.9'
39
-
40
- spec.add_runtime_dependency 'hash_digger', '~> 0.0'
41
40
  end
data/lib/hash_remapper.rb CHANGED
@@ -3,11 +3,11 @@
3
3
  require 'set'
4
4
  require 'hash_digger'
5
5
 
6
+ require 'letters' if ENV['DEBUG']
7
+ require 'byebug' if ENV['DEBUG']
8
+
6
9
  # Utility class to map original Hash keys to the new ones
7
10
  class HashRemapper
8
- # Current Library Version
9
- VERSION = '0.3.0'
10
-
11
11
  class << self
12
12
  # Remaps `data` Hash by renaming keys, creating new ones and
13
13
  # optionally aggregating values
@@ -22,19 +22,20 @@ class HashRemapper
22
22
  #
23
23
  # @param [Hash] data the original Hash to remap
24
24
  # @param [Boolean] pass_trough the flag to pass the original key/value pairs (default: false)
25
- # @param [Hash] mapping the Hash which in the simplest case tells how to rename keys
25
+ # @param [Hash] mapping the Hash which in the simplest case tells how to rename keys (source: :target)
26
26
  #
27
27
  # @return [Hash] remapped version of the original Hash
28
28
  # (selected keys only or all the keys if we passed originals)
29
29
  def remap(data, pass_trough = false, mapping)
30
30
  mapping = pass_trough_mapping(data, mapping) if pass_trough
31
31
 
32
- mapping.each_with_object({}) do |(from, to), acc|
32
+ mapping.each_with_object(Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) }) do |(from, to), acc|
33
33
  key, value = try_callable(from, to, data, acc) ||
34
- try_digging(to, data) ||
34
+ try_digging(from, to, data, acc) ||
35
+ try_nesting(from, to, data, acc) ||
35
36
  [to, data[from]]
36
37
 
37
- acc[key] = value
38
+ set_value(acc, key, value)
38
39
  acc
39
40
  end
40
41
  end
@@ -53,11 +54,11 @@ class HashRemapper
53
54
  def try_callable(from, to, data, acc)
54
55
  return unless to.respond_to?(:call)
55
56
 
56
- target_name, target_data = to.call(data[from], data)
57
+ target_name, target_data = to.call(data.dig(*from), data)
57
58
 
58
- if acc.key?(target_name) && acc[target_name].respond_to?(:merge)
59
- target_data = acc[target_name].merge(target_data)
60
- end
59
+ target_data = acc.dig(*target_name).respond_to?(:merge) && target_data.respond_to?(:merge) ?
60
+ acc.dig(*target_name).merge(target_data) :
61
+ target_data
61
62
 
62
63
  [target_name, target_data]
63
64
  end
@@ -66,34 +67,45 @@ class HashRemapper
66
67
  # (if the mapping value is enumerable)
67
68
  # @see https://github.com/smileart/hash_digger
68
69
  #
69
- # @param [Array] to the target key to map to ([new_key, digging_path, strict_flag])
70
+ # @param [Array] to the target key to map to ([new_key, {path: 'a.*.b', strict: false, lambda: ->(res){ … }}])
70
71
  # @param [Hash] data the whole original Hash to use as the digging target
71
72
  #
72
73
  # @return [Array(Object,Object)] key and its value to put to the resulting Hash
73
- def try_digging(to, data)
74
+ def try_digging(_from, to, data, acc)
74
75
  return unless to.respond_to?(:each)
75
76
 
76
77
  digger_args = to.fetch(1)
77
78
 
78
79
  # v0.1.0 backward compartability layer ([new_key, [:digg, :path, :keys]])
79
80
  return [to.first, data.dig(*to.last)] if digger_args.kind_of?(Array)
81
+ return unless digger_args.respond_to?(:fetch)
80
82
 
81
83
  lambda = digger_args.fetch(:lambda) { nil }
82
84
 
83
85
  # @see https://github.com/DamirSvrtan/fasterer — fetch_with_argument_vs_block
84
86
  # @see https://github.com/smileart/hash_digger — digger args
85
- [
86
- to.fetch(0),
87
- HashDigger::Digger.dig(
88
- data: data,
89
- path: digger_args.fetch(:path) { '*' },
90
- strict: digger_args.fetch(:strict) { true },
91
- default: digger_args.fetch(:default) { nil },
92
- &lambda
93
- )
87
+ target_name = to.fetch(0)
88
+
89
+ source_data = HashDigger::Digger.dig(
90
+ data: data,
91
+ path: digger_args.fetch(:path) { '*' },
92
+ strict: digger_args.fetch(:strict) { true },
93
+ default: digger_args.fetch(:default) { nil },
94
+ &lambda
95
+ )
96
+
97
+ return [
98
+ target_name,
99
+ acc.dig(*target_name).respond_to?(:merge) && source_data.respond_to?(:merge) ?
100
+ acc.dig(*target_name).merge(source_data) :
101
+ source_data
94
102
  ]
95
103
  end
96
104
 
105
+ def try_nesting(_from, to, data, _acc)
106
+ [to.fetch(0), data[to.fetch(1)]] if to.respond_to?(:fetch)
107
+ end
108
+
97
109
  # Method which automatically prepares direct mapping (e.g. { :a => :a })
98
110
  # for the keys that weren't used in the mapping Hash (to pass them through "as is")
99
111
  #
@@ -110,5 +122,16 @@ class HashRemapper
110
122
 
111
123
  mapping.merge(pass_trough_mapping)
112
124
  end
125
+
126
+ def set_value(data, key, value)
127
+ unless key.respond_to?(:each)
128
+ data[key] = value
129
+ return data
130
+ end
131
+
132
+ last_key = key.pop
133
+
134
+ data.dig(*key)[last_key] = value
135
+ end
113
136
  end
114
137
  end
@@ -0,0 +1,4 @@
1
+ class HashRemapper
2
+ # Current Library Version
3
+ VERSION = '0.4.2'
4
+ end
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_remapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Serge Bedzhyk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-09 00:00:00.000000000 Z
11
+ date: 2019-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hash_digger
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.0.4
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: bundler
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -150,20 +170,6 @@ dependencies:
150
170
  - - "~>"
151
171
  - !ruby/object:Gem::Version
152
172
  version: '0.9'
153
- - !ruby/object:Gem::Dependency
154
- name: hash_digger
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '0.0'
160
- type: :runtime
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '0.0'
167
173
  description: A little lib which maps original keys to the new ones and more
168
174
  email: smileart21@gmail.com
169
175
  executables: []
@@ -182,9 +188,11 @@ files:
182
188
  - Rakefile
183
189
  - bin/console
184
190
  - bin/setup
191
+ - exmaples/weather.rb
185
192
  - hash_remapper.gemspec
186
193
  - img/hash_remapper.png
187
194
  - lib/hash_remapper.rb
195
+ - lib/hash_remapper/version.rb
188
196
  homepage: https://github.com/smileart/hash_remapper
189
197
  licenses:
190
198
  - MIT