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
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
class NiceHash
|
|
2
|
+
|
|
3
|
+
###########################################################################
|
|
4
|
+
# Validates a given values_hash_to_validate with string patterns and select fields from pattern_hash
|
|
5
|
+
# input:
|
|
6
|
+
# patterns_hash:
|
|
7
|
+
# (Hash) Hash where we have defined the patterns to follow.
|
|
8
|
+
# (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
|
|
9
|
+
# values_hash_to_validate: (Hash) Hash of values to validate
|
|
10
|
+
# only_patterns: (TrueFalse) (by default true) If true it will validate only the patterns and not the other fields
|
|
11
|
+
# output: (Hash)
|
|
12
|
+
# A hash with the validation results. It will return only the validation errors so in case no validation errors found, empty hash.
|
|
13
|
+
# The keys of the hash will be the keys of the values hash with the validation error.
|
|
14
|
+
# The value in case of a pattern, will be an array with one or more of these possibilities:
|
|
15
|
+
# :length: wrong length, minimum or maximum
|
|
16
|
+
# :min_length: wrong minimum length
|
|
17
|
+
# :max_length: wrong maximum length
|
|
18
|
+
# :value: wrong resultant value
|
|
19
|
+
# :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
|
|
20
|
+
# :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.
|
|
21
|
+
# :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
|
|
22
|
+
# The value in any other case it will be false if the value is not corresponding to the expected.
|
|
23
|
+
# examples:
|
|
24
|
+
# values_to_validate = {:name=>"Peter",
|
|
25
|
+
# :address=>"fnMuKW",
|
|
26
|
+
# :city=>"Dublin",
|
|
27
|
+
# :products=>[{:name=>"V4", :price=>"344"}, {:name=>"E", :price=>"a"}]
|
|
28
|
+
# }
|
|
29
|
+
# results = NiceHash.validate([my_hash, :correct], values_to_validate)
|
|
30
|
+
# #> {:address=>[:min_length, :length],
|
|
31
|
+
# :products=> [{:name=>[:min_length, :length]},
|
|
32
|
+
# {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
|
|
33
|
+
# ]
|
|
34
|
+
# }
|
|
35
|
+
# results = NiceHash.validate([my_hash, :correct], values_to_validate, only_patterns: false)
|
|
36
|
+
# #> {:address=>[:min_length, :length],
|
|
37
|
+
# :city=>false,
|
|
38
|
+
# :products=> [{:name=>[:min_length, :length]},
|
|
39
|
+
# {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
|
|
40
|
+
# ]
|
|
41
|
+
# }
|
|
42
|
+
# Using it directly on Hash class:
|
|
43
|
+
# validate(select_hash_key=nil, values_hash_to_validate) (alias: val)
|
|
44
|
+
# validate_patterns(select_hash_key=nil, values_hash_to_validate)
|
|
45
|
+
#
|
|
46
|
+
# results = my_hash.validate_patterns(:correct, values_to_validate)
|
|
47
|
+
# results = my_hash.validate(:correct, values_to_validate)
|
|
48
|
+
###########################################################################
|
|
49
|
+
def NiceHash.validate(patterns_hash, values_hash_to_validate, only_patterns: true)
|
|
50
|
+
if patterns_hash.kind_of?(Array)
|
|
51
|
+
pattern_hash = patterns_hash[0]
|
|
52
|
+
select_hash_key = patterns_hash[1]
|
|
53
|
+
elsif patterns_hash.kind_of?(Hash)
|
|
54
|
+
pattern_hash = patterns_hash
|
|
55
|
+
select_hash_key = nil
|
|
56
|
+
else
|
|
57
|
+
puts "NiceHash.validate wrong pattern_hash supplied #{patterns_hash.inspect}"
|
|
58
|
+
return { error: :error }
|
|
59
|
+
end
|
|
60
|
+
values = values_hash_to_validate
|
|
61
|
+
if pattern_hash.keys.size == get_all_keys(pattern_hash).size and values.keys.size != get_all_keys(values) and
|
|
62
|
+
pattern_hash.keys.size == pattern_hash.keys.flatten.size # dont't set_values for the case of same_value
|
|
63
|
+
# all patterns on patterns_hash are described on first level, so no same structure than values
|
|
64
|
+
pattern_hash = values.set_values(pattern_hash)
|
|
65
|
+
end
|
|
66
|
+
results = {}
|
|
67
|
+
same_values = {}
|
|
68
|
+
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
69
|
+
pattern_hash.each { |key, value|
|
|
70
|
+
if key.kind_of?(Array)
|
|
71
|
+
same_values[key[0]] = key.dup
|
|
72
|
+
same_values[key[0]].shift
|
|
73
|
+
key = key[0]
|
|
74
|
+
end
|
|
75
|
+
if value.kind_of?(Hash)
|
|
76
|
+
if !select_hash_key.nil? and value.keys.include?(select_hash_key)
|
|
77
|
+
value = value[select_hash_key]
|
|
78
|
+
elsif values.keys.include?(key) and values[key].kind_of?(Hash)
|
|
79
|
+
res = NiceHash.validate([value, select_hash_key], values[key], only_patterns: only_patterns)
|
|
80
|
+
results[key] = res if res.size > 0
|
|
81
|
+
next
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if values.keys.include?(key)
|
|
86
|
+
if value.kind_of?(String) or value.kind_of?(Symbol)
|
|
87
|
+
if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
88
|
+
res = StringPattern.validate(pattern: value, text: values[key])
|
|
89
|
+
results[key] = res if res.size > 0
|
|
90
|
+
elsif !only_patterns and ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
91
|
+
results[key] = false unless value.to_s.split("|").include?(values[key])
|
|
92
|
+
elsif !only_patterns
|
|
93
|
+
results[key] = false unless value.to_s == values[key].to_s
|
|
94
|
+
end
|
|
95
|
+
elsif value.kind_of?(Range)
|
|
96
|
+
if values[key].class != value.first.class or values[key].class != value.last.class
|
|
97
|
+
results[key] = false
|
|
98
|
+
elsif values[key] < value.first or values[key] > value.last
|
|
99
|
+
results[key] = false
|
|
100
|
+
end
|
|
101
|
+
elsif value.kind_of?(Class) and value == DateTime
|
|
102
|
+
if values[key].is_a?(String) and values[key].size == 24
|
|
103
|
+
d = Date.strptime(values[key], "%Y-%m-%dT%H:%M:%S.%LZ") rescue results[key] = false
|
|
104
|
+
elsif values[key].is_a?(Time) or values[key].is_a?(Date) or values[key].is_a?(DateTime)
|
|
105
|
+
# correct
|
|
106
|
+
else
|
|
107
|
+
results[key] = false
|
|
108
|
+
end
|
|
109
|
+
elsif value.kind_of?(Module) and value == Boolean
|
|
110
|
+
results[key] = false unless values[key].is_a?(Boolean)
|
|
111
|
+
elsif value.kind_of?(Regexp)
|
|
112
|
+
rex = Regexp.new("^#{value}$")
|
|
113
|
+
unless values[key].to_s.match?(rex)
|
|
114
|
+
results[key] = false
|
|
115
|
+
end
|
|
116
|
+
elsif value.kind_of?(Array)
|
|
117
|
+
array_pattern = false
|
|
118
|
+
complex_data = false
|
|
119
|
+
value.each { |v|
|
|
120
|
+
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
121
|
+
res = StringPattern.validate(pattern: value, text: values[key])
|
|
122
|
+
results[key] = res if res == false
|
|
123
|
+
array_pattern = true
|
|
124
|
+
break
|
|
125
|
+
elsif v.kind_of?(Hash) or v.kind_of?(Array) or v.kind_of?(Struct)
|
|
126
|
+
complex_data = true
|
|
127
|
+
break
|
|
128
|
+
end
|
|
129
|
+
}
|
|
130
|
+
unless array_pattern or results.include?(key)
|
|
131
|
+
if value.size == 1 and values[key].size > 1
|
|
132
|
+
# for the case value == ['Ford|Newton|Seat'] and values == ['Ford', 'Newton', 'Ford']
|
|
133
|
+
i = 0
|
|
134
|
+
if values[key].class == value.class
|
|
135
|
+
values[key].each do |v|
|
|
136
|
+
if value[0].is_a?(Hash)
|
|
137
|
+
res = NiceHash.validate([value[0], select_hash_key], v, only_patterns: only_patterns)
|
|
138
|
+
else
|
|
139
|
+
# for the case {cars: ['Ford|Newton|Seat']}
|
|
140
|
+
res = NiceHash.validate([{ key => value[0] }, select_hash_key], { key => v }, only_patterns: only_patterns)
|
|
141
|
+
#res = {key => res[:doit]} if res.is_a?(Hash) and res.key?(:doit)
|
|
142
|
+
array_pattern = true
|
|
143
|
+
end
|
|
144
|
+
if res.size > 0
|
|
145
|
+
results[key] = Array.new() if !results.keys.include?(key)
|
|
146
|
+
results[key][i] = res
|
|
147
|
+
end
|
|
148
|
+
i += 1
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
results[key] = false
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
i = 0
|
|
155
|
+
value.each { |v|
|
|
156
|
+
if v.is_a?(Hash)
|
|
157
|
+
res = NiceHash.validate([v, select_hash_key], values[key][i], only_patterns: only_patterns)
|
|
158
|
+
else
|
|
159
|
+
# for the case {cars: ['Ford|Newton|Seat']}
|
|
160
|
+
res = NiceHash.validate([{ key => v }, select_hash_key], { key => values[key][i] }, only_patterns: only_patterns)
|
|
161
|
+
array_pattern = true
|
|
162
|
+
end
|
|
163
|
+
if res.size > 0
|
|
164
|
+
results[key] = Array.new() if !results.keys.include?(key)
|
|
165
|
+
results[key][i] = res
|
|
166
|
+
end
|
|
167
|
+
i += 1
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
unless array_pattern or only_patterns or results.include?(key) or complex_data
|
|
172
|
+
results[key] = false unless value == values[key]
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
unless only_patterns or value.kind_of?(Proc)
|
|
176
|
+
results[key] = false unless value == values[key]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
if same_values.include?(key)
|
|
180
|
+
same_values[key].each { |k|
|
|
181
|
+
if values.keys.include?(k)
|
|
182
|
+
if values[key] != values[k]
|
|
183
|
+
results[k] = "Not equal to #{key}"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
}
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
return results
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
end
|
data/lib/nice_hash.rb
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
|
|
2
|
-
#todo:
|
|
2
|
+
#todo: consider adding SP_USE_NESTED_KEYS = true
|
|
3
3
|
|
|
4
4
|
require_relative "nice/hash/add_to_ruby" if SP_ADD_TO_RUBY
|
|
5
|
+
require_relative "nice/hash/change_one_by_one"
|
|
6
|
+
require_relative "nice/hash/compare_structure"
|
|
7
|
+
require_relative "nice/hash/delete_nested"
|
|
8
|
+
require_relative "nice/hash/generate"
|
|
9
|
+
require_relative "nice/hash/get_all_keys"
|
|
10
|
+
require_relative "nice/hash/get_values"
|
|
11
|
+
require_relative "nice/hash/nice_filter"
|
|
12
|
+
require_relative "nice/hash/pattern_fields"
|
|
13
|
+
require_relative "nice/hash/select_fields"
|
|
14
|
+
require_relative "nice/hash/select_key"
|
|
15
|
+
require_relative "nice/hash/set_nested"
|
|
16
|
+
require_relative "nice/hash/set_values"
|
|
17
|
+
require_relative "nice/hash/transtring"
|
|
18
|
+
require_relative "nice/hash/validate"
|
|
19
|
+
|
|
5
20
|
|
|
6
21
|
require "string_pattern"
|
|
7
22
|
|
|
@@ -33,1136 +48,4 @@ class NiceHash
|
|
|
33
48
|
attr_reader :values
|
|
34
49
|
end
|
|
35
50
|
@values = {}
|
|
36
|
-
|
|
37
|
-
###########################################################################
|
|
38
|
-
# It will filter the hash by the key specified on select_hash_key.
|
|
39
|
-
# In case a subhash specified on a value it will be selected only the value of the key specified on select_hash_key
|
|
40
|
-
#
|
|
41
|
-
# input:
|
|
42
|
-
# pattern_hash: (Hash) Hash we want to select specific keys
|
|
43
|
-
# select_hash_key: (key value) The key we want to select on the subhashes
|
|
44
|
-
# output: (Hash)
|
|
45
|
-
# The same hash but in case a subhash specified on a value it will be selected only the value of the key specified on select_hash_key
|
|
46
|
-
# example:
|
|
47
|
-
# new_hash = NiceHash.select_key(my_hash, :wrong)
|
|
48
|
-
# #> {:name=>"Peter", :address=>"\#$$$$$", :city=>"Germany", :products=> [{:name=>:"10:Ln", :price=>"-20"}, {:name=>:"10:Ln", :price=>"-20"}]}
|
|
49
|
-
# Using it directly on Hash class, select_key(select_hash_key):
|
|
50
|
-
# new_hash = my_hash.select_key(:wrong)
|
|
51
|
-
###########################################################################
|
|
52
|
-
def NiceHash.select_key(pattern_hash, select_hash_key)
|
|
53
|
-
hashv = Hash.new()
|
|
54
|
-
|
|
55
|
-
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
56
|
-
pattern_hash.each { |key, value|
|
|
57
|
-
if value.kind_of?(Hash)
|
|
58
|
-
if value.keys.include?(select_hash_key)
|
|
59
|
-
value = value[select_hash_key]
|
|
60
|
-
else
|
|
61
|
-
value = NiceHash.select_key(value, select_hash_key)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
if value.kind_of?(Array)
|
|
65
|
-
array_pattern = false
|
|
66
|
-
value.each { |v|
|
|
67
|
-
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
68
|
-
hashv[key] = value
|
|
69
|
-
array_pattern = true
|
|
70
|
-
break
|
|
71
|
-
end
|
|
72
|
-
}
|
|
73
|
-
unless array_pattern
|
|
74
|
-
value_ret = Array.new
|
|
75
|
-
value.each { |v|
|
|
76
|
-
ret = NiceHash.select_key(v, select_hash_key)
|
|
77
|
-
value_ret << ret
|
|
78
|
-
}
|
|
79
|
-
hashv[key] = value_ret
|
|
80
|
-
end
|
|
81
|
-
else
|
|
82
|
-
hashv[key] = value
|
|
83
|
-
end
|
|
84
|
-
}
|
|
85
|
-
else
|
|
86
|
-
return pattern_hash
|
|
87
|
-
end
|
|
88
|
-
return hashv
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
###########################################################################
|
|
92
|
-
# It will search for the keys supplied and it will set the value specified
|
|
93
|
-
#
|
|
94
|
-
# input:
|
|
95
|
-
# hash_array: (Hash/Array) Hash/Array we want to search on
|
|
96
|
-
# hash_values: (Hash) Hash that contains the keys and values to set
|
|
97
|
-
# output: (Hash/Array)
|
|
98
|
-
# The same hash/array but with the values we specifed changed
|
|
99
|
-
# example:
|
|
100
|
-
# new_hash = NiceHash.set_values(my_hash, {city: 'London', price: '1000'})
|
|
101
|
-
# #> {:name=>"Peter", :address=>"\#$$$$$", :city=>"London", :products=> [{:name=>:"10:Ln", :price=>"1000"}, {:name=>:"10:Ln", :price=>"1000"}]}
|
|
102
|
-
# Using it directly on Hash class, set_values(hash_values):
|
|
103
|
-
# new_hash = my_hash.set_values({city: 'London', price: '1000'})
|
|
104
|
-
# Setting specific nested keys
|
|
105
|
-
# new_hash = my_hash.set_values({'data.lab.products.price': 75, 'data.lab.beep': false})
|
|
106
|
-
###########################################################################
|
|
107
|
-
def NiceHash.set_values(hash_array, hash_values)
|
|
108
|
-
hashv = Hash.new()
|
|
109
|
-
if hash_array.is_a?(Hash) and hash_array.size > 0
|
|
110
|
-
hash_array.each do |k, v|
|
|
111
|
-
if hash_values.keys.include?(k)
|
|
112
|
-
hashv[k] = hash_values[k]
|
|
113
|
-
elsif v.is_a?(Array)
|
|
114
|
-
if hash_values.has_rkey?('\.') # the kind of 'uno.dos.tres'
|
|
115
|
-
new_hash_values = {}
|
|
116
|
-
hash_values.each do |kk,vv|
|
|
117
|
-
if kk.to_s.match?(/^#{k}\./)
|
|
118
|
-
kk = kk.to_s.gsub(/^#{k}\./, '').to_sym
|
|
119
|
-
new_hash_values[kk] = vv
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
hashv[k] = NiceHash.set_values(v, new_hash_values)
|
|
123
|
-
else
|
|
124
|
-
hashv[k] = NiceHash.set_values(v, hash_values)
|
|
125
|
-
end
|
|
126
|
-
elsif v.is_a?(Hash)
|
|
127
|
-
if hash_values.has_rkey?('\.') # the kind of 'uno.dos.tres'
|
|
128
|
-
new_hash_values = {}
|
|
129
|
-
hash_values.each do |kk,vv|
|
|
130
|
-
if kk.to_s.match?(/^#{k}\./)
|
|
131
|
-
kk = kk.to_s.gsub(/^#{k}\./, '').to_sym
|
|
132
|
-
new_hash_values[kk] = vv
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
hashv[k] = NiceHash.set_values(v, new_hash_values)
|
|
136
|
-
else
|
|
137
|
-
hashv[k] = NiceHash.set_values(v, hash_values)
|
|
138
|
-
end
|
|
139
|
-
else
|
|
140
|
-
hashv[k] = v
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
hash_values.each do |k, v|
|
|
144
|
-
hashv = NiceHash.set_nested(hashv, k, v, true) if k.is_a?(Hash)
|
|
145
|
-
end
|
|
146
|
-
return hashv
|
|
147
|
-
elsif hash_array.is_a?(Array) and hash_array.size > 0
|
|
148
|
-
hashv = Array.new
|
|
149
|
-
hash_array.each do |r|
|
|
150
|
-
hashv << NiceHash.set_values(r, hash_values)
|
|
151
|
-
end
|
|
152
|
-
return hashv
|
|
153
|
-
else
|
|
154
|
-
return hash_array
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
###########################################################################
|
|
159
|
-
# It will return an array of the keys where we are using string patterns.
|
|
160
|
-
#
|
|
161
|
-
# input:
|
|
162
|
-
# pattern_hash: (Hash) Hash we want to get the pattern_fields
|
|
163
|
-
# select_hash_key: (key value) (optional) The key we want to select on the subhashes
|
|
164
|
-
# output: (Array)
|
|
165
|
-
# Array of the kind: [ [key], [key, subkey, subkey] ]
|
|
166
|
-
# Each value of the array can be used as parameter for the methods: dig, bury
|
|
167
|
-
# examples:
|
|
168
|
-
# NiceHash.pattern_fields(my_hash)
|
|
169
|
-
# #> [
|
|
170
|
-
# [:address, :correct],
|
|
171
|
-
# [:products, 0, :name],
|
|
172
|
-
# [:products, 0, :price, :correct],
|
|
173
|
-
# [:products, 1, :name],
|
|
174
|
-
# [:products, 1, :price, :correct]
|
|
175
|
-
# ]
|
|
176
|
-
# NiceHash.pattern_fields(my_hash, :correct)
|
|
177
|
-
# #> [
|
|
178
|
-
# [:address],
|
|
179
|
-
# [:products, 0, :name],
|
|
180
|
-
# [:products, 0, :price],
|
|
181
|
-
# [:products, 1, :name],
|
|
182
|
-
# [:products, 1, :price]
|
|
183
|
-
# ]
|
|
184
|
-
# Using it directly on Hash class, pattern_fields(*select_hash_key) (alias: patterns):
|
|
185
|
-
# my_hash.pattern_fields
|
|
186
|
-
# my_hash.pattern_fields(:correct)
|
|
187
|
-
# my_hash.patterns(:correct)
|
|
188
|
-
###########################################################################
|
|
189
|
-
def NiceHash.pattern_fields(pattern_hash, *select_hash_key)
|
|
190
|
-
pattern_fields = Array.new
|
|
191
|
-
|
|
192
|
-
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
193
|
-
pattern_hash.each { |key, value|
|
|
194
|
-
key = [key]
|
|
195
|
-
if value.kind_of?(Hash)
|
|
196
|
-
if select_hash_key.size == 1 and value.keys.include?(select_hash_key[0])
|
|
197
|
-
value = value[select_hash_key[0]]
|
|
198
|
-
else
|
|
199
|
-
res = NiceHash.pattern_fields(value, *select_hash_key)
|
|
200
|
-
if res.size > 0
|
|
201
|
-
res.each { |r|
|
|
202
|
-
pattern_fields << (r.unshift(key)).flatten
|
|
203
|
-
}
|
|
204
|
-
end
|
|
205
|
-
next
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
if value.kind_of?(String)
|
|
209
|
-
if StringPattern.optimistic and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
210
|
-
pattern_fields << key
|
|
211
|
-
end
|
|
212
|
-
elsif value.kind_of?(Symbol)
|
|
213
|
-
if value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
214
|
-
pattern_fields << key
|
|
215
|
-
end
|
|
216
|
-
elsif value.kind_of?(Array)
|
|
217
|
-
array_pattern = false
|
|
218
|
-
value.each { |v|
|
|
219
|
-
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
220
|
-
pattern_fields << key
|
|
221
|
-
array_pattern = true
|
|
222
|
-
break
|
|
223
|
-
end
|
|
224
|
-
}
|
|
225
|
-
unless array_pattern
|
|
226
|
-
i = 0
|
|
227
|
-
value.each { |v|
|
|
228
|
-
res = NiceHash.pattern_fields(v, *select_hash_key)
|
|
229
|
-
if res.size > 0
|
|
230
|
-
res.each { |r|
|
|
231
|
-
pattern_fields << (r.unshift(i).unshift(key)).flatten
|
|
232
|
-
}
|
|
233
|
-
end
|
|
234
|
-
i += 1
|
|
235
|
-
}
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
}
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
return pattern_fields
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
###########################################################################
|
|
245
|
-
# It will return an array of the keys where we are using select values of the kind: "value1|value2|value3".
|
|
246
|
-
#
|
|
247
|
-
# input:
|
|
248
|
-
# pattern_hash: (Hash) Hash we want to get the select_fields
|
|
249
|
-
# select_hash_key: (key value) (optional) The key we want to select on the subhashes
|
|
250
|
-
# output: (Array)
|
|
251
|
-
# Array of the kind: [ [key], [key, subkey, subkey] ]
|
|
252
|
-
# Each value of the array can be used as parameter for the methods: dig, bury
|
|
253
|
-
# examples:
|
|
254
|
-
# NiceHash.select_fields(my_hash)
|
|
255
|
-
# #> [[:city, :correct]]
|
|
256
|
-
# NiceHash.select_fields(my_hash, :correct)
|
|
257
|
-
# #> [[:city]]
|
|
258
|
-
# Using it directly on Hash class, select_fields(*select_hash_key):
|
|
259
|
-
# my_hash.select_fields
|
|
260
|
-
# my_hash.select_fields(:correct)
|
|
261
|
-
###########################################################################
|
|
262
|
-
def NiceHash.select_fields(pattern_hash, *select_hash_key)
|
|
263
|
-
select_fields = Array.new
|
|
264
|
-
|
|
265
|
-
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
266
|
-
pattern_hash.each { |key, value|
|
|
267
|
-
key = [key]
|
|
268
|
-
if value.kind_of?(Hash)
|
|
269
|
-
if select_hash_key.size == 1 and value.keys.include?(select_hash_key[0])
|
|
270
|
-
value = value[select_hash_key[0]]
|
|
271
|
-
else
|
|
272
|
-
res = NiceHash.select_fields(value, *select_hash_key)
|
|
273
|
-
if res.size > 0
|
|
274
|
-
res.each { |r|
|
|
275
|
-
select_fields << (r.unshift(key)).flatten
|
|
276
|
-
}
|
|
277
|
-
end
|
|
278
|
-
next
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
if value.kind_of?(String)
|
|
282
|
-
if StringPattern.optimistic and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
283
|
-
select_fields << key
|
|
284
|
-
end
|
|
285
|
-
elsif value.kind_of?(Symbol)
|
|
286
|
-
if value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
287
|
-
select_fields << key
|
|
288
|
-
end
|
|
289
|
-
elsif value.kind_of?(Array)
|
|
290
|
-
array_pattern = false
|
|
291
|
-
value.each { |v|
|
|
292
|
-
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
293
|
-
array_pattern = true
|
|
294
|
-
break
|
|
295
|
-
end
|
|
296
|
-
}
|
|
297
|
-
unless array_pattern
|
|
298
|
-
i = 0
|
|
299
|
-
value.each { |v|
|
|
300
|
-
res = NiceHash.select_fields(v, *select_hash_key)
|
|
301
|
-
if res.size > 0
|
|
302
|
-
res.each { |r|
|
|
303
|
-
select_fields << (r.unshift(i).unshift(key)).flatten
|
|
304
|
-
}
|
|
305
|
-
end
|
|
306
|
-
i += 1
|
|
307
|
-
}
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
}
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
return select_fields
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
###########################################################################
|
|
317
|
-
# It will generate a new hash with the values generated from the string patterns and select fields specified.
|
|
318
|
-
# 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
|
|
319
|
-
# If expected_errors specified the values will be generated with the specified errors.
|
|
320
|
-
# input:
|
|
321
|
-
# pattern_hash: (Hash) Hash we want to use to generate the values
|
|
322
|
-
# select_hash_key: (key value) (optional) The key we want to select on the subhashes
|
|
323
|
-
# expected_errors: (Array) (optional) (alias: errors) To generate the string patterns with the specified errors.
|
|
324
|
-
# The possible values you can specify is one or more of these ones:
|
|
325
|
-
# :length: wrong length, minimum or maximum
|
|
326
|
-
# :min_length: wrong minimum length
|
|
327
|
-
# :max_length: wrong maximum length
|
|
328
|
-
# :value: wrong resultant value
|
|
329
|
-
# :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
|
|
330
|
-
# :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.
|
|
331
|
-
# :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
|
|
332
|
-
# output: (Hash)
|
|
333
|
-
# The Hash with the select_hash_key selected and the values generated from the string patterns and select fields specified.
|
|
334
|
-
# examples:
|
|
335
|
-
# new_hash = NiceHash.generate(my_hash)
|
|
336
|
-
# #> {:name=>"Peter",
|
|
337
|
-
# :address=>{:wrong=>"\#$$$$$", :correct=>"KZPCzxsWGMLqonesu wbqH"},
|
|
338
|
-
# :city=>{:wrong=>"Germany", :correct=>"Barcelona"},
|
|
339
|
-
# :products=> [
|
|
340
|
-
# {:name=>"gIqkWygmVm", :price=>{:wrong=>"-20", :correct=>"34338330"}},
|
|
341
|
-
# {:name=>"CK68VLIcYf", :price=>{:wrong=>"-20", :correct=>"616066520"}}
|
|
342
|
-
# ]
|
|
343
|
-
# }
|
|
344
|
-
# new_hash = NiceHash.generate(my_hash, :correct)
|
|
345
|
-
# #> {:name=>"Peter",
|
|
346
|
-
# :address=>"juQeAVZjIuWBPsE",
|
|
347
|
-
# :city=>"Madrid",
|
|
348
|
-
# :products=> [
|
|
349
|
-
# {:name=>"G44Ilr0puV", :price=>"477813"},
|
|
350
|
-
# {:name=>"v6ojs79LOp", :price=>"74820"}
|
|
351
|
-
# ]
|
|
352
|
-
# }
|
|
353
|
-
# new_hash = NiceHash.generate(my_hash, :correct, expected_errors: [:min_length])
|
|
354
|
-
# #> {:name=>"Peter",
|
|
355
|
-
# :address=>"ZytjefJ",
|
|
356
|
-
# :city=>"Madri",
|
|
357
|
-
# :products=>[
|
|
358
|
-
# {:name=>"cIBrzeO", :price=>""},
|
|
359
|
-
# {:name=>"5", :price=>""}
|
|
360
|
-
# ]
|
|
361
|
-
# }
|
|
362
|
-
# Using it directly on Hash class, generate(select_hash_key=nil, expected_errors: []) (alias: gen):
|
|
363
|
-
# new_hash = my_hash.generate
|
|
364
|
-
# new_hash = my_hash.gen(:correct)
|
|
365
|
-
# new_hash = my_hash.generate(:correct, errors: [:min_length])
|
|
366
|
-
###########################################################################
|
|
367
|
-
def NiceHash.generate(pattern_hash, select_hash_key = nil, expected_errors: [], **synonyms)
|
|
368
|
-
hashv = Hash.new()
|
|
369
|
-
same_values = Hash.new()
|
|
370
|
-
expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
|
|
371
|
-
if expected_errors.kind_of?(Symbol)
|
|
372
|
-
expected_errors = [expected_errors]
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
376
|
-
pattern_hash.each { |key, value|
|
|
377
|
-
if key.kind_of?(Array)
|
|
378
|
-
same_values[key[0]] = key.dup
|
|
379
|
-
same_values[key[0]].shift
|
|
380
|
-
key = key[0]
|
|
381
|
-
end
|
|
382
|
-
if value.kind_of?(Hash)
|
|
383
|
-
if value.keys.include?(select_hash_key)
|
|
384
|
-
value = value[select_hash_key]
|
|
385
|
-
else
|
|
386
|
-
value = NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
if value.kind_of?(String) or value.kind_of?(Symbol)
|
|
390
|
-
if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
391
|
-
hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
|
|
392
|
-
elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
393
|
-
if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round == 0)
|
|
394
|
-
min = value.to_s.split("|").min { |a, b| a.size <=> b.size }
|
|
395
|
-
hashv[key] = min[0..-2] unless min == ""
|
|
396
|
-
end
|
|
397
|
-
if !hashv.keys.include?(key) and (expected_errors.include?(:max_length) or expected_errors.include?(:length))
|
|
398
|
-
max = value.to_s.split("|").max { |a, b| a.size <=> b.size }
|
|
399
|
-
hashv[key] = max + max[-1]
|
|
400
|
-
end
|
|
401
|
-
if expected_errors.include?(:value) or
|
|
402
|
-
expected_errors.include?(:string_set_not_allowed) or
|
|
403
|
-
expected_errors.include?(:required_data)
|
|
404
|
-
if hashv.keys.include?(key)
|
|
405
|
-
v = hashv[key]
|
|
406
|
-
else
|
|
407
|
-
v = value.to_s.split("|").sample
|
|
408
|
-
end
|
|
409
|
-
unless expected_errors.include?(:string_set_not_allowed)
|
|
410
|
-
v = StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
|
|
411
|
-
hashv[key] = v unless value.to_s.split("|").include?(v)
|
|
412
|
-
end
|
|
413
|
-
unless hashv.keys.include?(key)
|
|
414
|
-
one_wrong_letter = StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
|
|
415
|
-
v[rand(v.size)] = one_wrong_letter
|
|
416
|
-
hashv[key] = v unless value.to_s.split("|").include?(v)
|
|
417
|
-
end
|
|
418
|
-
end
|
|
419
|
-
unless hashv.keys.include?(key)
|
|
420
|
-
hashv[key] = value.to_s.split("|").sample
|
|
421
|
-
end
|
|
422
|
-
else
|
|
423
|
-
hashv[key] = value
|
|
424
|
-
end
|
|
425
|
-
elsif value.kind_of?(Array)
|
|
426
|
-
array_pattern = false
|
|
427
|
-
value.each { |v|
|
|
428
|
-
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
429
|
-
hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
|
|
430
|
-
array_pattern = true
|
|
431
|
-
break
|
|
432
|
-
end
|
|
433
|
-
}
|
|
434
|
-
unless array_pattern
|
|
435
|
-
value_ret = Array.new
|
|
436
|
-
value.each { |v|
|
|
437
|
-
if v.is_a?(Hash)
|
|
438
|
-
ret = NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
|
|
439
|
-
else
|
|
440
|
-
ret = NiceHash.generate({ doit: v }, select_hash_key, expected_errors: expected_errors)
|
|
441
|
-
ret = ret[:doit] if ret.is_a?(Hash) and ret.key?(:doit)
|
|
442
|
-
end
|
|
443
|
-
ret = v if ret.kind_of?(Hash) and ret.size == 0
|
|
444
|
-
value_ret << ret
|
|
445
|
-
}
|
|
446
|
-
hashv[key] = value_ret
|
|
447
|
-
end
|
|
448
|
-
elsif value.kind_of?(Range)
|
|
449
|
-
if expected_errors.empty?
|
|
450
|
-
hashv[key] = rand(value)
|
|
451
|
-
else
|
|
452
|
-
hashv[key] = rand(value)
|
|
453
|
-
expected_errors.each do |er|
|
|
454
|
-
if er == :min_length
|
|
455
|
-
hashv[key] = rand((value.first - value.last)..value.first - 1)
|
|
456
|
-
elsif er == :max_length
|
|
457
|
-
hashv[key] = rand((value.last + 1)..(value.last * 2))
|
|
458
|
-
elsif er == :length
|
|
459
|
-
if rand.round == 1
|
|
460
|
-
hashv[key] = rand((value.first - value.last)..value.first - 1)
|
|
461
|
-
else
|
|
462
|
-
hashv[key] = rand((value.last + 1)..(value.last * 2))
|
|
463
|
-
end
|
|
464
|
-
elsif er == :value
|
|
465
|
-
hashv[key] = :"1-10:N/L/".gen
|
|
466
|
-
end
|
|
467
|
-
end
|
|
468
|
-
end
|
|
469
|
-
elsif value.kind_of?(Class) and value == DateTime
|
|
470
|
-
if expected_errors.empty?
|
|
471
|
-
hashv[key] = Time.now.stamp
|
|
472
|
-
else
|
|
473
|
-
hashv[key] = Time.now.stamp
|
|
474
|
-
expected_errors.each do |er|
|
|
475
|
-
if er == :min_length
|
|
476
|
-
hashv[key] = hashv[key].chop
|
|
477
|
-
elsif er == :max_length
|
|
478
|
-
hashv[key] = hashv[key] + "Z"
|
|
479
|
-
elsif er == :length
|
|
480
|
-
if rand.round == 1
|
|
481
|
-
hashv[key] = hashv[key].chop
|
|
482
|
-
else
|
|
483
|
-
hashv[key] = hashv[key] + "Z"
|
|
484
|
-
end
|
|
485
|
-
elsif er == :value
|
|
486
|
-
hashv[key][rand(hashv[key].size - 1)] = "1:L".gen
|
|
487
|
-
end
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
elsif value.kind_of?(Module) and value == Boolean
|
|
491
|
-
if expected_errors.empty?
|
|
492
|
-
hashv[key] = (rand.round == 0)
|
|
493
|
-
else
|
|
494
|
-
hashv[key] = (rand.round == 0)
|
|
495
|
-
expected_errors.each do |er|
|
|
496
|
-
if er == :value
|
|
497
|
-
hashv[key] = "1-10:L".gen
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
end
|
|
501
|
-
elsif value.kind_of?(Proc)
|
|
502
|
-
hashv[key] = value.call
|
|
503
|
-
elsif value.kind_of?(Regexp)
|
|
504
|
-
hashv[key] = value.generate(expected_errors: expected_errors)
|
|
505
|
-
else
|
|
506
|
-
hashv[key] = value
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
if same_values.include?(key)
|
|
510
|
-
same_values[key].each { |k|
|
|
511
|
-
hashv[k] = hashv[key]
|
|
512
|
-
}
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
@values = hashv
|
|
516
|
-
}
|
|
517
|
-
end
|
|
518
|
-
|
|
519
|
-
return hashv
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
###########################################################################
|
|
523
|
-
# Validates a given values_hash_to_validate with string patterns and select fields from pattern_hash
|
|
524
|
-
# input:
|
|
525
|
-
# patterns_hash:
|
|
526
|
-
# (Hash) Hash where we have defined the patterns to follow.
|
|
527
|
-
# (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
|
|
528
|
-
# values_hash_to_validate: (Hash) Hash of values to validate
|
|
529
|
-
# only_patterns: (TrueFalse) (by default true) If true it will validate only the patterns and not the other fields
|
|
530
|
-
# output: (Hash)
|
|
531
|
-
# A hash with the validation results. It will return only the validation errors so in case no validation errors found, empty hash.
|
|
532
|
-
# The keys of the hash will be the keys of the values hash with the validation error.
|
|
533
|
-
# The value in case of a pattern, will be an array with one or more of these possibilities:
|
|
534
|
-
# :length: wrong length, minimum or maximum
|
|
535
|
-
# :min_length: wrong minimum length
|
|
536
|
-
# :max_length: wrong maximum length
|
|
537
|
-
# :value: wrong resultant value
|
|
538
|
-
# :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
|
|
539
|
-
# :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.
|
|
540
|
-
# :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
|
|
541
|
-
# The value in any other case it will be false if the value is not corresponding to the expected.
|
|
542
|
-
# examples:
|
|
543
|
-
# values_to_validate = {:name=>"Peter",
|
|
544
|
-
# :address=>"fnMuKW",
|
|
545
|
-
# :city=>"Dublin",
|
|
546
|
-
# :products=>[{:name=>"V4", :price=>"344"}, {:name=>"E", :price=>"a"}]
|
|
547
|
-
# }
|
|
548
|
-
# results = NiceHash.validate([my_hash, :correct], values_to_validate)
|
|
549
|
-
# #> {:address=>[:min_length, :length],
|
|
550
|
-
# :products=> [{:name=>[:min_length, :length]},
|
|
551
|
-
# {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
|
|
552
|
-
# ]
|
|
553
|
-
# }
|
|
554
|
-
# results = NiceHash.validate([my_hash, :correct], values_to_validate, only_patterns: false)
|
|
555
|
-
# #> {:address=>[:min_length, :length],
|
|
556
|
-
# :city=>false,
|
|
557
|
-
# :products=> [{:name=>[:min_length, :length]},
|
|
558
|
-
# {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
|
|
559
|
-
# ]
|
|
560
|
-
# }
|
|
561
|
-
# Using it directly on Hash class:
|
|
562
|
-
# validate(select_hash_key=nil, values_hash_to_validate) (alias: val)
|
|
563
|
-
# validate_patterns(select_hash_key=nil, values_hash_to_validate)
|
|
564
|
-
#
|
|
565
|
-
# results = my_hash.validate_patterns(:correct, values_to_validate)
|
|
566
|
-
# results = my_hash.validate(:correct, values_to_validate)
|
|
567
|
-
###########################################################################
|
|
568
|
-
def NiceHash.validate(patterns_hash, values_hash_to_validate, only_patterns: true)
|
|
569
|
-
if patterns_hash.kind_of?(Array)
|
|
570
|
-
pattern_hash = patterns_hash[0]
|
|
571
|
-
select_hash_key = patterns_hash[1]
|
|
572
|
-
elsif patterns_hash.kind_of?(Hash)
|
|
573
|
-
pattern_hash = patterns_hash
|
|
574
|
-
select_hash_key = nil
|
|
575
|
-
else
|
|
576
|
-
puts "NiceHash.validate wrong pattern_hash supplied #{patterns_hash.inspect}"
|
|
577
|
-
return { error: :error }
|
|
578
|
-
end
|
|
579
|
-
values = values_hash_to_validate
|
|
580
|
-
if pattern_hash.keys.size == get_all_keys(pattern_hash).size and values.keys.size != get_all_keys(values)
|
|
581
|
-
# all patterns on patterns_hash are described on first level, so no same structure than values
|
|
582
|
-
pattern_hash = values.set_values(pattern_hash)
|
|
583
|
-
end
|
|
584
|
-
results = {}
|
|
585
|
-
same_values = {}
|
|
586
|
-
if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
|
|
587
|
-
pattern_hash.each { |key, value|
|
|
588
|
-
if key.kind_of?(Array)
|
|
589
|
-
same_values[key[0]] = key.dup
|
|
590
|
-
same_values[key[0]].shift
|
|
591
|
-
key = key[0]
|
|
592
|
-
end
|
|
593
|
-
if value.kind_of?(Hash)
|
|
594
|
-
if !select_hash_key.nil? and value.keys.include?(select_hash_key)
|
|
595
|
-
value = value[select_hash_key]
|
|
596
|
-
elsif values.keys.include?(key) and values[key].kind_of?(Hash)
|
|
597
|
-
res = NiceHash.validate([value, select_hash_key], values[key], only_patterns: only_patterns)
|
|
598
|
-
results[key] = res if res.size > 0
|
|
599
|
-
next
|
|
600
|
-
end
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
if values.keys.include?(key)
|
|
604
|
-
if value.kind_of?(String) or value.kind_of?(Symbol)
|
|
605
|
-
if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
|
|
606
|
-
res = StringPattern.validate(pattern: value, text: values[key])
|
|
607
|
-
results[key] = res if res.size > 0
|
|
608
|
-
elsif !only_patterns and ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
|
|
609
|
-
results[key] = false unless value.to_s.split("|").include?(values[key])
|
|
610
|
-
elsif !only_patterns
|
|
611
|
-
results[key] = false unless value.to_s == values[key].to_s
|
|
612
|
-
end
|
|
613
|
-
elsif value.kind_of?(Range)
|
|
614
|
-
if values[key].class != value.first.class or values[key].class != value.last.class
|
|
615
|
-
results[key] = false
|
|
616
|
-
elsif values[key] < value.first or values[key] > value.last
|
|
617
|
-
results[key] = false
|
|
618
|
-
end
|
|
619
|
-
elsif value.kind_of?(Class) and value == DateTime
|
|
620
|
-
if values[key].is_a?(String) and values[key].size == 24
|
|
621
|
-
d = Date.strptime(values[key], "%Y-%m-%dT%H:%M:%S.%LZ") rescue results[key] = false
|
|
622
|
-
elsif values[key].is_a?(Time) or values[key].is_a?(Date) or values[key].is_a?(DateTime)
|
|
623
|
-
# correct
|
|
624
|
-
else
|
|
625
|
-
results[key] = false
|
|
626
|
-
end
|
|
627
|
-
elsif value.kind_of?(Module) and value == Boolean
|
|
628
|
-
results[key] = false unless values[key].is_a?(Boolean)
|
|
629
|
-
elsif value.kind_of?(Regexp)
|
|
630
|
-
rex = Regexp.new("^#{value}$")
|
|
631
|
-
unless values[key].to_s.match?(rex)
|
|
632
|
-
results[key] = false
|
|
633
|
-
end
|
|
634
|
-
elsif value.kind_of?(Array)
|
|
635
|
-
array_pattern = false
|
|
636
|
-
complex_data = false
|
|
637
|
-
value.each { |v|
|
|
638
|
-
if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
|
|
639
|
-
res = StringPattern.validate(pattern: value, text: values[key])
|
|
640
|
-
results[key] = res if res == false
|
|
641
|
-
array_pattern = true
|
|
642
|
-
break
|
|
643
|
-
elsif v.kind_of?(Hash) or v.kind_of?(Array) or v.kind_of?(Struct)
|
|
644
|
-
complex_data = true
|
|
645
|
-
break
|
|
646
|
-
end
|
|
647
|
-
}
|
|
648
|
-
unless array_pattern or results.include?(key)
|
|
649
|
-
if value.size == 1 and values[key].size > 1
|
|
650
|
-
# for the case value == ['Ford|Newton|Seat'] and values == ['Ford', 'Newton', 'Ford']
|
|
651
|
-
i = 0
|
|
652
|
-
if values[key].class == value.class
|
|
653
|
-
values[key].each do |v|
|
|
654
|
-
if value[0].is_a?(Hash)
|
|
655
|
-
res = NiceHash.validate([value[0], select_hash_key], v, only_patterns: only_patterns)
|
|
656
|
-
else
|
|
657
|
-
# for the case {cars: ['Ford|Newton|Seat']}
|
|
658
|
-
res = NiceHash.validate([{ key => value[0] }, select_hash_key], { key => v }, only_patterns: only_patterns)
|
|
659
|
-
#res = {key => res[:doit]} if res.is_a?(Hash) and res.key?(:doit)
|
|
660
|
-
array_pattern = true
|
|
661
|
-
end
|
|
662
|
-
if res.size > 0
|
|
663
|
-
results[key] = Array.new() if !results.keys.include?(key)
|
|
664
|
-
results[key][i] = res
|
|
665
|
-
end
|
|
666
|
-
i += 1
|
|
667
|
-
end
|
|
668
|
-
else
|
|
669
|
-
results[key] = false
|
|
670
|
-
end
|
|
671
|
-
else
|
|
672
|
-
i = 0
|
|
673
|
-
value.each { |v|
|
|
674
|
-
if v.is_a?(Hash)
|
|
675
|
-
res = NiceHash.validate([v, select_hash_key], values[key][i], only_patterns: only_patterns)
|
|
676
|
-
else
|
|
677
|
-
# for the case {cars: ['Ford|Newton|Seat']}
|
|
678
|
-
res = NiceHash.validate([{ key => v }, select_hash_key], { key => values[key][i] }, only_patterns: only_patterns)
|
|
679
|
-
array_pattern = true
|
|
680
|
-
end
|
|
681
|
-
if res.size > 0
|
|
682
|
-
results[key] = Array.new() if !results.keys.include?(key)
|
|
683
|
-
results[key][i] = res
|
|
684
|
-
end
|
|
685
|
-
i += 1
|
|
686
|
-
}
|
|
687
|
-
end
|
|
688
|
-
end
|
|
689
|
-
unless array_pattern or only_patterns or results.include?(key) or complex_data
|
|
690
|
-
results[key] = false unless value == values[key]
|
|
691
|
-
end
|
|
692
|
-
else
|
|
693
|
-
unless only_patterns or value.kind_of?(Proc)
|
|
694
|
-
results[key] = false unless value == values[key]
|
|
695
|
-
end
|
|
696
|
-
end
|
|
697
|
-
|
|
698
|
-
if same_values.include?(key)
|
|
699
|
-
same_values[key].each { |k|
|
|
700
|
-
if values.keys.include?(k)
|
|
701
|
-
if values[key] != values[k]
|
|
702
|
-
results[k] = "Not equal to #{key}"
|
|
703
|
-
end
|
|
704
|
-
end
|
|
705
|
-
}
|
|
706
|
-
end
|
|
707
|
-
end
|
|
708
|
-
}
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
return results
|
|
712
|
-
end
|
|
713
|
-
|
|
714
|
-
###########################################################################
|
|
715
|
-
# Change only one value at a time and return an Array of Hashes
|
|
716
|
-
# 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.
|
|
717
|
-
# 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
|
|
718
|
-
# input:
|
|
719
|
-
# patterns_hash:
|
|
720
|
-
# (Hash) Hash where we have defined the patterns to follow.
|
|
721
|
-
# (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
|
|
722
|
-
# values_hash: (Hash) Hash of values to use to modify the values generated on patterns_hash
|
|
723
|
-
# output: (Array of Hashes)
|
|
724
|
-
# example:
|
|
725
|
-
# wrong_min_length_hash = my_hash.generate(:correct, errors: :min_length)
|
|
726
|
-
# array_of_hashes = NiceHash.change_one_by_one([my_hash, :correct], wrong_min_length_hash)
|
|
727
|
-
# array_of_hashes.each {|hash_with_one_wrong_field|
|
|
728
|
-
# #Here your code to send through http the JSON data stored in hash_with_one_wrong_field
|
|
729
|
-
# #if you want to know which field is the one that is wrong:
|
|
730
|
-
# res = my_hash.validate(:correct, hash_with_one_wrong_field)
|
|
731
|
-
# }
|
|
732
|
-
###########################################################################
|
|
733
|
-
def NiceHash.change_one_by_one(patterns_hash, values_hash)
|
|
734
|
-
if patterns_hash.kind_of?(Array)
|
|
735
|
-
select_key = patterns_hash[1]
|
|
736
|
-
pattern_hash = patterns_hash[0]
|
|
737
|
-
else
|
|
738
|
-
pattern_hash = patterns_hash
|
|
739
|
-
select_key = []
|
|
740
|
-
end
|
|
741
|
-
array = Array.new
|
|
742
|
-
good_values = NiceHash.generate(pattern_hash, select_key)
|
|
743
|
-
select_keys = pattern_hash.pattern_fields(select_key) + pattern_hash.select_fields(select_key)
|
|
744
|
-
select_keys.each { |field|
|
|
745
|
-
new_hash = Marshal.load(Marshal.dump(good_values))
|
|
746
|
-
# to deal with the case same values... like in pwd1, pwd2, pwd3
|
|
747
|
-
if field[-1].kind_of?(Array)
|
|
748
|
-
last_to_set = field[-1]
|
|
749
|
-
else
|
|
750
|
-
last_to_set = [field[-1]]
|
|
751
|
-
end
|
|
752
|
-
last_to_set.each { |f|
|
|
753
|
-
keys = field[0..-2] << f
|
|
754
|
-
new_hash.bury(keys, values_hash.dig(*keys))
|
|
755
|
-
}
|
|
756
|
-
array << new_hash
|
|
757
|
-
}
|
|
758
|
-
return array
|
|
759
|
-
end
|
|
760
|
-
|
|
761
|
-
##################################################
|
|
762
|
-
# Get values from the Hash structure (array of Hashes allowed)
|
|
763
|
-
# In case the key supplied doesn't exist in the hash then it will be returned nil for that one
|
|
764
|
-
# input:
|
|
765
|
-
# hashv: a simple hash or a hash containing arrays. Example:
|
|
766
|
-
# example={"id"=>344,
|
|
767
|
-
# "customer"=>{
|
|
768
|
-
# "name"=>"Peter Smith",
|
|
769
|
-
# "phone"=>334334333
|
|
770
|
-
# },
|
|
771
|
-
# "tickets"=>[
|
|
772
|
-
# {"idt"=>345,"name"=>"myFavor1"},
|
|
773
|
-
# {"idt"=>3123},
|
|
774
|
-
# {"idt"=>3145,"name"=>"Special ticket"}
|
|
775
|
-
# ]
|
|
776
|
-
# }
|
|
777
|
-
# keys: one key (string) or an array of keys. key can be a nested key
|
|
778
|
-
# output:
|
|
779
|
-
# a Hash of Arrays with all values found.
|
|
780
|
-
# Example of output with example.get_values("id","name")
|
|
781
|
-
# {"id"=>344, "name"=>["Peter Smith", ["myFavor1", "Special ticket"]]}
|
|
782
|
-
# Example of output with example.get_values("idt")
|
|
783
|
-
# {"idt"=>[345,3123,3145]}
|
|
784
|
-
# Example of output with example.get_values(:'tickets.idt')
|
|
785
|
-
# {:"tickets.idt"=>[345,3123,3145]}
|
|
786
|
-
#
|
|
787
|
-
####################################################
|
|
788
|
-
def NiceHash.get_values(hashv, keys)
|
|
789
|
-
#todo: check if we should return {"id"=>344, "name"=>["Peter Smith", "myFavor1", "Special ticket"]} instead of
|
|
790
|
-
# {"id"=>344, "name"=>["Peter Smith", ["myFavor1", "Special ticket"]]}
|
|
791
|
-
if keys.kind_of?(String) or keys.kind_of?(Symbol)
|
|
792
|
-
keys = [keys]
|
|
793
|
-
end
|
|
794
|
-
if (keys.grep(/\./)).empty?
|
|
795
|
-
nested = false
|
|
796
|
-
else
|
|
797
|
-
nested = true
|
|
798
|
-
end
|
|
799
|
-
result = Hash.new()
|
|
800
|
-
number_of_results = Hash.new()
|
|
801
|
-
keys.each { |key|
|
|
802
|
-
number_of_results[key] = 0
|
|
803
|
-
}
|
|
804
|
-
if hashv.kind_of?(Array)
|
|
805
|
-
hashv.each { |tmp|
|
|
806
|
-
if tmp.kind_of?(Array) or tmp.kind_of?(Hash)
|
|
807
|
-
n_result = get_values(tmp, keys)
|
|
808
|
-
if n_result != :error
|
|
809
|
-
n_result.each { |n_key, n_value|
|
|
810
|
-
if result.has_key?(n_key)
|
|
811
|
-
if !result[n_key].kind_of?(Array) or
|
|
812
|
-
(result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
|
|
813
|
-
if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
|
|
814
|
-
res_tx = result[n_key].dup()
|
|
815
|
-
else
|
|
816
|
-
res_tx = result[n_key]
|
|
817
|
-
end
|
|
818
|
-
result[n_key] = Array.new()
|
|
819
|
-
result[n_key].push(res_tx)
|
|
820
|
-
result[n_key].push(n_value)
|
|
821
|
-
else
|
|
822
|
-
result[n_key].push(n_value)
|
|
823
|
-
end
|
|
824
|
-
else
|
|
825
|
-
result[n_key] = n_value
|
|
826
|
-
end
|
|
827
|
-
number_of_results[n_key] += 1
|
|
828
|
-
}
|
|
829
|
-
end
|
|
830
|
-
end
|
|
831
|
-
}
|
|
832
|
-
elsif hashv.kind_of?(Hash)
|
|
833
|
-
hashv.each { |key, value|
|
|
834
|
-
#if keys.include?(key) then
|
|
835
|
-
#added to be able to access the keys with symbols to strings and opposite
|
|
836
|
-
if keys.include?(key) or keys.include?(key.to_s) or keys.include?(key.to_sym)
|
|
837
|
-
#todo: check on next ruby versions since it will be not necessary to do it
|
|
838
|
-
#added to be able to access the keys with symbols to strings and opposite
|
|
839
|
-
key = key.to_s() if keys.include?(key.to_s)
|
|
840
|
-
key = key.to_sym() if keys.include?(key.to_sym)
|
|
841
|
-
|
|
842
|
-
if result.has_key?(key)
|
|
843
|
-
if !result[key].kind_of?(Array) or
|
|
844
|
-
(result[key].kind_of?(Array) and number_of_results[key] < result[key].size)
|
|
845
|
-
if result[key].kind_of?(Hash) or result[key].kind_of?(Array)
|
|
846
|
-
res_tx = result[key].dup()
|
|
847
|
-
else
|
|
848
|
-
res_tx = result[key]
|
|
849
|
-
end
|
|
850
|
-
result[key] = Array.new()
|
|
851
|
-
result[key].push(res_tx)
|
|
852
|
-
result[key].push(value)
|
|
853
|
-
else
|
|
854
|
-
result[key].push(value)
|
|
855
|
-
end
|
|
856
|
-
else
|
|
857
|
-
result[key] = value
|
|
858
|
-
end
|
|
859
|
-
number_of_results[key] += 1
|
|
860
|
-
end
|
|
861
|
-
if value.kind_of?(Array) or value.kind_of?(Hash)
|
|
862
|
-
if nested
|
|
863
|
-
keys_nested = []
|
|
864
|
-
keys.grep(/^#{key}\./).each do |k|
|
|
865
|
-
keys_nested << k.to_s.gsub(/^#{key}\./,'').to_sym
|
|
866
|
-
end
|
|
867
|
-
n_result_tmp = get_values(value, keys_nested)
|
|
868
|
-
n_result = {}
|
|
869
|
-
n_result_tmp.each do |k,v|
|
|
870
|
-
n_result["#{key}.#{k}".to_sym] = v
|
|
871
|
-
end
|
|
872
|
-
else
|
|
873
|
-
n_result = get_values(value, keys)
|
|
874
|
-
end
|
|
875
|
-
if n_result != :error
|
|
876
|
-
n_result.each { |n_key, n_value|
|
|
877
|
-
if result.has_key?(n_key)
|
|
878
|
-
if !result[n_key].kind_of?(Array) or
|
|
879
|
-
(result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
|
|
880
|
-
if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
|
|
881
|
-
res_tx = result[n_key].dup()
|
|
882
|
-
else
|
|
883
|
-
res_tx = result[n_key]
|
|
884
|
-
end
|
|
885
|
-
result[n_key] = Array.new()
|
|
886
|
-
result[n_key].push(res_tx)
|
|
887
|
-
result[n_key].push(n_value)
|
|
888
|
-
else
|
|
889
|
-
result[n_key].push(n_value)
|
|
890
|
-
end
|
|
891
|
-
else
|
|
892
|
-
result[n_key] = n_value
|
|
893
|
-
end
|
|
894
|
-
number_of_results[n_key] += 1
|
|
895
|
-
}
|
|
896
|
-
end
|
|
897
|
-
end
|
|
898
|
-
}
|
|
899
|
-
else
|
|
900
|
-
return :error
|
|
901
|
-
end
|
|
902
|
-
if result.kind_of?(Hash) and caller[0]["get_values"].nil? #no error or anything weird
|
|
903
|
-
(keys - result.keys).each { |k| #in case some keys don't exist in the hash
|
|
904
|
-
result[k] = nil
|
|
905
|
-
}
|
|
906
|
-
end
|
|
907
|
-
return result
|
|
908
|
-
end
|
|
909
|
-
|
|
910
|
-
##################################################
|
|
911
|
-
# Analyzes the supplied replica and verifies that the structure follows the one supplied on structure
|
|
912
|
-
#
|
|
913
|
-
# @param structure [Array] [Hash] Contains the structure that should follow the replica. It can be a nested combination of arrays and hashes.
|
|
914
|
-
# @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.
|
|
915
|
-
# @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.
|
|
916
|
-
# @param patterns [Hash] add verification of data values following the patterns supplied on a one level hash
|
|
917
|
-
#
|
|
918
|
-
# @return [Boolean] true in case replica follows the structure supplied
|
|
919
|
-
#
|
|
920
|
-
# @example
|
|
921
|
-
# my_structure = [
|
|
922
|
-
# { name: 'xxx',
|
|
923
|
-
# zip: 'yyyy',
|
|
924
|
-
# customer: true,
|
|
925
|
-
# product_ids: [1]
|
|
926
|
-
# }
|
|
927
|
-
# ]
|
|
928
|
-
# my_replica = [ {name: 'Peter Ben', zip: '1121A', customer: false, product_ids: []},
|
|
929
|
-
# {name: 'John Woop', zip: '74014', customer: true, product_ids: [10,120,301]}]
|
|
930
|
-
# NiceHash.compare_structure(my_structure, my_replica)
|
|
931
|
-
# #>true
|
|
932
|
-
##################################################
|
|
933
|
-
def NiceHash.compare_structure(structure, replica, compare_only_if_exist_key = false, patterns = {})
|
|
934
|
-
unless structure.class == replica.class or
|
|
935
|
-
((structure.is_a?(TrueClass) or structure.is_a?(FalseClass)) and (replica.is_a?(TrueClass) or replica.is_a?(FalseClass)))
|
|
936
|
-
puts "NiceHash.compare_structure: different object type #{structure.class} is not #{replica.class}. expected: #{structure.inspect}. found: #{replica.inspect}."
|
|
937
|
-
return false
|
|
938
|
-
end
|
|
939
|
-
success = true
|
|
940
|
-
if structure.is_a?(Hash)
|
|
941
|
-
structure.each do |key, value|
|
|
942
|
-
if patterns.key?(key) and replica.key?(key)
|
|
943
|
-
unless (patterns[key].is_a?(Array) and replica[key].is_a?(Array) and replica[key].empty?) or
|
|
944
|
-
{key => patterns[key]}.validate({key => replica[key]}).empty?
|
|
945
|
-
puts "NiceHash.compare_structure: key :#{key} not following pattern #{patterns[key]}. value: #{replica[key]}"
|
|
946
|
-
success = false
|
|
947
|
-
end
|
|
948
|
-
end
|
|
949
|
-
|
|
950
|
-
if compare_only_if_exist_key and replica.key?(key)
|
|
951
|
-
unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
|
|
952
|
-
puts "NiceHash.compare_structure: key :#{key} different."
|
|
953
|
-
success = false
|
|
954
|
-
end
|
|
955
|
-
elsif compare_only_if_exist_key == false
|
|
956
|
-
unless replica.key?(key)
|
|
957
|
-
puts "NiceHash.compare_structure: key :#{key} missing."
|
|
958
|
-
success = false
|
|
959
|
-
else
|
|
960
|
-
unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
|
|
961
|
-
puts "NiceHash.compare_structure: key :#{key} different."
|
|
962
|
-
success = false
|
|
963
|
-
end
|
|
964
|
-
end
|
|
965
|
-
end
|
|
966
|
-
end
|
|
967
|
-
elsif structure.is_a?(Array)
|
|
968
|
-
# compare all elements of replica with the structure of the first element on structure
|
|
969
|
-
replica.each do |elem|
|
|
970
|
-
unless compare_structure(structure[0], elem, compare_only_if_exist_key, patterns)
|
|
971
|
-
success = false
|
|
972
|
-
end
|
|
973
|
-
end
|
|
974
|
-
end
|
|
975
|
-
return success
|
|
976
|
-
end
|
|
977
|
-
|
|
978
|
-
##################################################
|
|
979
|
-
# Translate a hash of hashes into a string separted by .
|
|
980
|
-
#
|
|
981
|
-
# @param hash [Hash] The hash we want to translate
|
|
982
|
-
#
|
|
983
|
-
# @return [String]
|
|
984
|
-
#
|
|
985
|
-
# @example
|
|
986
|
-
# my_hash = { uno: {dos: :tres} }
|
|
987
|
-
# NiceHash.transtring(my_hash)
|
|
988
|
-
# #>"uno.dos.tres"
|
|
989
|
-
##################################################
|
|
990
|
-
def self.transtring(hash)
|
|
991
|
-
keys = []
|
|
992
|
-
if hash.is_a?(Hash)
|
|
993
|
-
hash.each do |k, v|
|
|
994
|
-
if v.is_a?(Hash)
|
|
995
|
-
keys << k
|
|
996
|
-
keys << transtring(v)
|
|
997
|
-
else
|
|
998
|
-
keys << k
|
|
999
|
-
keys << v
|
|
1000
|
-
end
|
|
1001
|
-
end
|
|
1002
|
-
else
|
|
1003
|
-
keys << hash
|
|
1004
|
-
end
|
|
1005
|
-
return keys.join(".")
|
|
1006
|
-
end
|
|
1007
|
-
|
|
1008
|
-
##################################################
|
|
1009
|
-
# Get all the keys of a hash
|
|
1010
|
-
#
|
|
1011
|
-
# @param hash [Hash] The hash
|
|
1012
|
-
#
|
|
1013
|
-
# @return [Array]
|
|
1014
|
-
#
|
|
1015
|
-
# @example
|
|
1016
|
-
# my_hash = { uno: {dos: {tres: 3}} }
|
|
1017
|
-
# NiceHash.get_all_keys(my_hash)
|
|
1018
|
-
# #>[:uno, :dos, :tres]
|
|
1019
|
-
##################################################
|
|
1020
|
-
def self.get_all_keys(h)
|
|
1021
|
-
h.each_with_object([]) do |(k, v), keys|
|
|
1022
|
-
keys << k
|
|
1023
|
-
keys.concat(get_all_keys(v)) if v.is_a? Hash
|
|
1024
|
-
if v.is_a?(Array)
|
|
1025
|
-
v.each do |vv|
|
|
1026
|
-
keys.concat(get_all_keys(vv)) if vv.is_a? Hash or vv.is_a? Array
|
|
1027
|
-
end
|
|
1028
|
-
end
|
|
1029
|
-
end
|
|
1030
|
-
end
|
|
1031
|
-
|
|
1032
|
-
##################################################
|
|
1033
|
-
# Deletes the supplied nested key
|
|
1034
|
-
#
|
|
1035
|
-
# @param hash [Hash] The hash we want
|
|
1036
|
-
# @param nested_key [Hash] [String] String with the nested key: 'uno.dos.tres' or a hash { uno: {dos: :tres} }
|
|
1037
|
-
#
|
|
1038
|
-
# @return [Hash]
|
|
1039
|
-
#
|
|
1040
|
-
# @example
|
|
1041
|
-
# my_hash = { user: {
|
|
1042
|
-
# address: {
|
|
1043
|
-
# city: 'Madrid',
|
|
1044
|
-
# country: 'Spain'
|
|
1045
|
-
# },
|
|
1046
|
-
# name: 'Peter',
|
|
1047
|
-
# age: 33
|
|
1048
|
-
# },
|
|
1049
|
-
# customer: true
|
|
1050
|
-
# }
|
|
1051
|
-
# NiceHash.delete_nested(my_hash, 'user.address.city')
|
|
1052
|
-
# #>{:user=>{:address=>{:country=>"Spain"}, :name=>"Peter", :age=>33}, :customer=>true}
|
|
1053
|
-
##################################################
|
|
1054
|
-
def self.delete_nested(hash, nested_key)
|
|
1055
|
-
nested_key = transtring(nested_key)
|
|
1056
|
-
keys = nested_key.split(".")
|
|
1057
|
-
if keys.size == 1
|
|
1058
|
-
hash.delete(nested_key.to_sym)
|
|
1059
|
-
else
|
|
1060
|
-
last_key = keys[-1]
|
|
1061
|
-
eval("hash.#{keys[0..(keys.size - 2)].join(".")}.delete(:#{last_key})")
|
|
1062
|
-
end
|
|
1063
|
-
return hash
|
|
1064
|
-
end
|
|
1065
|
-
|
|
1066
|
-
##################################################
|
|
1067
|
-
# sets the supplied value on the supplied nested key
|
|
1068
|
-
#
|
|
1069
|
-
# @param hash [Hash] The hash we want
|
|
1070
|
-
# @param nested_key [Hash] [String] String with the nested key: 'uno.dos.tres' or a hash { uno: {dos: :tres} }
|
|
1071
|
-
# @param value [] value to set
|
|
1072
|
-
#
|
|
1073
|
-
# @return [Hash]
|
|
1074
|
-
#
|
|
1075
|
-
# @example
|
|
1076
|
-
# my_hash = { user: {
|
|
1077
|
-
# address: {
|
|
1078
|
-
# city: 'Madrid',
|
|
1079
|
-
# country: 'Spain'
|
|
1080
|
-
# },
|
|
1081
|
-
# name: 'Peter',
|
|
1082
|
-
# age: 33
|
|
1083
|
-
# },
|
|
1084
|
-
# customer: true
|
|
1085
|
-
# }
|
|
1086
|
-
# NiceHash.set_nested(my_hash, 'user.address.city', 'Barcelona')
|
|
1087
|
-
# #>{:user=>{:address=>{:city=>'Barcelona', :country=>"Spain"}, :name=>"Peter", :age=>33}, :customer=>true}
|
|
1088
|
-
##################################################
|
|
1089
|
-
def self.set_nested(hash, nested_key, value, only_if_exist = false)
|
|
1090
|
-
nested_key = transtring(nested_key)
|
|
1091
|
-
keys = nested_key.split(".")
|
|
1092
|
-
if keys.size == 1
|
|
1093
|
-
hash[nested_key.to_sym] = value unless only_if_exist and !hash.key?(nested_key.to_sym)
|
|
1094
|
-
else
|
|
1095
|
-
exist = true
|
|
1096
|
-
if only_if_exist
|
|
1097
|
-
ht = hash.deep_copy
|
|
1098
|
-
keys.each do |k|
|
|
1099
|
-
unless ht.key?(k.to_sym)
|
|
1100
|
-
exist = false
|
|
1101
|
-
break
|
|
1102
|
-
end
|
|
1103
|
-
ht = ht[k.to_sym]
|
|
1104
|
-
end
|
|
1105
|
-
end
|
|
1106
|
-
if !only_if_exist or (only_if_exist and exist)
|
|
1107
|
-
if value.is_a?(String)
|
|
1108
|
-
eval("hash.#{nested_key}='#{value}'")
|
|
1109
|
-
else
|
|
1110
|
-
#todo: consider other kind of objects apart of strings
|
|
1111
|
-
eval("hash.#{nested_key}=#{value}")
|
|
1112
|
-
end
|
|
1113
|
-
end
|
|
1114
|
-
end
|
|
1115
|
-
return hash
|
|
1116
|
-
end
|
|
1117
|
-
|
|
1118
|
-
##################################################
|
|
1119
|
-
# Filter the hash supplied and returns only the specified keys
|
|
1120
|
-
#
|
|
1121
|
-
# @param hash [Hash] The hash we want to filter
|
|
1122
|
-
# @param keys [Array] [Symbol] Array of symbols or symbol. Nested keys can be used: 'uno.dos.tres'
|
|
1123
|
-
#
|
|
1124
|
-
# @return [Hash]
|
|
1125
|
-
#
|
|
1126
|
-
# @example
|
|
1127
|
-
# my_hash = { user: {
|
|
1128
|
-
# address: {
|
|
1129
|
-
# city: 'Madrid',
|
|
1130
|
-
# country: 'Spain'
|
|
1131
|
-
# },
|
|
1132
|
-
# name: 'Peter',
|
|
1133
|
-
# age: 33,
|
|
1134
|
-
# customers: [{name: 'Peter', currency: 'Euro'}, {name:'John', currency: 'Euro'}]
|
|
1135
|
-
# },
|
|
1136
|
-
# customer: true
|
|
1137
|
-
# }
|
|
1138
|
-
# NiceHash.nice_filter(my_hash, [:'user.address.city', :'customer', :'user.customers.name'])
|
|
1139
|
-
# #> {:user => {:address => {:city => "Madrid"}, :customers => [{:name => "Peter"}, {:name => "John"}]}, :customer => true}
|
|
1140
|
-
##################################################
|
|
1141
|
-
def self.nice_filter(hash, keys)
|
|
1142
|
-
result = {}
|
|
1143
|
-
keys = [keys] unless keys.is_a?(Array)
|
|
1144
|
-
keys.each do |k|
|
|
1145
|
-
kn = k.to_s.split('.')
|
|
1146
|
-
if hash.is_a?(Hash) and hash.key?(k)
|
|
1147
|
-
if hash[k].is_a?(Hash)
|
|
1148
|
-
result[k] = {} unless result.key?(k)
|
|
1149
|
-
else
|
|
1150
|
-
result[k] = hash[k]
|
|
1151
|
-
end
|
|
1152
|
-
elsif hash.is_a?(Hash) and hash.key?(kn.first.to_sym)
|
|
1153
|
-
keys_nested = []
|
|
1154
|
-
keys.grep(/^#{kn.first}\./).each do |k2|
|
|
1155
|
-
keys_nested << k2.to_s.gsub(/^#{kn.first}\./,'').to_sym
|
|
1156
|
-
end
|
|
1157
|
-
result[kn.first.to_sym] = nice_filter(hash[kn.first.to_sym], keys_nested)
|
|
1158
|
-
elsif hash.is_a?(Array)
|
|
1159
|
-
result = []
|
|
1160
|
-
hash.each do |a|
|
|
1161
|
-
res = nice_filter(a, keys)
|
|
1162
|
-
result << res unless res.empty?
|
|
1163
|
-
end
|
|
1164
|
-
end
|
|
1165
|
-
end
|
|
1166
|
-
return result
|
|
1167
|
-
end
|
|
1168
51
|
end
|