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.
@@ -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
@@ -1,7 +1,22 @@
1
1
  SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
2
- #todo: add SP_USE_NESTED_KEYS = true
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