nice_hash 1.15.2 → 1.15.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/.yardopts +1 -1
- data/lib/nice/hash/change_one_by_one.rb +49 -0
- data/lib/nice/hash/compare_structure.rb +69 -0
- data/lib/nice/hash/delete_nested.rb +37 -0
- data/lib/nice/hash/generate.rb +209 -0
- data/lib/nice/hash/get_all_keys.rb +26 -0
- data/lib/nice/hash/get_values.rb +152 -0
- data/lib/nice/hash/nice_filter.rb +53 -0
- data/lib/nice/hash/pattern_fields.rb +89 -0
- data/lib/nice/hash/select_fields.rb +75 -0
- data/lib/nice/hash/select_key.rb +56 -0
- data/lib/nice/hash/set_nested.rb +54 -0
- data/lib/nice/hash/set_values.rb +99 -0
- data/lib/nice/hash/transtring.rb +32 -0
- data/lib/nice/hash/validate.rb +196 -0
- data/lib/nice_hash.rb +16 -1133
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e1518b1eb83a00333a4051d91aa0b40fb9fe58e4b0b3ad229fcc78b0f86eb56
|
|
4
|
+
data.tar.gz: 69c215835f068a459dfc0e01605c45c42a5447e4e818eeabfac3f90a746c6012
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f61c5845665e7dcddf367e2850afb0d83b95bef3d39c8931be7bce5f27ffc24d6352b5c13d9a600f0d128fc25b1ff8332a2796943abf7f6fe647c4ac9739830e
|
|
7
|
+
data.tar.gz: 6f5ae2c245b192ee7ee331bf6d1a990551dd5e57e93997d168e83a3acfa2d471649ca83e06efdc0a3e18f4f948b1a7e594a6bdd5a2de916745bc9339e2228994
|
data/.yardopts
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
###########################################################################
|
|
3
|
+
# Change only one value at a time and return an Array of Hashes
|
|
4
|
+
# Let's guess we need to test a typical registration REST service and the service has many fields with many validations but we want to test it one field at a time.
|
|
5
|
+
# This method generates values following the patterns on patterns_hash and generates a new hash for every pattern/select field found on patterns_hash using the value supplied on values_hash
|
|
6
|
+
# input:
|
|
7
|
+
# patterns_hash:
|
|
8
|
+
# (Hash) Hash where we have defined the patterns to follow.
|
|
9
|
+
# (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
|
|
10
|
+
# values_hash: (Hash) Hash of values to use to modify the values generated on patterns_hash
|
|
11
|
+
# output: (Array of Hashes)
|
|
12
|
+
# example:
|
|
13
|
+
# wrong_min_length_hash = my_hash.generate(:correct, errors: :min_length)
|
|
14
|
+
# array_of_hashes = NiceHash.change_one_by_one([my_hash, :correct], wrong_min_length_hash)
|
|
15
|
+
# array_of_hashes.each {|hash_with_one_wrong_field|
|
|
16
|
+
# #Here your code to send through http the JSON data stored in hash_with_one_wrong_field
|
|
17
|
+
# #if you want to know which field is the one that is wrong:
|
|
18
|
+
# res = my_hash.validate(:correct, hash_with_one_wrong_field)
|
|
19
|
+
# }
|
|
20
|
+
###########################################################################
|
|
21
|
+
def NiceHash.change_one_by_one(patterns_hash, values_hash)
|
|
22
|
+
if patterns_hash.kind_of?(Array)
|
|
23
|
+
select_key = patterns_hash[1]
|
|
24
|
+
pattern_hash = patterns_hash[0]
|
|
25
|
+
else
|
|
26
|
+
pattern_hash = patterns_hash
|
|
27
|
+
select_key = []
|
|
28
|
+
end
|
|
29
|
+
array = Array.new
|
|
30
|
+
good_values = NiceHash.generate(pattern_hash, select_key)
|
|
31
|
+
select_keys = pattern_hash.pattern_fields(select_key) + pattern_hash.select_fields(select_key)
|
|
32
|
+
select_keys.each { |field|
|
|
33
|
+
new_hash = Marshal.load(Marshal.dump(good_values))
|
|
34
|
+
# to deal with the case same values... like in pwd1, pwd2, pwd3
|
|
35
|
+
if field[-1].kind_of?(Array)
|
|
36
|
+
last_to_set = field[-1]
|
|
37
|
+
else
|
|
38
|
+
last_to_set = [field[-1]]
|
|
39
|
+
end
|
|
40
|
+
last_to_set.each { |f|
|
|
41
|
+
keys = field[0..-2] << f
|
|
42
|
+
new_hash.bury(keys, values_hash.dig(*keys))
|
|
43
|
+
}
|
|
44
|
+
array << new_hash
|
|
45
|
+
}
|
|
46
|
+
return array
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
##################################################
|
|
3
|
+
# Analyzes the supplied replica and verifies that the structure follows the one supplied on structure
|
|
4
|
+
#
|
|
5
|
+
# @param structure [Array] [Hash] Contains the structure that should follow the replica. It can be a nested combination of arrays and hashes.
|
|
6
|
+
# @param replica [Array] [Hash] Contains the element to be verified on following the supplied structure. It can be a nested combination of arrays and hashes.
|
|
7
|
+
# @param compare_only_if_exist_key [Boolean] (Default false) If true, in case an element exist on structure but doesn't exist on replica won't be verified.
|
|
8
|
+
# @param patterns [Hash] add verification of data values following the patterns supplied on a one level hash
|
|
9
|
+
#
|
|
10
|
+
# @return [Boolean] true in case replica follows the structure supplied
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# my_structure = [
|
|
14
|
+
# { name: 'xxx',
|
|
15
|
+
# zip: 'yyyy',
|
|
16
|
+
# customer: true,
|
|
17
|
+
# product_ids: [1]
|
|
18
|
+
# }
|
|
19
|
+
# ]
|
|
20
|
+
# my_replica = [ {name: 'Peter Ben', zip: '1121A', customer: false, product_ids: []},
|
|
21
|
+
# {name: 'John Woop', zip: '74014', customer: true, product_ids: [10,120,301]}]
|
|
22
|
+
# NiceHash.compare_structure(my_structure, my_replica)
|
|
23
|
+
# #>true
|
|
24
|
+
##################################################
|
|
25
|
+
def NiceHash.compare_structure(structure, replica, compare_only_if_exist_key = false, patterns = {})
|
|
26
|
+
unless structure.class == replica.class or
|
|
27
|
+
((structure.is_a?(TrueClass) or structure.is_a?(FalseClass)) and (replica.is_a?(TrueClass) or replica.is_a?(FalseClass)))
|
|
28
|
+
puts "NiceHash.compare_structure: different object type #{structure.class} is not #{replica.class}. expected: #{structure.inspect}. found: #{replica.inspect}."
|
|
29
|
+
return false
|
|
30
|
+
end
|
|
31
|
+
success = true
|
|
32
|
+
if structure.is_a?(Hash)
|
|
33
|
+
structure.each do |key, value|
|
|
34
|
+
if patterns.key?(key) and replica.key?(key)
|
|
35
|
+
unless (patterns[key].is_a?(Array) and replica[key].is_a?(Array) and replica[key].empty?) or
|
|
36
|
+
{key => patterns[key]}.validate({key => replica[key]}).empty?
|
|
37
|
+
puts "NiceHash.compare_structure: key :#{key} not following pattern #{patterns[key]}. value: #{replica[key]}"
|
|
38
|
+
success = false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if compare_only_if_exist_key and replica.key?(key)
|
|
43
|
+
unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
|
|
44
|
+
puts "NiceHash.compare_structure: key :#{key} different."
|
|
45
|
+
success = false
|
|
46
|
+
end
|
|
47
|
+
elsif compare_only_if_exist_key == false
|
|
48
|
+
unless replica.key?(key)
|
|
49
|
+
puts "NiceHash.compare_structure: key :#{key} missing."
|
|
50
|
+
success = false
|
|
51
|
+
else
|
|
52
|
+
unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
|
|
53
|
+
puts "NiceHash.compare_structure: key :#{key} different."
|
|
54
|
+
success = false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
elsif structure.is_a?(Array)
|
|
60
|
+
# compare all elements of replica with the structure of the first element on structure
|
|
61
|
+
replica.each do |elem|
|
|
62
|
+
unless compare_structure(structure[0], elem, compare_only_if_exist_key, patterns)
|
|
63
|
+
success = false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
return success
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
##################################################
|
|
3
|
+
# Deletes the supplied nested key
|
|
4
|
+
#
|
|
5
|
+
# @param hash [Hash] The hash we want
|
|
6
|
+
# @param nested_key [Hash] [String] String with the nested key: 'uno.dos.tres' or a hash { uno: {dos: :tres} }
|
|
7
|
+
#
|
|
8
|
+
# @return [Hash]
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# my_hash = { user: {
|
|
12
|
+
# address: {
|
|
13
|
+
# city: 'Madrid',
|
|
14
|
+
# country: 'Spain'
|
|
15
|
+
# },
|
|
16
|
+
# name: 'Peter',
|
|
17
|
+
# age: 33
|
|
18
|
+
# },
|
|
19
|
+
# customer: true
|
|
20
|
+
# }
|
|
21
|
+
# NiceHash.delete_nested(my_hash, 'user.address.city')
|
|
22
|
+
# #>{:user=>{:address=>{:country=>"Spain"}, :name=>"Peter", :age=>33}, :customer=>true}
|
|
23
|
+
##################################################
|
|
24
|
+
def self.delete_nested(hash, nested_key)
|
|
25
|
+
nested_key = transtring(nested_key)
|
|
26
|
+
keys = nested_key.split(".")
|
|
27
|
+
if keys.size == 1
|
|
28
|
+
hash.delete(nested_key.to_sym)
|
|
29
|
+
else
|
|
30
|
+
last_key = keys[-1]
|
|
31
|
+
eval("hash.#{keys[0..(keys.size - 2)].join(".")}.delete(:#{last_key})")
|
|
32
|
+
end
|
|
33
|
+
return hash
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
|
|
3
|
+
###########################################################################
|
|
4
|
+
# It will generate a new hash with the values generated from the string patterns and select fields specified.
|
|
5
|
+
# In case supplied select_hash_key and a subhash specified on a value it will be selected only the value of the key specified on select_hash_key
|
|
6
|
+
# If expected_errors specified the values will be generated with the specified errors.
|
|
7
|
+
# input:
|
|
8
|
+
# pattern_hash: (Hash) Hash we want to use to generate the values
|
|
9
|
+
# select_hash_key: (key value) (optional) The key we want to select on the subhashes
|
|
10
|
+
# expected_errors: (Array) (optional) (alias: errors) To generate the string patterns with the specified errors.
|
|
11
|
+
# The possible values you can specify is one or more of these ones:
|
|
12
|
+
# :length: wrong length, minimum or maximum
|
|
13
|
+
# :min_length: wrong minimum length
|
|
14
|
+
# :max_length: wrong maximum length
|
|
15
|
+
# :value: wrong resultant value
|
|
16
|
+
# :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
|
|
17
|
+
# :excluded_data: the resultant string will include one or more characters that should be excluded. It works only if excluded data supplied on the pattern.
|
|
18
|
+
# :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
|
|
19
|
+
# output: (Hash)
|
|
20
|
+
# The Hash with the select_hash_key selected and the values generated from the string patterns and select fields specified.
|
|
21
|
+
# examples:
|
|
22
|
+
# new_hash = NiceHash.generate(my_hash)
|
|
23
|
+
# #> {:name=>"Peter",
|
|
24
|
+
# :address=>{:wrong=>"\#$$$$$", :correct=>"KZPCzxsWGMLqonesu wbqH"},
|
|
25
|
+
# :city=>{:wrong=>"Germany", :correct=>"Barcelona"},
|
|
26
|
+
# :products=> [
|
|
27
|
+
# {:name=>"gIqkWygmVm", :price=>{:wrong=>"-20", :correct=>"34338330"}},
|
|
28
|
+
# {:name=>"CK68VLIcYf", :price=>{:wrong=>"-20", :correct=>"616066520"}}
|
|
29
|
+
# ]
|
|
30
|
+
# }
|
|
31
|
+
# new_hash = NiceHash.generate(my_hash, :correct)
|
|
32
|
+
# #> {:name=>"Peter",
|
|
33
|
+
# :address=>"juQeAVZjIuWBPsE",
|
|
34
|
+
# :city=>"Madrid",
|
|
35
|
+
# :products=> [
|
|
36
|
+
# {:name=>"G44Ilr0puV", :price=>"477813"},
|
|
37
|
+
# {:name=>"v6ojs79LOp", :price=>"74820"}
|
|
38
|
+
# ]
|
|
39
|
+
# }
|
|
40
|
+
# new_hash = NiceHash.generate(my_hash, :correct, expected_errors: [:min_length])
|
|
41
|
+
# #> {:name=>"Peter",
|
|
42
|
+
# :address=>"ZytjefJ",
|
|
43
|
+
# :city=>"Madri",
|
|
44
|
+
# :products=>[
|
|
45
|
+
# {:name=>"cIBrzeO", :price=>""},
|
|
46
|
+
# {:name=>"5", :price=>""}
|
|
47
|
+
# ]
|
|
48
|
+
# }
|
|
49
|
+
# Using it directly on Hash class, generate(select_hash_key=nil, expected_errors: []) (alias: gen):
|
|
50
|
+
# new_hash = my_hash.generate
|
|
51
|
+
# new_hash = my_hash.gen(:correct)
|
|
52
|
+
# new_hash = my_hash.generate(:correct, errors: [:min_length])
|
|
53
|
+
###########################################################################
|
|
54
|
+
def NiceHash.generate(pattern_hash, select_hash_key = nil, expected_errors: [], **synonyms)
|
|
55
|
+
hashv = Hash.new()
|
|
56
|
+
same_values = Hash.new()
|
|
57
|
+
expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
|
|
58
|
+
if expected_errors.kind_of?(Symbol)
|
|
59
|
+
expected_errors = [expected_errors]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
63
|
+
pattern_hash.each { |key, value|
|
|
64
|
+
if key.kind_of?(Array)
|
|
65
|
+
same_values[key[0]] = key.dup
|
|
66
|
+
same_values[key[0]].shift
|
|
67
|
+
key = key[0]
|
|
68
|
+
end
|
|
69
|
+
if value.kind_of?(Hash)
|
|
70
|
+
if value.keys.include?(select_hash_key)
|
|
71
|
+
value = value[select_hash_key]
|
|
72
|
+
else
|
|
73
|
+
value = NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
if value.kind_of?(String) or value.kind_of?(Symbol)
|
|
77
|
+
if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
78
|
+
hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
|
|
79
|
+
elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
80
|
+
if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round == 0)
|
|
81
|
+
min = value.to_s.split("|").min { |a, b| a.size <=> b.size }
|
|
82
|
+
hashv[key] = min[0..-2] unless min == ""
|
|
83
|
+
end
|
|
84
|
+
if !hashv.keys.include?(key) and (expected_errors.include?(:max_length) or expected_errors.include?(:length))
|
|
85
|
+
max = value.to_s.split("|").max { |a, b| a.size <=> b.size }
|
|
86
|
+
hashv[key] = max + max[-1]
|
|
87
|
+
end
|
|
88
|
+
if expected_errors.include?(:value) or
|
|
89
|
+
expected_errors.include?(:string_set_not_allowed) or
|
|
90
|
+
expected_errors.include?(:required_data)
|
|
91
|
+
if hashv.keys.include?(key)
|
|
92
|
+
v = hashv[key]
|
|
93
|
+
else
|
|
94
|
+
v = value.to_s.split("|").sample
|
|
95
|
+
end
|
|
96
|
+
unless expected_errors.include?(:string_set_not_allowed)
|
|
97
|
+
v = StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
|
|
98
|
+
hashv[key] = v unless value.to_s.split("|").include?(v)
|
|
99
|
+
end
|
|
100
|
+
unless hashv.keys.include?(key)
|
|
101
|
+
one_wrong_letter = StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
|
|
102
|
+
v[rand(v.size)] = one_wrong_letter
|
|
103
|
+
hashv[key] = v unless value.to_s.split("|").include?(v)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
unless hashv.keys.include?(key)
|
|
107
|
+
hashv[key] = value.to_s.split("|").sample
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
hashv[key] = value
|
|
111
|
+
end
|
|
112
|
+
elsif value.kind_of?(Array)
|
|
113
|
+
array_pattern = false
|
|
114
|
+
value.each { |v|
|
|
115
|
+
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
116
|
+
hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
|
|
117
|
+
array_pattern = true
|
|
118
|
+
break
|
|
119
|
+
end
|
|
120
|
+
}
|
|
121
|
+
unless array_pattern
|
|
122
|
+
value_ret = Array.new
|
|
123
|
+
value.each { |v|
|
|
124
|
+
if v.is_a?(Hash)
|
|
125
|
+
ret = NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
|
|
126
|
+
else
|
|
127
|
+
ret = NiceHash.generate({ doit: v }, select_hash_key, expected_errors: expected_errors)
|
|
128
|
+
ret = ret[:doit] if ret.is_a?(Hash) and ret.key?(:doit)
|
|
129
|
+
end
|
|
130
|
+
ret = v if ret.kind_of?(Hash) and ret.size == 0
|
|
131
|
+
value_ret << ret
|
|
132
|
+
}
|
|
133
|
+
hashv[key] = value_ret
|
|
134
|
+
end
|
|
135
|
+
elsif value.kind_of?(Range)
|
|
136
|
+
if expected_errors.empty?
|
|
137
|
+
hashv[key] = rand(value)
|
|
138
|
+
else
|
|
139
|
+
hashv[key] = rand(value)
|
|
140
|
+
expected_errors.each do |er|
|
|
141
|
+
if er == :min_length
|
|
142
|
+
hashv[key] = rand((value.first - value.last)..value.first - 1)
|
|
143
|
+
elsif er == :max_length
|
|
144
|
+
hashv[key] = rand((value.last + 1)..(value.last * 2))
|
|
145
|
+
elsif er == :length
|
|
146
|
+
if rand.round == 1
|
|
147
|
+
hashv[key] = rand((value.first - value.last)..value.first - 1)
|
|
148
|
+
else
|
|
149
|
+
hashv[key] = rand((value.last + 1)..(value.last * 2))
|
|
150
|
+
end
|
|
151
|
+
elsif er == :value
|
|
152
|
+
hashv[key] = :"1-10:N/L/".gen
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
elsif value.kind_of?(Class) and value == DateTime
|
|
157
|
+
if expected_errors.empty?
|
|
158
|
+
hashv[key] = Time.now.stamp
|
|
159
|
+
else
|
|
160
|
+
hashv[key] = Time.now.stamp
|
|
161
|
+
expected_errors.each do |er|
|
|
162
|
+
if er == :min_length
|
|
163
|
+
hashv[key] = hashv[key].chop
|
|
164
|
+
elsif er == :max_length
|
|
165
|
+
hashv[key] = hashv[key] + "Z"
|
|
166
|
+
elsif er == :length
|
|
167
|
+
if rand.round == 1
|
|
168
|
+
hashv[key] = hashv[key].chop
|
|
169
|
+
else
|
|
170
|
+
hashv[key] = hashv[key] + "Z"
|
|
171
|
+
end
|
|
172
|
+
elsif er == :value
|
|
173
|
+
hashv[key][rand(hashv[key].size - 1)] = "1:L".gen
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
elsif value.kind_of?(Module) and value == Boolean
|
|
178
|
+
if expected_errors.empty?
|
|
179
|
+
hashv[key] = (rand.round == 0)
|
|
180
|
+
else
|
|
181
|
+
hashv[key] = (rand.round == 0)
|
|
182
|
+
expected_errors.each do |er|
|
|
183
|
+
if er == :value
|
|
184
|
+
hashv[key] = "1-10:L".gen
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
elsif value.kind_of?(Proc)
|
|
189
|
+
hashv[key] = value.call
|
|
190
|
+
elsif value.kind_of?(Regexp)
|
|
191
|
+
hashv[key] = value.generate(expected_errors: expected_errors)
|
|
192
|
+
else
|
|
193
|
+
hashv[key] = value
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
if same_values.include?(key)
|
|
197
|
+
same_values[key].each { |k|
|
|
198
|
+
hashv[k] = hashv[key]
|
|
199
|
+
}
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
@values = hashv
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
return hashv
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
##################################################
|
|
3
|
+
# Get all the keys of a hash
|
|
4
|
+
#
|
|
5
|
+
# @param hash [Hash] The hash
|
|
6
|
+
#
|
|
7
|
+
# @return [Array]
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# my_hash = { uno: {dos: {tres: 3}} }
|
|
11
|
+
# NiceHash.get_all_keys(my_hash)
|
|
12
|
+
# #>[:uno, :dos, :tres]
|
|
13
|
+
##################################################
|
|
14
|
+
def self.get_all_keys(h)
|
|
15
|
+
h.each_with_object([]) do |(k, v), keys|
|
|
16
|
+
keys << k
|
|
17
|
+
keys.concat(get_all_keys(v)) if v.is_a? Hash
|
|
18
|
+
if v.is_a?(Array)
|
|
19
|
+
v.each do |vv|
|
|
20
|
+
keys.concat(get_all_keys(vv)) if vv.is_a? Hash or vv.is_a? Array
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
|
|
3
|
+
##################################################
|
|
4
|
+
# Get values from the Hash structure (array of Hashes allowed)
|
|
5
|
+
# In case the key supplied doesn't exist in the hash then it will be returned nil for that one
|
|
6
|
+
# input:
|
|
7
|
+
# hashv: a simple hash or a hash containing arrays. Example:
|
|
8
|
+
# example={"id"=>344,
|
|
9
|
+
# "customer"=>{
|
|
10
|
+
# "name"=>"Peter Smith",
|
|
11
|
+
# "phone"=>334334333
|
|
12
|
+
# },
|
|
13
|
+
# "tickets"=>[
|
|
14
|
+
# {"idt"=>345,"name"=>"myFavor1"},
|
|
15
|
+
# {"idt"=>3123},
|
|
16
|
+
# {"idt"=>3145,"name"=>"Special ticket"}
|
|
17
|
+
# ]
|
|
18
|
+
# }
|
|
19
|
+
# keys: one key (string) or an array of keys. key can be a nested key
|
|
20
|
+
# output:
|
|
21
|
+
# a Hash of Arrays with all values found.
|
|
22
|
+
# Example of output with example.get_values("id","name")
|
|
23
|
+
# {"id"=>344, "name"=>["Peter Smith", ["myFavor1", "Special ticket"]]}
|
|
24
|
+
# Example of output with example.get_values("idt")
|
|
25
|
+
# {"idt"=>[345,3123,3145]}
|
|
26
|
+
# Example of output with example.get_values(:'tickets.idt')
|
|
27
|
+
# {:"tickets.idt"=>[345,3123,3145]}
|
|
28
|
+
#
|
|
29
|
+
####################################################
|
|
30
|
+
def NiceHash.get_values(hashv, keys)
|
|
31
|
+
#todo: check if we should return {"id"=>344, "name"=>["Peter Smith", "myFavor1", "Special ticket"]} instead of
|
|
32
|
+
# {"id"=>344, "name"=>["Peter Smith", ["myFavor1", "Special ticket"]]}
|
|
33
|
+
if keys.kind_of?(String) or keys.kind_of?(Symbol)
|
|
34
|
+
keys = [keys]
|
|
35
|
+
end
|
|
36
|
+
if (keys.grep(/\./)).empty?
|
|
37
|
+
nested = false
|
|
38
|
+
else
|
|
39
|
+
nested = true
|
|
40
|
+
end
|
|
41
|
+
result = Hash.new()
|
|
42
|
+
number_of_results = Hash.new()
|
|
43
|
+
keys.each { |key|
|
|
44
|
+
number_of_results[key] = 0
|
|
45
|
+
}
|
|
46
|
+
if hashv.kind_of?(Array)
|
|
47
|
+
hashv.each { |tmp|
|
|
48
|
+
if tmp.kind_of?(Array) or tmp.kind_of?(Hash)
|
|
49
|
+
n_result = get_values(tmp, keys)
|
|
50
|
+
if n_result != :error
|
|
51
|
+
n_result.each { |n_key, n_value|
|
|
52
|
+
if result.has_key?(n_key)
|
|
53
|
+
if !result[n_key].kind_of?(Array) or
|
|
54
|
+
(result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
|
|
55
|
+
if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
|
|
56
|
+
res_tx = result[n_key].dup()
|
|
57
|
+
else
|
|
58
|
+
res_tx = result[n_key]
|
|
59
|
+
end
|
|
60
|
+
result[n_key] = Array.new()
|
|
61
|
+
result[n_key].push(res_tx)
|
|
62
|
+
result[n_key].push(n_value)
|
|
63
|
+
else
|
|
64
|
+
result[n_key].push(n_value)
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
result[n_key] = n_value
|
|
68
|
+
end
|
|
69
|
+
number_of_results[n_key] += 1
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
}
|
|
74
|
+
elsif hashv.kind_of?(Hash)
|
|
75
|
+
hashv.each { |key, value|
|
|
76
|
+
#if keys.include?(key) then
|
|
77
|
+
#added to be able to access the keys with symbols to strings and opposite
|
|
78
|
+
if keys.include?(key) or keys.include?(key.to_s) or keys.include?(key.to_sym)
|
|
79
|
+
#todo: check on next ruby versions since it will be not necessary to do it
|
|
80
|
+
#added to be able to access the keys with symbols to strings and opposite
|
|
81
|
+
key = key.to_s() if keys.include?(key.to_s)
|
|
82
|
+
key = key.to_sym() if keys.include?(key.to_sym)
|
|
83
|
+
|
|
84
|
+
if result.has_key?(key)
|
|
85
|
+
if !result[key].kind_of?(Array) or
|
|
86
|
+
(result[key].kind_of?(Array) and number_of_results[key] < result[key].size)
|
|
87
|
+
if result[key].kind_of?(Hash) or result[key].kind_of?(Array)
|
|
88
|
+
res_tx = result[key].dup()
|
|
89
|
+
else
|
|
90
|
+
res_tx = result[key]
|
|
91
|
+
end
|
|
92
|
+
result[key] = Array.new()
|
|
93
|
+
result[key].push(res_tx)
|
|
94
|
+
result[key].push(value)
|
|
95
|
+
else
|
|
96
|
+
result[key].push(value)
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
result[key] = value
|
|
100
|
+
end
|
|
101
|
+
number_of_results[key] += 1
|
|
102
|
+
end
|
|
103
|
+
if value.kind_of?(Array) or value.kind_of?(Hash)
|
|
104
|
+
if nested
|
|
105
|
+
keys_nested = []
|
|
106
|
+
keys.grep(/^#{key}\./).each do |k|
|
|
107
|
+
keys_nested << k.to_s.gsub(/^#{key}\./,'').to_sym
|
|
108
|
+
end
|
|
109
|
+
n_result_tmp = get_values(value, keys_nested)
|
|
110
|
+
n_result = {}
|
|
111
|
+
n_result_tmp.each do |k,v|
|
|
112
|
+
n_result["#{key}.#{k}".to_sym] = v
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
n_result = get_values(value, keys)
|
|
116
|
+
end
|
|
117
|
+
if n_result != :error
|
|
118
|
+
n_result.each { |n_key, n_value|
|
|
119
|
+
if result.has_key?(n_key)
|
|
120
|
+
if !result[n_key].kind_of?(Array) or
|
|
121
|
+
(result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
|
|
122
|
+
if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
|
|
123
|
+
res_tx = result[n_key].dup()
|
|
124
|
+
else
|
|
125
|
+
res_tx = result[n_key]
|
|
126
|
+
end
|
|
127
|
+
result[n_key] = Array.new()
|
|
128
|
+
result[n_key].push(res_tx)
|
|
129
|
+
result[n_key].push(n_value)
|
|
130
|
+
else
|
|
131
|
+
result[n_key].push(n_value)
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
result[n_key] = n_value
|
|
135
|
+
end
|
|
136
|
+
number_of_results[n_key] += 1
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
return :error
|
|
143
|
+
end
|
|
144
|
+
if result.kind_of?(Hash) and caller[0]["get_values"].nil? #no error or anything weird
|
|
145
|
+
(keys - result.keys).each { |k| #in case some keys don't exist in the hash
|
|
146
|
+
result[k] = nil
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
return result
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|