darthjee-core_ext 1.7.4 → 3.0.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 +5 -5
- data/.circleci/config.yml +63 -9
- data/.gitignore +2 -0
- data/.reek.yml +3 -0
- data/.rubocop.yml +18 -1
- data/.rubocop_todo.yml +15 -8
- data/ARRAY_README.md +72 -15
- data/CLASS_README.md +154 -0
- data/DATE_README.md +19 -9
- data/Dockerfile +18 -5
- data/ENUMERABLE_README.md +154 -4
- data/Gemfile +13 -0
- data/HASH_README.md +276 -135
- data/MATH_README.md +14 -10
- data/Makefile +7 -0
- data/OBJECT_README.md +18 -35
- data/README.md +12 -4
- data/Rakefile +1 -0
- data/SYMBOL_README.md +13 -18
- data/config/check_specs.yml +20 -0
- data/config/rubycritc.rb +12 -0
- data/config/yardstick.yml +45 -3
- data/core_ext.gemspec +10 -18
- data/docker-compose.yml +24 -7
- data/lib/darthjee/core_ext/array/hash_builder.rb +22 -5
- data/lib/darthjee/core_ext/array.rb +22 -14
- data/lib/darthjee/core_ext/class.rb +29 -10
- data/lib/darthjee/core_ext/date.rb +1 -0
- data/lib/darthjee/core_ext/enumerable.rb +67 -29
- data/lib/darthjee/core_ext/hash/cameliazable.rb +52 -21
- data/lib/darthjee/core_ext/hash/chain_fetcher.rb +18 -1
- data/lib/darthjee/core_ext/hash/changeable.rb +88 -0
- data/lib/darthjee/core_ext/hash/deep_hash_constructor/setter.rb +112 -0
- data/lib/darthjee/core_ext/hash/deep_hash_constructor.rb +127 -62
- data/lib/darthjee/core_ext/hash/key_changeable.rb +138 -66
- data/lib/darthjee/core_ext/hash/key_changer.rb +146 -45
- data/lib/darthjee/core_ext/hash/keys_sorter.rb +43 -6
- data/lib/darthjee/core_ext/hash/squasher.rb +131 -18
- data/lib/darthjee/core_ext/hash/transformable.rb +133 -41
- data/lib/darthjee/core_ext/hash/transposeable.rb +37 -8
- data/lib/darthjee/core_ext/hash/value_changer.rb +76 -36
- data/lib/darthjee/core_ext/hash.rb +6 -4
- data/lib/darthjee/core_ext/math.rb +37 -6
- data/lib/darthjee/core_ext/numeric.rb +13 -2
- data/lib/darthjee/core_ext/object.rb +27 -0
- data/lib/darthjee/core_ext/symbol.rb +22 -2
- data/lib/darthjee/core_ext/version.rb +1 -1
- data/scripts/check_readme.sh +6 -0
- data/scripts/rubycritic.sh +10 -0
- data/spec/integration/readme/array_spec.rb +96 -0
- data/spec/integration/readme/class_spec.rb +123 -0
- data/spec/integration/readme/date_spec.rb +20 -0
- data/spec/integration/readme/enumerable_spec.rb +87 -0
- data/spec/integration/readme/hash_spec.rb +400 -0
- data/spec/integration/readme/math_spec.rb +31 -0
- data/spec/integration/readme/object_spec.rb +49 -0
- data/spec/integration/readme/symbol_spec.rb +29 -0
- data/spec/integration/yard/darthjee/core_ext/array_spec.rb +1 -1
- data/spec/integration/yard/darthjee/core_ext/class/default_value_spec.rb +10 -8
- data/spec/integration/yard/darthjee/core_ext/enumerable_spec.rb +24 -4
- data/spec/integration/yard/darthjee/core_ext/hash/cameliazable_spec.rb +11 -0
- data/spec/integration/yard/darthjee/core_ext/hash/changeable_spec.rb +68 -0
- data/spec/integration/yard/darthjee/core_ext/hash/deep_hash_constructor/setter_spec.rb +34 -0
- data/spec/integration/yard/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +65 -0
- data/spec/integration/yard/darthjee/core_ext/hash/key_changeable_spec.rb +8 -0
- data/spec/integration/yard/darthjee/core_ext/hash/key_changer_spec.rb +59 -0
- data/spec/integration/yard/darthjee/core_ext/hash/keys_sorter_spec.rb +25 -0
- data/spec/integration/yard/darthjee/core_ext/hash/squasher_spec.rb +60 -0
- data/spec/integration/yard/darthjee/core_ext/hash/transformable_spec.rb +67 -37
- data/spec/integration/yard/darthjee/core_ext/hash/transposeable_spec.rb +35 -0
- data/spec/integration/yard/darthjee/core_ext/hash/value_changer_spec.rb +1 -1
- data/spec/integration/yard/darthjee/core_ext/hash_spec.rb +13 -2
- data/spec/integration/yard/darthjee/core_ext/math_spec.rb +28 -0
- data/spec/integration/yard/darthjee/core_ext/numeric_spec.rb +11 -0
- data/spec/integration/yard/darthjee/core_ext/object_spec.rb +47 -0
- data/spec/integration/yard/darthjee/core_ext/symbol_spec.rb +17 -0
- data/spec/lib/array_spec.rb +2 -1
- data/spec/lib/darthjee/core_ext/hash/deep_hash_constructor/setter_spec.rb +64 -0
- data/spec/lib/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +16 -156
- data/spec/lib/darthjee/core_ext/hash/keys_sorter_spec.rb +5 -4
- data/spec/lib/darthjee/core_ext/hash/squasher_spec.rb +8 -2
- data/spec/lib/darthjee/core_ext/hash/value_changer_spec.rb +5 -13
- data/spec/lib/hash_spec.rb +87 -147
- data/spec/lib/object_spec.rb +32 -0
- data/spec/lib/symbol_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/models/client.rb +23 -0
- data/spec/support/models/dummy_iterator.rb +4 -4
- data/spec/support/models/hash/value_changer/dummy.rb +1 -0
- data/spec/support/shared_examples/array/array_random.rb +2 -3
- data/spec/support/shared_examples/hash/chain_hash_keys_changer.rb +1 -1
- data/spec/support/shared_examples/hash/deep_hash.rb +166 -0
- data/spec/support/shared_examples/hash/hash_keys_changer.rb +3 -3
- data/spec/support/shared_examples/hash/hash_squasher.rb +54 -9
- data/spec/support/shared_examples/hash/keys_sorter.rb +266 -6
- data/spec/support/shared_examples/hash/map_to_hash.rb +1 -1
- data/spec/support/shared_examples/hash/remap.rb +4 -4
- data/spec/support/shared_examples/hash/value_changer.rb +2 -2
- metadata +41 -189
- data/lib/darthjee/core_ext/hash/to_hash_mapper.rb +0 -25
- data/spec/lib/darthjee/core_ext/hash/to_hash_mapper_spec.rb +0 -11
@@ -3,17 +3,16 @@
|
|
3
3
|
module Darthjee
|
4
4
|
module CoreExt
|
5
5
|
module Hash
|
6
|
-
# Module holding methods
|
7
|
-
# keys of a hash
|
6
|
+
# Module holding methods for camelizing keys of a hash
|
8
7
|
#
|
9
8
|
# @api public
|
10
9
|
module Cameliazable
|
11
|
-
# Change keys to CamelCase without changing the
|
12
|
-
# original hash
|
10
|
+
# Change keys to CamelCase without changing the original hash
|
13
11
|
#
|
14
12
|
# @return [::Hash] new hash with changed keys
|
15
13
|
# @param [::Hash] options options of camelization
|
16
|
-
# @option options [::
|
14
|
+
# @option options [::TrueClass,::FalseClass]
|
15
|
+
# uppercase_first_letter: flag
|
17
16
|
# defining the type of CamelCase
|
18
17
|
#
|
19
18
|
# @see Hash::KeyChanger#camelize_keys
|
@@ -28,28 +27,28 @@ module Darthjee
|
|
28
27
|
# @example
|
29
28
|
# hash = { first_key: 1, 'second_key' => 2 }
|
30
29
|
# options = { uppercase_first_letter: false }
|
31
|
-
# hash.camelize_keys(options) # returns {
|
30
|
+
# hash.camelize_keys(**options) # returns {
|
32
31
|
# # firstKey: 1,
|
33
32
|
# # 'secondKey' => 2
|
34
33
|
# # }
|
35
34
|
#
|
36
|
-
def camelize_keys(
|
37
|
-
dup.camelize_keys!(
|
35
|
+
def camelize_keys(**)
|
36
|
+
dup.camelize_keys!(**)
|
38
37
|
end
|
39
38
|
|
40
|
-
# Change keys to CamelCase changing the
|
41
|
-
# original hash
|
39
|
+
# Change keys to CamelCase changing the original hash
|
42
40
|
#
|
43
41
|
# @return [::Hash] new hash with changed keys
|
44
42
|
# @param [::Hash] options options of camelization
|
45
|
-
# @option options [::
|
43
|
+
# @option options [::TrueClass,::FalseClass]
|
44
|
+
# uppercase_first_letter: flag
|
46
45
|
# defining the type of CamelCase
|
47
46
|
#
|
48
47
|
# @example (see #camelize_keys)
|
49
48
|
#
|
50
49
|
# @see #camelize_keys
|
51
|
-
def camelize_keys!(
|
52
|
-
Hash::KeyChanger.new(self).camelize_keys(
|
50
|
+
def camelize_keys!(**)
|
51
|
+
Hash::KeyChanger.new(self).camelize_keys(**)
|
53
52
|
end
|
54
53
|
|
55
54
|
# Camelize all keys in the hash as `key.camelize(:lower)
|
@@ -63,8 +62,8 @@ module Darthjee
|
|
63
62
|
# # 'secondKey' => 2
|
64
63
|
# # }
|
65
64
|
#
|
66
|
-
def lower_camelize_keys(
|
67
|
-
dup.lower_camelize_keys!(
|
65
|
+
def lower_camelize_keys(**)
|
66
|
+
dup.lower_camelize_keys!(**)
|
68
67
|
end
|
69
68
|
|
70
69
|
# Camelize all keys in the hash
|
@@ -72,18 +71,50 @@ module Darthjee
|
|
72
71
|
# @return [::Hash] self after changing the keys
|
73
72
|
#
|
74
73
|
# @example (see #lower_camelize_keys)
|
75
|
-
def lower_camelize_keys!(options
|
74
|
+
def lower_camelize_keys!(**options)
|
76
75
|
options = options.merge(uppercase_first_letter: false)
|
77
76
|
|
78
|
-
camelize_keys!(options)
|
77
|
+
camelize_keys!(**options)
|
79
78
|
end
|
80
79
|
|
81
|
-
|
82
|
-
|
80
|
+
# Change all keys to be snakecase
|
81
|
+
#
|
82
|
+
# THis method does not change the original hash
|
83
|
+
#
|
84
|
+
# @param options [::Hash]
|
85
|
+
# @option options recursive [::TrueClass,::FalseClass]
|
86
|
+
# flag for recursive transformation
|
87
|
+
#
|
88
|
+
# @see Hash::KeyChanger#change_keys
|
89
|
+
#
|
90
|
+
# @example underscoring all keys
|
91
|
+
# hash = { firstKey: 1, 'SecondKey' => 2 }
|
92
|
+
#
|
93
|
+
# hash.underscore_keys # returns {
|
94
|
+
# # first_key: 1,
|
95
|
+
# # 'second_key' => 2
|
96
|
+
# # }
|
97
|
+
#
|
98
|
+
# @return [::Hash]
|
99
|
+
def underscore_keys(**)
|
100
|
+
dup.underscore_keys!(**)
|
83
101
|
end
|
84
102
|
|
85
|
-
|
86
|
-
|
103
|
+
# Change all keys to be snakecase
|
104
|
+
#
|
105
|
+
# THis method changes the original hash
|
106
|
+
#
|
107
|
+
# @param options [::Hash]
|
108
|
+
# @option options recursive [::TrueClass,::FalseClass]
|
109
|
+
# flag for recursive transformation
|
110
|
+
#
|
111
|
+
# @see Hash::KeyChanger#change_keys
|
112
|
+
#
|
113
|
+
# @example (see #underscore_keys)
|
114
|
+
#
|
115
|
+
# @return [::Hash]
|
116
|
+
def underscore_keys!(**)
|
117
|
+
Hash::KeyChanger.new(self).underscore_keys(**)
|
87
118
|
end
|
88
119
|
end
|
89
120
|
end
|
@@ -7,6 +7,8 @@ module Darthjee
|
|
7
7
|
#
|
8
8
|
# @api private
|
9
9
|
#
|
10
|
+
# @author Darthjee
|
11
|
+
#
|
10
12
|
# @see Darthjee::CoreExt::Hash#chain_fetch
|
11
13
|
class ChainFetcher
|
12
14
|
def initialize(hash, *keys, &block)
|
@@ -21,13 +23,23 @@ module Darthjee
|
|
21
23
|
#
|
22
24
|
# @return [Object] value fetched from array
|
23
25
|
def fetch
|
24
|
-
block.present?
|
26
|
+
return fetch_with_block if block.present?
|
27
|
+
|
28
|
+
fetch_without_block
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
28
32
|
|
33
|
+
# @private
|
29
34
|
attr_reader :hash, :keys, :block
|
30
35
|
|
36
|
+
# @private
|
37
|
+
#
|
38
|
+
# Perform chain fetch when block is given
|
39
|
+
#
|
40
|
+
# The block will be called in case a key is missed
|
41
|
+
#
|
42
|
+
# @return [Object]
|
31
43
|
def fetch_with_block
|
32
44
|
@hash = hash.fetch(keys.shift) do |*args|
|
33
45
|
missed_keys = keys
|
@@ -37,6 +49,11 @@ module Darthjee
|
|
37
49
|
hash
|
38
50
|
end
|
39
51
|
|
52
|
+
# @private
|
53
|
+
#
|
54
|
+
# Perform chain fetch when block is not given
|
55
|
+
#
|
56
|
+
# @return [Object]
|
40
57
|
def fetch_without_block
|
41
58
|
@hash = hash.fetch(keys.shift) until keys.empty?
|
42
59
|
hash
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Darthjee
|
4
|
+
module CoreExt
|
5
|
+
module Hash
|
6
|
+
# Methods responsible for changing hash values
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
#
|
10
|
+
# @author Darthjee
|
11
|
+
module Changeable
|
12
|
+
# Creates a new hash with changes in its values
|
13
|
+
#
|
14
|
+
# @param options [::Hash]
|
15
|
+
# @option options [::TrueClass,::FalseClass]
|
16
|
+
# recursive (true) flag indicating recursive sorting
|
17
|
+
# @option options [::TrueClass,::FalseClass]
|
18
|
+
# skip_inner (true) Flag indicating to skip running
|
19
|
+
# transformation on Hash objects
|
20
|
+
#
|
21
|
+
# @yield (value) changing value block
|
22
|
+
#
|
23
|
+
# @return [::Hash]
|
24
|
+
#
|
25
|
+
# @example Simple Usage
|
26
|
+
# hash = { a: 1, b: 2 }
|
27
|
+
# hash.change_values do |value|
|
28
|
+
# value + 1
|
29
|
+
# end # returns { a: 2, b: 3 }
|
30
|
+
#
|
31
|
+
# @example Skipping inner hash transformation
|
32
|
+
# hash = { a: 1, b: { c: 1 } }
|
33
|
+
#
|
34
|
+
# hash.change_values(&:to_s)) # returns {
|
35
|
+
# # a: "1",
|
36
|
+
# # b: { c: "1" }
|
37
|
+
# # }
|
38
|
+
#
|
39
|
+
# @example Not skipping inner hash transformation
|
40
|
+
# hash = { a: 1, b: { c: 1 } }
|
41
|
+
#
|
42
|
+
# hash.change_values(skip_inner: false, &:to_s))
|
43
|
+
# # returns {
|
44
|
+
# # a: "1",
|
45
|
+
# # b: "{:c=>1}"
|
46
|
+
# # }
|
47
|
+
def change_values(**, &)
|
48
|
+
deep_dup.change_values!(**, &)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Changes the values of a hash
|
52
|
+
#
|
53
|
+
# @param options [::Hash]
|
54
|
+
# @option options [::TrueClass,::FalseClass]
|
55
|
+
# recursive (true) flag indicating recursive sorting
|
56
|
+
# @option options [::TrueClass,::FalseClass]
|
57
|
+
# skip_inner (true) Flag indicating to skip running
|
58
|
+
# transformation on Hash objects
|
59
|
+
#
|
60
|
+
# @yield (value) changing value block
|
61
|
+
#
|
62
|
+
# @return [::Hash]
|
63
|
+
#
|
64
|
+
# @example (see change_values)
|
65
|
+
#
|
66
|
+
# @example Changing inner hash
|
67
|
+
# inner_hash = { c: 2 }
|
68
|
+
# hash = { a: 1, b: inner_hash }
|
69
|
+
#
|
70
|
+
# hash.change_values!(&:to_s)
|
71
|
+
#
|
72
|
+
# inner_hash # changed to { c: "2" }
|
73
|
+
#
|
74
|
+
# @example Not changing inner hash
|
75
|
+
# inner_hash = { c: 2 }
|
76
|
+
# hash = { a: 1, b: inner_hash }
|
77
|
+
#
|
78
|
+
# hash.change_values!(skip_inner: false, &:to_s)
|
79
|
+
#
|
80
|
+
# hash # changed to { a: "1", b: "{:c=>2}" }
|
81
|
+
# inner_hash # still { c: 2 }
|
82
|
+
def change_values!(**, &)
|
83
|
+
Hash::ValueChanger.new(**, &).change(self)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Darthjee
|
4
|
+
module CoreExt
|
5
|
+
module Hash
|
6
|
+
class DeepHashConstructor
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# Class responsible for setting value localized inside hash
|
10
|
+
#
|
11
|
+
# @example Simple usage
|
12
|
+
# hash = {}
|
13
|
+
# base = 'person'
|
14
|
+
# setter = Darthjee::CoreExt::Hash::DeepHashConstructor::Setter.new(
|
15
|
+
# hash, base
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
# setter.set('age', 21)
|
19
|
+
#
|
20
|
+
# hash # changed to {
|
21
|
+
# # 'person' => {
|
22
|
+
# # 'age' => 21,
|
23
|
+
# # }
|
24
|
+
# # }
|
25
|
+
class Setter
|
26
|
+
# @param hash [Hash] hash to be changed
|
27
|
+
# @param base_key [::String] base key of hash where
|
28
|
+
# subhash will be created
|
29
|
+
def initialize(hash, base_key)
|
30
|
+
@hash = hash
|
31
|
+
@base_key = base_key
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets a value in the correct key inside the hash
|
35
|
+
#
|
36
|
+
# @param key [::String,::NilClass] key where value will live
|
37
|
+
# @param value [::Object] value to be set
|
38
|
+
#
|
39
|
+
# @return [::Object] value
|
40
|
+
#
|
41
|
+
# @example (see DeepHashConstructor)
|
42
|
+
#
|
43
|
+
# @example With Array index
|
44
|
+
# hash = {}
|
45
|
+
# base = 'person[0]'
|
46
|
+
# setter = Darthjee::CoreExt::Hash::DeepHashConstructor::Setter.new(
|
47
|
+
# hash, base
|
48
|
+
# )
|
49
|
+
#
|
50
|
+
# setter.set('age', 21)
|
51
|
+
#
|
52
|
+
# hash # changed to {
|
53
|
+
# # 'person' => [{
|
54
|
+
# # 'age' => 21,
|
55
|
+
# # }]
|
56
|
+
# # }
|
57
|
+
def set(key, value)
|
58
|
+
return hash[base_key] = value unless key || index
|
59
|
+
return array[index] = value unless key
|
60
|
+
|
61
|
+
sub_hash[key] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# @private
|
67
|
+
attr_reader :hash, :base_key
|
68
|
+
|
69
|
+
# @private
|
70
|
+
# Extract index of array from base_key
|
71
|
+
#
|
72
|
+
# @return [::NilClass,::Integer]
|
73
|
+
def index
|
74
|
+
return @index if instance_variable_defined?('@index')
|
75
|
+
|
76
|
+
match = base_key.match(/\[([^)]+)\]/)
|
77
|
+
return @index = nil unless match
|
78
|
+
|
79
|
+
@index = match[1].to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
# @private
|
83
|
+
# Returns array that will receive value
|
84
|
+
#
|
85
|
+
# This array is only created / used when base_key
|
86
|
+
# contains index
|
87
|
+
#
|
88
|
+
# @return [::Array]
|
89
|
+
def array
|
90
|
+
return @array if instance_variable_defined?('@array')
|
91
|
+
|
92
|
+
key_without_index = base_key.tr("[#{index}]", '')
|
93
|
+
|
94
|
+
@array = hash[key_without_index] ||= []
|
95
|
+
end
|
96
|
+
|
97
|
+
# @private
|
98
|
+
# Returns sub hash that will receive the value
|
99
|
+
#
|
100
|
+
# This sub_hash is only build when key is not nil
|
101
|
+
#
|
102
|
+
# @return [::Hash]
|
103
|
+
def sub_hash
|
104
|
+
return array[index] ||= {} if index
|
105
|
+
|
106
|
+
hash[base_key] ||= {}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -3,90 +3,155 @@
|
|
3
3
|
module Darthjee
|
4
4
|
module CoreExt
|
5
5
|
module Hash
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @author Darthjee
|
9
|
+
#
|
10
|
+
# Class responsible for creating a Hash deep hash
|
11
|
+
#
|
12
|
+
# Deep hash construction happens when a hash of one layer
|
13
|
+
# (no sub hashes) has keys that, once explitted, can be
|
14
|
+
# assembled in a hash with many layers
|
15
|
+
#
|
16
|
+
# @see Transformable#squash
|
17
|
+
# @see Transformable#to_deep_hash
|
18
|
+
#
|
19
|
+
# @example (see Transformable#to_deep_hash)
|
20
|
+
# @example General Usage
|
21
|
+
# hash = {
|
22
|
+
# 'account.person.name[0]' => 'John',
|
23
|
+
# 'account.person.name[1]' => 'Wick',
|
24
|
+
# 'account.person.age' => 20,
|
25
|
+
# 'account.number' => '102030',
|
26
|
+
# :'house.number' => 67,
|
27
|
+
# :'house.zip' => 12_345
|
28
|
+
# }
|
29
|
+
#
|
30
|
+
# constructor = Darthjee::CoreExt::Hash::DeepHashConstructor.new('.')
|
31
|
+
#
|
32
|
+
# constructor.deep_hash(hash) # returns {
|
33
|
+
# # 'account' => {
|
34
|
+
# # 'person' => {
|
35
|
+
# # 'name' => ['John', 'Wick'],
|
36
|
+
# # 'age' => 20
|
37
|
+
# # },
|
38
|
+
# # 'number' => '102030',
|
39
|
+
# # },
|
40
|
+
# # 'house' => {
|
41
|
+
# # 'number' => 67,
|
42
|
+
# # 'zip' => 12345
|
43
|
+
# # }
|
44
|
+
# # }
|
6
45
|
class DeepHashConstructor
|
7
|
-
|
46
|
+
autoload :Setter, "#{PATH}/hash/deep_hash_constructor/setter"
|
8
47
|
|
9
|
-
|
48
|
+
# @param separator [::String] keys splitter
|
49
|
+
def initialize(separator = '.')
|
10
50
|
@separator = separator
|
11
51
|
end
|
12
52
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
53
|
+
# Performs deep hash transformation
|
54
|
+
#
|
55
|
+
# @param hash [::Hash] On layered hash
|
56
|
+
#
|
57
|
+
# @return [::Hash] Many layered hash
|
58
|
+
#
|
59
|
+
# @example (see DeepHashConstructor)
|
60
|
+
def deep_hash(hash)
|
61
|
+
break_keys(hash).tap do
|
62
|
+
hash.keys.each do |key|
|
63
|
+
hash[key] = deep_hash_value(hash[key])
|
64
|
+
end
|
20
65
|
end
|
21
66
|
end
|
22
67
|
|
23
68
|
private
|
24
69
|
|
25
|
-
|
26
|
-
|
27
|
-
value.is_a?(Hash) ? deep_hash(value) : value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def hash_deep_hash(hash)
|
32
|
-
{}.tap do |new_hash|
|
33
|
-
hash.each do |k, v|
|
34
|
-
base_key, child_key = split_key(k, separator)
|
35
|
-
set_deep_hash_positioned_value(new_hash, base_key, v, child_key)
|
36
|
-
end
|
70
|
+
# @private
|
71
|
+
attr_reader :separator
|
37
72
|
|
38
|
-
|
39
|
-
|
40
|
-
|
73
|
+
# @private
|
74
|
+
# break the keys creating sub-hashes
|
75
|
+
#
|
76
|
+
# @param hash [::Hash] hash to be broken
|
77
|
+
#
|
78
|
+
# @example Breaking many level keys
|
79
|
+
# hash = {
|
80
|
+
# 'account.person.name[0]' => 'John',
|
81
|
+
# 'account.person.name[1]' => 'Wick',
|
82
|
+
# 'account.person.age' => 20,
|
83
|
+
# 'account.number' => '102030',
|
84
|
+
# :'house.number' => 67,
|
85
|
+
# :'house.zip' => 12_345
|
86
|
+
# }
|
87
|
+
#
|
88
|
+
# constructor = Darthjee::CoreExt::Hash::DeepHashConstructor.new('.')
|
89
|
+
#
|
90
|
+
# constructor.send(:break_keys, hash)
|
91
|
+
#
|
92
|
+
# # Returns {
|
93
|
+
# # 'account' => {
|
94
|
+
# # %w[person name[0]] => 'John',
|
95
|
+
# # %w[person name[1]] => 'Wick',
|
96
|
+
# # %w[person age] => 20,
|
97
|
+
# # %w[number] => '102030'
|
98
|
+
# # },
|
99
|
+
# # 'house' => {
|
100
|
+
# # %w[number] => 67,
|
101
|
+
# # %w[zip] => 12_345
|
102
|
+
# # }
|
103
|
+
# # }
|
104
|
+
#
|
105
|
+
# @return [Hash]
|
106
|
+
def break_keys(hash)
|
107
|
+
hash.keys.each do |key|
|
108
|
+
value = hash.delete(key)
|
109
|
+
base_key, child_key = split_key(key, separator)
|
110
|
+
Setter.new(hash, base_key).set(child_key, value)
|
41
111
|
end
|
42
|
-
end
|
43
112
|
|
44
|
-
|
45
|
-
separator_rxp = separator == '.' ? "\\#{separator}" : separator
|
46
|
-
skipper = "[^#{separator}]"
|
47
|
-
regexp = Regexp.new("^(#{skipper}*)#{separator_rxp}(.*)")
|
48
|
-
match = key.match(regexp)
|
49
|
-
|
50
|
-
match ? match[1..2] : key
|
113
|
+
hash
|
51
114
|
end
|
52
115
|
|
53
|
-
|
54
|
-
|
55
|
-
|
116
|
+
# @private
|
117
|
+
#
|
118
|
+
# Recursively proccess a value calling deep hash on it
|
119
|
+
#
|
120
|
+
# @return [::Object]
|
121
|
+
def deep_hash_value(object)
|
122
|
+
return array_deep_hash(object) if object.is_a? Array
|
123
|
+
return deep_hash(object) if object.is_a? Hash
|
56
124
|
|
57
|
-
|
58
|
-
hash[key_without_index][index] = value
|
59
|
-
else
|
60
|
-
hash[key_without_index][index] ||= {}
|
61
|
-
hash[key_without_index][index][key] = value
|
62
|
-
end
|
125
|
+
object
|
63
126
|
end
|
64
127
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
128
|
+
# @private
|
129
|
+
#
|
130
|
+
# Map array performing deep hash on its Hash elements
|
131
|
+
#
|
132
|
+
# @param array [::Array] array to be mapped
|
133
|
+
#
|
134
|
+
# @return [::Array]
|
135
|
+
def array_deep_hash(array)
|
136
|
+
array.map do |value|
|
137
|
+
deep_hash_value(value)
|
75
138
|
end
|
76
139
|
end
|
77
140
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
141
|
+
# @private
|
142
|
+
#
|
143
|
+
# Split key into array of keys
|
144
|
+
#
|
145
|
+
# @param key [::String,::Symbol] key to be splitted
|
146
|
+
# @param separator [::String] string of key splitting
|
147
|
+
#
|
148
|
+
# @return [::Array<::String>,::String]
|
149
|
+
def split_key(key, separator)
|
150
|
+
keys = key.is_a?(Array) ? key : key.to_s.split(separator)
|
82
151
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
else
|
87
|
-
hash[base_key] ||= {}
|
88
|
-
hash[base_key][key] = value
|
89
|
-
end
|
152
|
+
return keys unless keys.second
|
153
|
+
|
154
|
+
[keys.shift, keys]
|
90
155
|
end
|
91
156
|
end
|
92
157
|
end
|