nice_hash 1.7.3 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -4
  3. data/LICENSE +21 -21
  4. data/README.md +654 -646
  5. data/lib/nice/hash/add_to_ruby.rb +240 -240
  6. data/lib/nice_hash.rb +720 -718
  7. metadata +3 -3
data/lib/nice_hash.rb CHANGED
@@ -1,718 +1,720 @@
1
- SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
2
- require_relative 'nice/hash/add_to_ruby' if SP_ADD_TO_RUBY
3
-
4
- require 'string_pattern'
5
-
6
- ###########################################################################
7
- # NiceHash creates hashes following certain patterns so your testing will be much easier.
8
- # You can easily generates all the hashes you want following the criteria you specify.
9
- # Many other features coming to Hash class like the methods 'bury' or select_key, access the keys like methods: my_hash.my_key.other_key.
10
- # You will be able to generate thousands of different hashes just declaring one and test easily APIs based on JSON for example.
11
- # To generate the strings following a pattern take a look at the documentation for string_pattern gem: https://github.com/MarioRuiz/string_pattern
12
- # This is the Hash we will be using on the examples declared on the methods source code documentation:
13
- # my_hash={
14
- # name: 'Peter',
15
- # address: {wrong: '#$$$$$', correct: :'10-30:L_'},
16
- # city: {wrong: 'Germany', correct: :'Madrid|Barcelona|London|Akureyri'},
17
- # products: [
18
- # {
19
- # name: :'10:Ln',
20
- # price: {wrong: '-20', correct: :'1-10:N'}
21
- # },
22
- # {
23
- # name: :'10:Ln',
24
- # price: {wrong: '-20', correct: :'1-10:N'}
25
- # },
26
- # ]
27
- # }
28
- ###########################################################################
29
- class NiceHash
30
- class << self
31
- attr_reader :values
32
- end
33
- @values = {}
34
-
35
- ###########################################################################
36
- # It will filter the hash by the key specified on select_hash_key.
37
- # In case a subhash specified on a value it will be selected only the value of the key specified on select_hash_key
38
- #
39
- # input:
40
- # pattern_hash: (Hash) Hash we want to select specific keys
41
- # select_hash_key: (key value) The key we want to select on the subhashes
42
- # output: (Hash)
43
- # 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
44
- # example:
45
- # new_hash = NiceHash.select_key(my_hash, :wrong)
46
- # #> {:name=>"Peter", :address=>"\#$$$$$", :city=>"Germany", :products=> [{:name=>:"10:Ln", :price=>"-20"}, {:name=>:"10:Ln", :price=>"-20"}]}
47
- # Using it directly on Hash class, select_key(select_hash_key):
48
- # new_hash = my_hash.select_key(:wrong)
49
- ###########################################################################
50
- def NiceHash.select_key(pattern_hash, select_hash_key)
51
- hashv=Hash.new()
52
-
53
- if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
54
- pattern_hash.each {|key, value|
55
-
56
- if value.kind_of?(Hash)
57
- if value.keys.include?(select_hash_key)
58
- value=value[select_hash_key]
59
- else
60
- value=NiceHash.select_key(value, select_hash_key)
61
- end
62
- end
63
- if value.kind_of?(Array)
64
- array_pattern=false
65
- value.each {|v|
66
- if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
67
- hashv[key]=value
68
- array_pattern=true
69
- break
70
- end
71
- }
72
- unless array_pattern
73
- value_ret=Array.new
74
- value.each {|v|
75
- ret=NiceHash.select_key(v, select_hash_key)
76
- value_ret<<ret
77
- }
78
- hashv[key]=value_ret
79
- end
80
- else
81
- hashv[key]=value
82
- end
83
- }
84
- else
85
- return pattern_hash
86
- end
87
- return hashv
88
- end
89
-
90
- ###########################################################################
91
- # It will return an array of the keys where we are using string patterns.
92
- #
93
- # input:
94
- # pattern_hash: (Hash) Hash we want to get the pattern_fields
95
- # select_hash_key: (key value) (optional) The key we want to select on the subhashes
96
- # output: (Array)
97
- # Array of the kind: [ [key], [key, subkey, subkey] ]
98
- # Each value of the array can be used as parameter for the methods: dig, bury
99
- # examples:
100
- # NiceHash.pattern_fields(my_hash)
101
- # #> [
102
- # [:address, :correct],
103
- # [:products, 0, :name],
104
- # [:products, 0, :price, :correct],
105
- # [:products, 1, :name],
106
- # [:products, 1, :price, :correct]
107
- # ]
108
- # NiceHash.pattern_fields(my_hash, :correct)
109
- # #> [
110
- # [:address],
111
- # [:products, 0, :name],
112
- # [:products, 0, :price],
113
- # [:products, 1, :name],
114
- # [:products, 1, :price]
115
- # ]
116
- # Using it directly on Hash class, pattern_fields(*select_hash_key) (alias: patterns):
117
- # my_hash.pattern_fields
118
- # my_hash.pattern_fields(:correct)
119
- # my_hash.patterns(:correct)
120
- ###########################################################################
121
- def NiceHash.pattern_fields(pattern_hash, *select_hash_key)
122
- pattern_fields = Array.new
123
-
124
- if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
125
- pattern_hash.each {|key, value|
126
- key=[key]
127
- if value.kind_of?(Hash)
128
- if select_hash_key.size==1 and value.keys.include?(select_hash_key[0])
129
- value=value[select_hash_key[0]]
130
- else
131
- res=NiceHash.pattern_fields(value, *select_hash_key)
132
- if res.size>0
133
- res.each {|r|
134
- pattern_fields<<(r.unshift(key)).flatten
135
- }
136
- end
137
- next
138
- end
139
- end
140
- if value.kind_of?(String)
141
- if StringPattern.optimistic and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
142
- pattern_fields << key
143
- end
144
- elsif value.kind_of?(Symbol)
145
- if value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
146
- pattern_fields << key
147
- end
148
- elsif value.kind_of?(Array)
149
-
150
- array_pattern=false
151
- value.each {|v|
152
- if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
153
- pattern_fields << key
154
- array_pattern=true
155
- break
156
- end
157
- }
158
- unless array_pattern
159
- i=0
160
- value.each {|v|
161
- res=NiceHash.pattern_fields(v, *select_hash_key)
162
- if res.size>0
163
- res.each {|r|
164
- pattern_fields<<(r.unshift(i).unshift(key)).flatten
165
- }
166
- end
167
- i+=1
168
- }
169
- end
170
- end
171
- }
172
- end
173
-
174
- return pattern_fields
175
- end
176
-
177
- ###########################################################################
178
- # It will return an array of the keys where we are using select values of the kind: "value1|value2|value3".
179
- #
180
- # input:
181
- # pattern_hash: (Hash) Hash we want to get the select_fields
182
- # select_hash_key: (key value) (optional) The key we want to select on the subhashes
183
- # output: (Array)
184
- # Array of the kind: [ [key], [key, subkey, subkey] ]
185
- # Each value of the array can be used as parameter for the methods: dig, bury
186
- # examples:
187
- # NiceHash.select_fields(my_hash)
188
- # #> [[:city, :correct]]
189
- # NiceHash.select_fields(my_hash, :correct)
190
- # #> [[:city]]
191
- # Using it directly on Hash class, select_fields(*select_hash_key):
192
- # my_hash.select_fields
193
- # my_hash.select_fields(:correct)
194
- ###########################################################################
195
- def NiceHash.select_fields(pattern_hash, *select_hash_key)
196
- select_fields = Array.new
197
-
198
- if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
199
- pattern_hash.each {|key, value|
200
- key=[key]
201
- if value.kind_of?(Hash)
202
- if select_hash_key.size==1 and value.keys.include?(select_hash_key[0])
203
- value=value[select_hash_key[0]]
204
- else
205
- res=NiceHash.select_fields(value, *select_hash_key)
206
- if res.size>0
207
- res.each {|r|
208
- select_fields<<(r.unshift(key)).flatten
209
- }
210
- end
211
- next
212
- end
213
- end
214
- if value.kind_of?(String)
215
- if StringPattern.optimistic and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
216
- select_fields << key
217
- end
218
- elsif value.kind_of?(Symbol)
219
- if value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
220
- select_fields << key
221
- end
222
- elsif value.kind_of?(Array)
223
-
224
- array_pattern=false
225
- value.each {|v|
226
- if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
227
- array_pattern=true
228
- break
229
- end
230
- }
231
- unless array_pattern
232
- i=0
233
- value.each {|v|
234
- res=NiceHash.select_fields(v, *select_hash_key)
235
- if res.size>0
236
- res.each {|r|
237
- select_fields<<(r.unshift(i).unshift(key)).flatten
238
- }
239
- end
240
- i+=1
241
- }
242
- end
243
- end
244
- }
245
- end
246
-
247
- return select_fields
248
- end
249
-
250
-
251
- ###########################################################################
252
- # It will generate a new hash with the values generated from the string patterns and select fields specified.
253
- # 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
254
- # If expected_errors specified the values will be generated with the specified errors.
255
- # input:
256
- # pattern_hash: (Hash) Hash we want to use to generate the values
257
- # select_hash_key: (key value) (optional) The key we want to select on the subhashes
258
- # expected_errors: (Array) (optional) (alias: errors) To generate the string patterns with the specified errors.
259
- # The possible values you can specify is one or more of these ones:
260
- # :length: wrong length, minimum or maximum
261
- # :min_length: wrong minimum length
262
- # :max_length: wrong maximum length
263
- # :value: wrong resultant value
264
- # :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
265
- # :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.
266
- # :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
267
- # output: (Hash)
268
- # The Hash with the select_hash_key selected and the values generated from the string patterns and select fields specified.
269
- # examples:
270
- # new_hash = NiceHash.generate(my_hash)
271
- # #> {:name=>"Peter",
272
- # :address=>{:wrong=>"\#$$$$$", :correct=>"KZPCzxsWGMLqonesu wbqH"},
273
- # :city=>{:wrong=>"Germany", :correct=>"Barcelona"},
274
- # :products=> [
275
- # {:name=>"gIqkWygmVm", :price=>{:wrong=>"-20", :correct=>"34338330"}},
276
- # {:name=>"CK68VLIcYf", :price=>{:wrong=>"-20", :correct=>"616066520"}}
277
- # ]
278
- # }
279
- # new_hash = NiceHash.generate(my_hash, :correct)
280
- # #> {:name=>"Peter",
281
- # :address=>"juQeAVZjIuWBPsE",
282
- # :city=>"Madrid",
283
- # :products=> [
284
- # {:name=>"G44Ilr0puV", :price=>"477813"},
285
- # {:name=>"v6ojs79LOp", :price=>"74820"}
286
- # ]
287
- # }
288
- # new_hash = NiceHash.generate(my_hash, :correct, expected_errors: [:min_length])
289
- # #> {:name=>"Peter",
290
- # :address=>"ZytjefJ",
291
- # :city=>"Madri",
292
- # :products=>[
293
- # {:name=>"cIBrzeO", :price=>""},
294
- # {:name=>"5", :price=>""}
295
- # ]
296
- # }
297
- # Using it directly on Hash class, generate(select_hash_key=nil, expected_errors: []) (alias: gen):
298
- # new_hash = my_hash.generate
299
- # new_hash = my_hash.gen(:correct)
300
- # new_hash = my_hash.generate(:correct, errors: [:min_length])
301
- ###########################################################################
302
- def NiceHash.generate(pattern_hash, select_hash_key=nil, expected_errors: [], **synonyms)
303
- hashv=Hash.new()
304
- same_values=Hash.new()
305
- expected_errors=synonyms[:errors] if synonyms.keys.include?(:errors)
306
- if expected_errors.kind_of?(Symbol)
307
- expected_errors=[expected_errors]
308
- end
309
-
310
- if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
311
- pattern_hash.each {|key, value|
312
-
313
- if key.kind_of?(Array)
314
- same_values[key[0]]=key.dup
315
- same_values[key[0]].shift
316
- key=key[0]
317
- end
318
- if value.kind_of?(Hash)
319
- if value.keys.include?(select_hash_key)
320
- value=value[select_hash_key]
321
- else
322
- value=NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
323
- end
324
- end
325
-
326
- if value.kind_of?(String) or value.kind_of?(Symbol)
327
- if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
328
- hashv[key]=StringPattern.generate(value, expected_errors: expected_errors)
329
- elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
330
- if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round==0)
331
- min=value.to_s.split("|").min {|a, b| a.size <=> b.size}
332
- hashv[key]=min[0..-2] unless min==""
333
- end
334
- if !hashv.keys.include?(key) and (expected_errors.include?(:max_length) or expected_errors.include?(:length))
335
- max=value.to_s.split("|").max {|a, b| a.size <=> b.size}
336
- hashv[key]=max+max[-1]
337
- end
338
- if expected_errors.include?(:value) or
339
- expected_errors.include?(:string_set_not_allowed) or
340
- expected_errors.include?(:required_data)
341
- if hashv.keys.include?(key)
342
- v=hashv[key]
343
- else
344
- v=value.to_s.split("|").sample
345
- end
346
- unless expected_errors.include?(:string_set_not_allowed)
347
- v=StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
348
- hashv[key]=v unless value.to_s.split("|").include?(v)
349
- end
350
- unless hashv.keys.include?(key)
351
- one_wrong_letter=StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
352
- v[rand(v.size)]=one_wrong_letter
353
- hashv[key]=v unless value.to_s.split("|").include?(v)
354
- end
355
- end
356
- unless hashv.keys.include?(key)
357
- hashv[key]=value.to_s.split("|").sample
358
- end
359
- else
360
- hashv[key]=value
361
- end
362
- elsif value.kind_of?(Array)
363
- array_pattern=false
364
- value.each {|v|
365
- if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
366
- hashv[key]=StringPattern.generate(value, expected_errors: expected_errors)
367
- array_pattern=true
368
- break
369
- end
370
- }
371
- unless array_pattern
372
- value_ret=Array.new
373
- value.each {|v|
374
- ret=NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
375
- ret=v if ret.kind_of?(Hash) and ret.size==0
376
- value_ret<<ret
377
- }
378
- hashv[key]=value_ret
379
- end
380
- elsif value.kind_of?(Proc)
381
- hashv[key]=value.call
382
- else
383
- hashv[key]=value
384
- end
385
-
386
- if same_values.include?(key)
387
- same_values[key].each {|k|
388
- hashv[k]=hashv[key]
389
- }
390
- end
391
-
392
- @values = hashv
393
-
394
- }
395
- end
396
-
397
- return hashv
398
- end
399
-
400
-
401
- ###########################################################################
402
- # Validates a given values_hash_to_validate with string patterns and select fields from pattern_hash
403
- # input:
404
- # patterns_hash:
405
- # (Hash) Hash where we have defined the patterns to follow.
406
- # (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
407
- # values_hash_to_validate: (Hash) Hash of values to validate
408
- # only_patterns: (TrueFalse) (by default true) If true it will validate only the patterns and not the other fields
409
- # output: (Hash)
410
- # A hash with the validation results. It will return only the validation errors so in case no validation errors found, empty hash.
411
- # The keys of the hash will be the keys of the values hash with the validation error.
412
- # The value in case of a pattern, will be an array with one or more of these possibilities:
413
- # :length: wrong length, minimum or maximum
414
- # :min_length: wrong minimum length
415
- # :max_length: wrong maximum length
416
- # :value: wrong resultant value
417
- # :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
418
- # :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.
419
- # :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
420
- # The value in any other case it will be false if the value is not corresponding to the expected.
421
- # examples:
422
- # values_to_validate = {:name=>"Peter",
423
- # :address=>"fnMuKW",
424
- # :city=>"Dublin",
425
- # :products=>[{:name=>"V4", :price=>"344"}, {:name=>"E", :price=>"a"}]
426
- # }
427
- # results = NiceHash.validate([my_hash, :correct], values_to_validate)
428
- # #> {:address=>[:min_length, :length],
429
- # :products=> [{:name=>[:min_length, :length]},
430
- # {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
431
- # ]
432
- # }
433
- # results = NiceHash.validate([my_hash, :correct], values_to_validate, only_patterns: false)
434
- # #> {:address=>[:min_length, :length],
435
- # :city=>false,
436
- # :products=> [{:name=>[:min_length, :length]},
437
- # {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
438
- # ]
439
- # }
440
- # Using it directly on Hash class:
441
- # validate(select_hash_key=nil, values_hash_to_validate) (alias: val)
442
- # validate_patterns(select_hash_key=nil, values_hash_to_validate)
443
- #
444
- # results = my_hash.validate_patterns(:correct, values_to_validate)
445
- # results = my_hash.validate(:correct, values_to_validate)
446
- ###########################################################################
447
- def NiceHash.validate(patterns_hash, values_hash_to_validate, only_patterns: true)
448
- if patterns_hash.kind_of?(Array)
449
- pattern_hash=patterns_hash[0]
450
- select_hash_key=patterns_hash[1]
451
- elsif patterns_hash.kind_of?(Hash)
452
- pattern_hash=patterns_hash
453
- select_hash_key=nil
454
- else
455
- puts "NiceHash.validate wrong pattern_hash supplied #{patterns_hash.inspect}"
456
- return {error: :error}
457
- end
458
- values = values_hash_to_validate
459
- results={}
460
- same_values={}
461
- if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
462
- pattern_hash.each {|key, value|
463
-
464
- if key.kind_of?(Array)
465
- same_values[key[0]]=key.dup
466
- same_values[key[0]].shift
467
- key=key[0]
468
- end
469
- if value.kind_of?(Hash)
470
- if !select_hash_key.nil? and value.keys.include?(select_hash_key)
471
- value=value[select_hash_key]
472
- elsif values.keys.include?(key) and values[key].kind_of?(Hash)
473
- res=NiceHash.validate([value, select_hash_key], values[key], only_patterns: only_patterns)
474
- results[key]=res if res.size>0
475
- next
476
- end
477
- end
478
-
479
- if values.keys.include?(key)
480
- if value.kind_of?(String) or value.kind_of?(Symbol)
481
- if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
482
- res=StringPattern.validate(pattern: value, text: values[key])
483
- results[key]=res if res.size>0
484
- 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
485
- results[key]=false unless value.to_s.split("|").include?(values[key])
486
- elsif !only_patterns
487
- results[key]=false unless value.to_s==values[key].to_s
488
- end
489
- elsif value.kind_of?(Array)
490
- array_pattern=false
491
- complex_data=false
492
- value.each {|v|
493
- if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
494
- res=StringPattern.validate(pattern: value, text: values[key])
495
- results[key]=res if res==false
496
- array_pattern=true
497
- break
498
- elsif v.kind_of?(Hash) or v.kind_of?(Array) or v.kind_of?(Struct)
499
- complex_data=true
500
- break
501
- end
502
- }
503
- unless array_pattern or results.include?(key)
504
- i=0
505
- value.each {|v|
506
- res=NiceHash.validate([v, select_hash_key], values[key][i], only_patterns: only_patterns)
507
- if res.size>0
508
- results[key]=Array.new() if !results.keys.include?(key)
509
- results[key][i]=res
510
- end
511
- i+=1
512
- }
513
-
514
- end
515
- unless array_pattern or only_patterns or results.include?(key) or complex_data
516
- results[key]=false unless value==values[key]
517
- end
518
-
519
- else
520
- unless only_patterns or value.kind_of?(Proc)
521
- results[key]=false unless value==values[key]
522
- end
523
- end
524
-
525
- if same_values.include?(key)
526
- same_values[key].each {|k|
527
- if values.keys.include?(k)
528
- if values[key]!=values[k]
529
- results[k]="Not equal to #{key}"
530
- end
531
- end
532
- }
533
- end
534
-
535
- end
536
- }
537
-
538
- end
539
-
540
- return results
541
- end
542
-
543
- ###########################################################################
544
- # Change only one value at a time and return an Array of Hashes
545
- # 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.
546
- # 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
547
- # input:
548
- # patterns_hash:
549
- # (Hash) Hash where we have defined the patterns to follow.
550
- # (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
551
- # values_hash: (Hash) Hash of values to use to modify the values generated on patterns_hash
552
- # output: (Array of Hashes)
553
- # example:
554
- # wrong_min_length_hash = my_hash.generate(:correct, errors: :min_length)
555
- # array_of_hashes = NiceHash.change_one_by_one([my_hash, :correct], wrong_min_length_hash)
556
- # array_of_hashes.each {|hash_with_one_wrong_field|
557
- # #Here your code to send through http the JSON data stored in hash_with_one_wrong_field
558
- # #if you want to know which field is the one that is wrong:
559
- # res = my_hash.validate(:correct, hash_with_one_wrong_field)
560
- # }
561
- ###########################################################################
562
- def NiceHash.change_one_by_one(patterns_hash, values_hash)
563
- if patterns_hash.kind_of?(Array)
564
- select_key=patterns_hash[1]
565
- pattern_hash=patterns_hash[0]
566
- else
567
- pattern_hash=patterns_hash
568
- select_key=[]
569
- end
570
- array=Array.new
571
- good_values=NiceHash.generate(pattern_hash, select_key)
572
- select_keys=pattern_hash.pattern_fields(select_key)+pattern_hash.select_fields(select_key)
573
- select_keys.each {|field|
574
- new_hash=Marshal.load(Marshal.dump(good_values))
575
- # to deal with the case same values... like in pwd1, pwd2, pwd3
576
- if field[-1].kind_of?(Array)
577
- last_to_set=field[-1]
578
- else
579
- last_to_set=[field[-1]]
580
- end
581
- last_to_set.each {|f|
582
- keys=field[0..-2]<<f
583
- new_hash.bury(keys, values_hash.dig(*keys))
584
- }
585
- array<<new_hash
586
- }
587
- return array
588
- end
589
-
590
-
591
- ##################################################
592
- # Get values from the Hash structure (array of Hashes allowed)
593
- # In case the key supplied doesn't exist in the hash then it will be return nil for that one
594
- # input:
595
- # hashv: a simple hash or a hash containing arrays. Example:
596
- # example={"id"=>344,
597
- # "customer"=>{
598
- # "name"=>"Peter Smith",
599
- # "phone"=>334334333
600
- # },
601
- # "tickets"=>[
602
- # {"idt"=>345,"name"=>"myFavor1"},
603
- # {"idt"=>3123},
604
- # {"idt"=>3145,"name"=>"Special ticket"}
605
- # ]
606
- # }
607
- # keys: one key (string) or an array of keys
608
- # output:
609
- # a Hash of Arrays with all values found.
610
- # Example of output with example.get_values("id","name")
611
- # {"id"=>[334],"name"=>["Peter North"]}
612
- # Example of output with example.get_values("idt")
613
- # {"idt"=>[345,3123,3145]}
614
- #
615
- ####################################################
616
- def NiceHash.get_values(hashv,keys)
617
- if keys.kind_of?(String) or keys.kind_of?(Symbol) then
618
- keys=[keys]
619
- end
620
- result=Hash.new()
621
- number_of_results=Hash.new()
622
- keys.each {|key|
623
- number_of_results[key]=0
624
- }
625
- if hashv.kind_of?(Array) then
626
- hashv.each {|tmp|
627
- if tmp.kind_of?(Array) or tmp.kind_of?(Hash) then
628
- n_result=get_values(tmp, keys)
629
- if n_result!=:error then
630
- n_result.each {|n_key, n_value|
631
- if result.has_key?(n_key) then
632
- if !result[n_key].kind_of?(Array) or
633
- (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size) then
634
- if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array) then
635
- res_tx=result[n_key].dup()
636
- else
637
- res_tx=result[n_key]
638
- end
639
- result[n_key]=Array.new()
640
- result[n_key].push(res_tx)
641
- result[n_key].push(n_value)
642
- else
643
- result[n_key].push(n_value)
644
- end
645
- else
646
- result[n_key]=n_value
647
- end
648
- number_of_results[n_key]+=1
649
- }
650
- end
651
- end
652
- }
653
- elsif hashv.kind_of?(Hash) then
654
- hashv.each {|key, value|
655
- #if keys.include?(key) then
656
- #added to be able to access the keys with symbols to strings and opposite
657
- if keys.include?(key) or keys.include?(key.to_s) or keys.include?(key.to_sym) then
658
- #added to be able to access the keys with symbols to strings and opposite
659
- key=key.to_s() if keys.include?(key.to_s)
660
- key=key.to_sym() if keys.include?(key.to_sym)
661
-
662
- if result.has_key?(key) then
663
- if !result[key].kind_of?(Array) or
664
- (result[key].kind_of?(Array) and number_of_results[key] < result[key].size) then
665
- if result[key].kind_of?(Hash) or result[key].kind_of?(Array) then
666
- res_tx=result[key].dup()
667
- else
668
- res_tx=result[key]
669
- end
670
- result[key]=Array.new()
671
- result[key].push(res_tx)
672
- result[key].push(value)
673
- else
674
- result[key].push(value)
675
- end
676
- else
677
- result[key]=value
678
- end
679
- number_of_results[key]+=1
680
- end
681
- if value.kind_of?(Array) or value.kind_of?(Hash) then
682
- n_result=get_values(value, keys)
683
- if n_result!=:error then
684
- n_result.each {|n_key, n_value|
685
- if result.has_key?(n_key) then
686
- if !result[n_key].kind_of?(Array) or
687
- (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size) then
688
- if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array) then
689
- res_tx=result[n_key].dup()
690
- else
691
- res_tx=result[n_key]
692
- end
693
- result[n_key]=Array.new()
694
- result[n_key].push(res_tx)
695
- result[n_key].push(n_value)
696
- else
697
- result[n_key].push(n_value)
698
- end
699
- else
700
- result[n_key]=n_value
701
- end
702
- number_of_results[n_key]+=1
703
- }
704
- end
705
- end
706
- }
707
- else
708
- return :error
709
- end
710
- if result.kind_of?(Hash) and caller[0]["get_values"].nil? then #no error or anything weird
711
- (keys-result.keys).each {|k| #in case some keys don't exist in the hash
712
- result[k]=nil
713
- }
714
- end
715
- return result
716
- end
717
-
718
- end
1
+ SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
2
+ require_relative 'nice/hash/add_to_ruby' if SP_ADD_TO_RUBY
3
+
4
+ require 'string_pattern'
5
+
6
+ ###########################################################################
7
+ # NiceHash creates hashes following certain patterns so your testing will be much easier.
8
+ # You can easily generates all the hashes you want following the criteria you specify.
9
+ # Many other features coming to Hash class like the methods 'bury' or select_key, access the keys like methods: my_hash.my_key.other_key.
10
+ # You will be able to generate thousands of different hashes just declaring one and test easily APIs based on JSON for example.
11
+ # To generate the strings following a pattern take a look at the documentation for string_pattern gem: https://github.com/MarioRuiz/string_pattern
12
+ # This is the Hash we will be using on the examples declared on the methods source code documentation:
13
+ # my_hash={
14
+ # name: 'Peter',
15
+ # address: {wrong: '#$$$$$', correct: :'10-30:L_'},
16
+ # city: {wrong: 'Germany', correct: :'Madrid|Barcelona|London|Akureyri'},
17
+ # products: [
18
+ # {
19
+ # name: :'10:Ln',
20
+ # price: {wrong: '-20', correct: :'1-10:N'}
21
+ # },
22
+ # {
23
+ # name: :'10:Ln',
24
+ # price: {wrong: '-20', correct: :'1-10:N'}
25
+ # },
26
+ # ]
27
+ # }
28
+ ###########################################################################
29
+ class NiceHash
30
+ class << self
31
+ attr_reader :values
32
+ end
33
+ @values = {}
34
+
35
+ ###########################################################################
36
+ # It will filter the hash by the key specified on select_hash_key.
37
+ # In case a subhash specified on a value it will be selected only the value of the key specified on select_hash_key
38
+ #
39
+ # input:
40
+ # pattern_hash: (Hash) Hash we want to select specific keys
41
+ # select_hash_key: (key value) The key we want to select on the subhashes
42
+ # output: (Hash)
43
+ # 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
44
+ # example:
45
+ # new_hash = NiceHash.select_key(my_hash, :wrong)
46
+ # #> {:name=>"Peter", :address=>"\#$$$$$", :city=>"Germany", :products=> [{:name=>:"10:Ln", :price=>"-20"}, {:name=>:"10:Ln", :price=>"-20"}]}
47
+ # Using it directly on Hash class, select_key(select_hash_key):
48
+ # new_hash = my_hash.select_key(:wrong)
49
+ ###########################################################################
50
+ def NiceHash.select_key(pattern_hash, select_hash_key)
51
+ hashv=Hash.new()
52
+
53
+ if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
54
+ pattern_hash.each {|key, value|
55
+
56
+ if value.kind_of?(Hash)
57
+ if value.keys.include?(select_hash_key)
58
+ value=value[select_hash_key]
59
+ else
60
+ value=NiceHash.select_key(value, select_hash_key)
61
+ end
62
+ end
63
+ if value.kind_of?(Array)
64
+ array_pattern=false
65
+ value.each {|v|
66
+ if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
67
+ hashv[key]=value
68
+ array_pattern=true
69
+ break
70
+ end
71
+ }
72
+ unless array_pattern
73
+ value_ret=Array.new
74
+ value.each {|v|
75
+ ret=NiceHash.select_key(v, select_hash_key)
76
+ value_ret<<ret
77
+ }
78
+ hashv[key]=value_ret
79
+ end
80
+ else
81
+ hashv[key]=value
82
+ end
83
+ }
84
+ else
85
+ return pattern_hash
86
+ end
87
+ return hashv
88
+ end
89
+
90
+ ###########################################################################
91
+ # It will return an array of the keys where we are using string patterns.
92
+ #
93
+ # input:
94
+ # pattern_hash: (Hash) Hash we want to get the pattern_fields
95
+ # select_hash_key: (key value) (optional) The key we want to select on the subhashes
96
+ # output: (Array)
97
+ # Array of the kind: [ [key], [key, subkey, subkey] ]
98
+ # Each value of the array can be used as parameter for the methods: dig, bury
99
+ # examples:
100
+ # NiceHash.pattern_fields(my_hash)
101
+ # #> [
102
+ # [:address, :correct],
103
+ # [:products, 0, :name],
104
+ # [:products, 0, :price, :correct],
105
+ # [:products, 1, :name],
106
+ # [:products, 1, :price, :correct]
107
+ # ]
108
+ # NiceHash.pattern_fields(my_hash, :correct)
109
+ # #> [
110
+ # [:address],
111
+ # [:products, 0, :name],
112
+ # [:products, 0, :price],
113
+ # [:products, 1, :name],
114
+ # [:products, 1, :price]
115
+ # ]
116
+ # Using it directly on Hash class, pattern_fields(*select_hash_key) (alias: patterns):
117
+ # my_hash.pattern_fields
118
+ # my_hash.pattern_fields(:correct)
119
+ # my_hash.patterns(:correct)
120
+ ###########################################################################
121
+ def NiceHash.pattern_fields(pattern_hash, *select_hash_key)
122
+ pattern_fields = Array.new
123
+
124
+ if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
125
+ pattern_hash.each {|key, value|
126
+ key=[key]
127
+ if value.kind_of?(Hash)
128
+ if select_hash_key.size==1 and value.keys.include?(select_hash_key[0])
129
+ value=value[select_hash_key[0]]
130
+ else
131
+ res=NiceHash.pattern_fields(value, *select_hash_key)
132
+ if res.size>0
133
+ res.each {|r|
134
+ pattern_fields<<(r.unshift(key)).flatten
135
+ }
136
+ end
137
+ next
138
+ end
139
+ end
140
+ if value.kind_of?(String)
141
+ if StringPattern.optimistic and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
142
+ pattern_fields << key
143
+ end
144
+ elsif value.kind_of?(Symbol)
145
+ if value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
146
+ pattern_fields << key
147
+ end
148
+ elsif value.kind_of?(Array)
149
+
150
+ array_pattern=false
151
+ value.each {|v|
152
+ if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
153
+ pattern_fields << key
154
+ array_pattern=true
155
+ break
156
+ end
157
+ }
158
+ unless array_pattern
159
+ i=0
160
+ value.each {|v|
161
+ res=NiceHash.pattern_fields(v, *select_hash_key)
162
+ if res.size>0
163
+ res.each {|r|
164
+ pattern_fields<<(r.unshift(i).unshift(key)).flatten
165
+ }
166
+ end
167
+ i+=1
168
+ }
169
+ end
170
+ end
171
+ }
172
+ end
173
+
174
+ return pattern_fields
175
+ end
176
+
177
+ ###########################################################################
178
+ # It will return an array of the keys where we are using select values of the kind: "value1|value2|value3".
179
+ #
180
+ # input:
181
+ # pattern_hash: (Hash) Hash we want to get the select_fields
182
+ # select_hash_key: (key value) (optional) The key we want to select on the subhashes
183
+ # output: (Array)
184
+ # Array of the kind: [ [key], [key, subkey, subkey] ]
185
+ # Each value of the array can be used as parameter for the methods: dig, bury
186
+ # examples:
187
+ # NiceHash.select_fields(my_hash)
188
+ # #> [[:city, :correct]]
189
+ # NiceHash.select_fields(my_hash, :correct)
190
+ # #> [[:city]]
191
+ # Using it directly on Hash class, select_fields(*select_hash_key):
192
+ # my_hash.select_fields
193
+ # my_hash.select_fields(:correct)
194
+ ###########################################################################
195
+ def NiceHash.select_fields(pattern_hash, *select_hash_key)
196
+ select_fields = Array.new
197
+
198
+ if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
199
+ pattern_hash.each {|key, value|
200
+ key=[key]
201
+ if value.kind_of?(Hash)
202
+ if select_hash_key.size==1 and value.keys.include?(select_hash_key[0])
203
+ value=value[select_hash_key[0]]
204
+ else
205
+ res=NiceHash.select_fields(value, *select_hash_key)
206
+ if res.size>0
207
+ res.each {|r|
208
+ select_fields<<(r.unshift(key)).flatten
209
+ }
210
+ end
211
+ next
212
+ end
213
+ end
214
+ if value.kind_of?(String)
215
+ if StringPattern.optimistic and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
216
+ select_fields << key
217
+ end
218
+ elsif value.kind_of?(Symbol)
219
+ if value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
220
+ select_fields << key
221
+ end
222
+ elsif value.kind_of?(Array)
223
+
224
+ array_pattern=false
225
+ value.each {|v|
226
+ if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
227
+ array_pattern=true
228
+ break
229
+ end
230
+ }
231
+ unless array_pattern
232
+ i=0
233
+ value.each {|v|
234
+ res=NiceHash.select_fields(v, *select_hash_key)
235
+ if res.size>0
236
+ res.each {|r|
237
+ select_fields<<(r.unshift(i).unshift(key)).flatten
238
+ }
239
+ end
240
+ i+=1
241
+ }
242
+ end
243
+ end
244
+ }
245
+ end
246
+
247
+ return select_fields
248
+ end
249
+
250
+
251
+ ###########################################################################
252
+ # It will generate a new hash with the values generated from the string patterns and select fields specified.
253
+ # 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
254
+ # If expected_errors specified the values will be generated with the specified errors.
255
+ # input:
256
+ # pattern_hash: (Hash) Hash we want to use to generate the values
257
+ # select_hash_key: (key value) (optional) The key we want to select on the subhashes
258
+ # expected_errors: (Array) (optional) (alias: errors) To generate the string patterns with the specified errors.
259
+ # The possible values you can specify is one or more of these ones:
260
+ # :length: wrong length, minimum or maximum
261
+ # :min_length: wrong minimum length
262
+ # :max_length: wrong maximum length
263
+ # :value: wrong resultant value
264
+ # :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
265
+ # :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.
266
+ # :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
267
+ # output: (Hash)
268
+ # The Hash with the select_hash_key selected and the values generated from the string patterns and select fields specified.
269
+ # examples:
270
+ # new_hash = NiceHash.generate(my_hash)
271
+ # #> {:name=>"Peter",
272
+ # :address=>{:wrong=>"\#$$$$$", :correct=>"KZPCzxsWGMLqonesu wbqH"},
273
+ # :city=>{:wrong=>"Germany", :correct=>"Barcelona"},
274
+ # :products=> [
275
+ # {:name=>"gIqkWygmVm", :price=>{:wrong=>"-20", :correct=>"34338330"}},
276
+ # {:name=>"CK68VLIcYf", :price=>{:wrong=>"-20", :correct=>"616066520"}}
277
+ # ]
278
+ # }
279
+ # new_hash = NiceHash.generate(my_hash, :correct)
280
+ # #> {:name=>"Peter",
281
+ # :address=>"juQeAVZjIuWBPsE",
282
+ # :city=>"Madrid",
283
+ # :products=> [
284
+ # {:name=>"G44Ilr0puV", :price=>"477813"},
285
+ # {:name=>"v6ojs79LOp", :price=>"74820"}
286
+ # ]
287
+ # }
288
+ # new_hash = NiceHash.generate(my_hash, :correct, expected_errors: [:min_length])
289
+ # #> {:name=>"Peter",
290
+ # :address=>"ZytjefJ",
291
+ # :city=>"Madri",
292
+ # :products=>[
293
+ # {:name=>"cIBrzeO", :price=>""},
294
+ # {:name=>"5", :price=>""}
295
+ # ]
296
+ # }
297
+ # Using it directly on Hash class, generate(select_hash_key=nil, expected_errors: []) (alias: gen):
298
+ # new_hash = my_hash.generate
299
+ # new_hash = my_hash.gen(:correct)
300
+ # new_hash = my_hash.generate(:correct, errors: [:min_length])
301
+ ###########################################################################
302
+ def NiceHash.generate(pattern_hash, select_hash_key=nil, expected_errors: [], **synonyms)
303
+ hashv=Hash.new()
304
+ same_values=Hash.new()
305
+ expected_errors=synonyms[:errors] if synonyms.keys.include?(:errors)
306
+ if expected_errors.kind_of?(Symbol)
307
+ expected_errors=[expected_errors]
308
+ end
309
+
310
+ if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
311
+ pattern_hash.each {|key, value|
312
+
313
+ if key.kind_of?(Array)
314
+ same_values[key[0]]=key.dup
315
+ same_values[key[0]].shift
316
+ key=key[0]
317
+ end
318
+ if value.kind_of?(Hash)
319
+ if value.keys.include?(select_hash_key)
320
+ value=value[select_hash_key]
321
+ else
322
+ value=NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
323
+ end
324
+ end
325
+
326
+ if value.kind_of?(String) or value.kind_of?(Symbol)
327
+ if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
328
+ hashv[key]=StringPattern.generate(value, expected_errors: expected_errors)
329
+ elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size>0
330
+ if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round==0)
331
+ min=value.to_s.split("|").min {|a, b| a.size <=> b.size}
332
+ hashv[key]=min[0..-2] unless min==""
333
+ end
334
+ if !hashv.keys.include?(key) and (expected_errors.include?(:max_length) or expected_errors.include?(:length))
335
+ max=value.to_s.split("|").max {|a, b| a.size <=> b.size}
336
+ hashv[key]=max+max[-1]
337
+ end
338
+ if expected_errors.include?(:value) or
339
+ expected_errors.include?(:string_set_not_allowed) or
340
+ expected_errors.include?(:required_data)
341
+ if hashv.keys.include?(key)
342
+ v=hashv[key]
343
+ else
344
+ v=value.to_s.split("|").sample
345
+ end
346
+ unless expected_errors.include?(:string_set_not_allowed)
347
+ v=StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
348
+ hashv[key]=v unless value.to_s.split("|").include?(v)
349
+ end
350
+ unless hashv.keys.include?(key)
351
+ one_wrong_letter=StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
352
+ v[rand(v.size)]=one_wrong_letter
353
+ hashv[key]=v unless value.to_s.split("|").include?(v)
354
+ end
355
+ end
356
+ unless hashv.keys.include?(key)
357
+ hashv[key]=value.to_s.split("|").sample
358
+ end
359
+ else
360
+ hashv[key]=value
361
+ end
362
+ elsif value.kind_of?(Array)
363
+ array_pattern=false
364
+ value.each {|v|
365
+ if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
366
+ hashv[key]=StringPattern.generate(value, expected_errors: expected_errors)
367
+ array_pattern=true
368
+ break
369
+ end
370
+ }
371
+ unless array_pattern
372
+ value_ret=Array.new
373
+ value.each {|v|
374
+ ret=NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
375
+ ret=v if ret.kind_of?(Hash) and ret.size==0
376
+ value_ret<<ret
377
+ }
378
+ hashv[key]=value_ret
379
+ end
380
+ elsif value.kind_of?(Proc)
381
+ hashv[key]=value.call
382
+ elsif value.kind_of?(Regexp)
383
+ hashv[key]=value.generate
384
+ else
385
+ hashv[key]=value
386
+ end
387
+
388
+ if same_values.include?(key)
389
+ same_values[key].each {|k|
390
+ hashv[k]=hashv[key]
391
+ }
392
+ end
393
+
394
+ @values = hashv
395
+
396
+ }
397
+ end
398
+
399
+ return hashv
400
+ end
401
+
402
+
403
+ ###########################################################################
404
+ # Validates a given values_hash_to_validate with string patterns and select fields from pattern_hash
405
+ # input:
406
+ # patterns_hash:
407
+ # (Hash) Hash where we have defined the patterns to follow.
408
+ # (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
409
+ # values_hash_to_validate: (Hash) Hash of values to validate
410
+ # only_patterns: (TrueFalse) (by default true) If true it will validate only the patterns and not the other fields
411
+ # output: (Hash)
412
+ # A hash with the validation results. It will return only the validation errors so in case no validation errors found, empty hash.
413
+ # The keys of the hash will be the keys of the values hash with the validation error.
414
+ # The value in case of a pattern, will be an array with one or more of these possibilities:
415
+ # :length: wrong length, minimum or maximum
416
+ # :min_length: wrong minimum length
417
+ # :max_length: wrong maximum length
418
+ # :value: wrong resultant value
419
+ # :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern.
420
+ # :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.
421
+ # :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string.
422
+ # The value in any other case it will be false if the value is not corresponding to the expected.
423
+ # examples:
424
+ # values_to_validate = {:name=>"Peter",
425
+ # :address=>"fnMuKW",
426
+ # :city=>"Dublin",
427
+ # :products=>[{:name=>"V4", :price=>"344"}, {:name=>"E", :price=>"a"}]
428
+ # }
429
+ # results = NiceHash.validate([my_hash, :correct], values_to_validate)
430
+ # #> {:address=>[:min_length, :length],
431
+ # :products=> [{:name=>[:min_length, :length]},
432
+ # {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
433
+ # ]
434
+ # }
435
+ # results = NiceHash.validate([my_hash, :correct], values_to_validate, only_patterns: false)
436
+ # #> {:address=>[:min_length, :length],
437
+ # :city=>false,
438
+ # :products=> [{:name=>[:min_length, :length]},
439
+ # {:name=>[:min_length, :length], :price=>[:value, :string_set_not_allowed]}
440
+ # ]
441
+ # }
442
+ # Using it directly on Hash class:
443
+ # validate(select_hash_key=nil, values_hash_to_validate) (alias: val)
444
+ # validate_patterns(select_hash_key=nil, values_hash_to_validate)
445
+ #
446
+ # results = my_hash.validate_patterns(:correct, values_to_validate)
447
+ # results = my_hash.validate(:correct, values_to_validate)
448
+ ###########################################################################
449
+ def NiceHash.validate(patterns_hash, values_hash_to_validate, only_patterns: true)
450
+ if patterns_hash.kind_of?(Array)
451
+ pattern_hash=patterns_hash[0]
452
+ select_hash_key=patterns_hash[1]
453
+ elsif patterns_hash.kind_of?(Hash)
454
+ pattern_hash=patterns_hash
455
+ select_hash_key=nil
456
+ else
457
+ puts "NiceHash.validate wrong pattern_hash supplied #{patterns_hash.inspect}"
458
+ return {error: :error}
459
+ end
460
+ values = values_hash_to_validate
461
+ results={}
462
+ same_values={}
463
+ if pattern_hash.kind_of?(Hash) and pattern_hash.size>0
464
+ pattern_hash.each {|key, value|
465
+
466
+ if key.kind_of?(Array)
467
+ same_values[key[0]]=key.dup
468
+ same_values[key[0]].shift
469
+ key=key[0]
470
+ end
471
+ if value.kind_of?(Hash)
472
+ if !select_hash_key.nil? and value.keys.include?(select_hash_key)
473
+ value=value[select_hash_key]
474
+ elsif values.keys.include?(key) and values[key].kind_of?(Hash)
475
+ res=NiceHash.validate([value, select_hash_key], values[key], only_patterns: only_patterns)
476
+ results[key]=res if res.size>0
477
+ next
478
+ end
479
+ end
480
+
481
+ if values.keys.include?(key)
482
+ if value.kind_of?(String) or value.kind_of?(Symbol)
483
+ if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size>0
484
+ res=StringPattern.validate(pattern: value, text: values[key])
485
+ results[key]=res if res.size>0
486
+ 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
487
+ results[key]=false unless value.to_s.split("|").include?(values[key])
488
+ elsif !only_patterns
489
+ results[key]=false unless value.to_s==values[key].to_s
490
+ end
491
+ elsif value.kind_of?(Array)
492
+ array_pattern=false
493
+ complex_data=false
494
+ value.each {|v|
495
+ if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
496
+ res=StringPattern.validate(pattern: value, text: values[key])
497
+ results[key]=res if res==false
498
+ array_pattern=true
499
+ break
500
+ elsif v.kind_of?(Hash) or v.kind_of?(Array) or v.kind_of?(Struct)
501
+ complex_data=true
502
+ break
503
+ end
504
+ }
505
+ unless array_pattern or results.include?(key)
506
+ i=0
507
+ value.each {|v|
508
+ res=NiceHash.validate([v, select_hash_key], values[key][i], only_patterns: only_patterns)
509
+ if res.size>0
510
+ results[key]=Array.new() if !results.keys.include?(key)
511
+ results[key][i]=res
512
+ end
513
+ i+=1
514
+ }
515
+
516
+ end
517
+ unless array_pattern or only_patterns or results.include?(key) or complex_data
518
+ results[key]=false unless value==values[key]
519
+ end
520
+
521
+ else
522
+ unless only_patterns or value.kind_of?(Proc)
523
+ results[key]=false unless value==values[key]
524
+ end
525
+ end
526
+
527
+ if same_values.include?(key)
528
+ same_values[key].each {|k|
529
+ if values.keys.include?(k)
530
+ if values[key]!=values[k]
531
+ results[k]="Not equal to #{key}"
532
+ end
533
+ end
534
+ }
535
+ end
536
+
537
+ end
538
+ }
539
+
540
+ end
541
+
542
+ return results
543
+ end
544
+
545
+ ###########################################################################
546
+ # Change only one value at a time and return an Array of Hashes
547
+ # 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.
548
+ # 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
549
+ # input:
550
+ # patterns_hash:
551
+ # (Hash) Hash where we have defined the patterns to follow.
552
+ # (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key
553
+ # values_hash: (Hash) Hash of values to use to modify the values generated on patterns_hash
554
+ # output: (Array of Hashes)
555
+ # example:
556
+ # wrong_min_length_hash = my_hash.generate(:correct, errors: :min_length)
557
+ # array_of_hashes = NiceHash.change_one_by_one([my_hash, :correct], wrong_min_length_hash)
558
+ # array_of_hashes.each {|hash_with_one_wrong_field|
559
+ # #Here your code to send through http the JSON data stored in hash_with_one_wrong_field
560
+ # #if you want to know which field is the one that is wrong:
561
+ # res = my_hash.validate(:correct, hash_with_one_wrong_field)
562
+ # }
563
+ ###########################################################################
564
+ def NiceHash.change_one_by_one(patterns_hash, values_hash)
565
+ if patterns_hash.kind_of?(Array)
566
+ select_key=patterns_hash[1]
567
+ pattern_hash=patterns_hash[0]
568
+ else
569
+ pattern_hash=patterns_hash
570
+ select_key=[]
571
+ end
572
+ array=Array.new
573
+ good_values=NiceHash.generate(pattern_hash, select_key)
574
+ select_keys=pattern_hash.pattern_fields(select_key)+pattern_hash.select_fields(select_key)
575
+ select_keys.each {|field|
576
+ new_hash=Marshal.load(Marshal.dump(good_values))
577
+ # to deal with the case same values... like in pwd1, pwd2, pwd3
578
+ if field[-1].kind_of?(Array)
579
+ last_to_set=field[-1]
580
+ else
581
+ last_to_set=[field[-1]]
582
+ end
583
+ last_to_set.each {|f|
584
+ keys=field[0..-2]<<f
585
+ new_hash.bury(keys, values_hash.dig(*keys))
586
+ }
587
+ array<<new_hash
588
+ }
589
+ return array
590
+ end
591
+
592
+
593
+ ##################################################
594
+ # Get values from the Hash structure (array of Hashes allowed)
595
+ # In case the key supplied doesn't exist in the hash then it will be return nil for that one
596
+ # input:
597
+ # hashv: a simple hash or a hash containing arrays. Example:
598
+ # example={"id"=>344,
599
+ # "customer"=>{
600
+ # "name"=>"Peter Smith",
601
+ # "phone"=>334334333
602
+ # },
603
+ # "tickets"=>[
604
+ # {"idt"=>345,"name"=>"myFavor1"},
605
+ # {"idt"=>3123},
606
+ # {"idt"=>3145,"name"=>"Special ticket"}
607
+ # ]
608
+ # }
609
+ # keys: one key (string) or an array of keys
610
+ # output:
611
+ # a Hash of Arrays with all values found.
612
+ # Example of output with example.get_values("id","name")
613
+ # {"id"=>[334],"name"=>["Peter North"]}
614
+ # Example of output with example.get_values("idt")
615
+ # {"idt"=>[345,3123,3145]}
616
+ #
617
+ ####################################################
618
+ def NiceHash.get_values(hashv,keys)
619
+ if keys.kind_of?(String) or keys.kind_of?(Symbol) then
620
+ keys=[keys]
621
+ end
622
+ result=Hash.new()
623
+ number_of_results=Hash.new()
624
+ keys.each {|key|
625
+ number_of_results[key]=0
626
+ }
627
+ if hashv.kind_of?(Array) then
628
+ hashv.each {|tmp|
629
+ if tmp.kind_of?(Array) or tmp.kind_of?(Hash) then
630
+ n_result=get_values(tmp, keys)
631
+ if n_result!=:error then
632
+ n_result.each {|n_key, n_value|
633
+ if result.has_key?(n_key) then
634
+ if !result[n_key].kind_of?(Array) or
635
+ (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size) then
636
+ if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array) then
637
+ res_tx=result[n_key].dup()
638
+ else
639
+ res_tx=result[n_key]
640
+ end
641
+ result[n_key]=Array.new()
642
+ result[n_key].push(res_tx)
643
+ result[n_key].push(n_value)
644
+ else
645
+ result[n_key].push(n_value)
646
+ end
647
+ else
648
+ result[n_key]=n_value
649
+ end
650
+ number_of_results[n_key]+=1
651
+ }
652
+ end
653
+ end
654
+ }
655
+ elsif hashv.kind_of?(Hash) then
656
+ hashv.each {|key, value|
657
+ #if keys.include?(key) then
658
+ #added to be able to access the keys with symbols to strings and opposite
659
+ if keys.include?(key) or keys.include?(key.to_s) or keys.include?(key.to_sym) then
660
+ #added to be able to access the keys with symbols to strings and opposite
661
+ key=key.to_s() if keys.include?(key.to_s)
662
+ key=key.to_sym() if keys.include?(key.to_sym)
663
+
664
+ if result.has_key?(key) then
665
+ if !result[key].kind_of?(Array) or
666
+ (result[key].kind_of?(Array) and number_of_results[key] < result[key].size) then
667
+ if result[key].kind_of?(Hash) or result[key].kind_of?(Array) then
668
+ res_tx=result[key].dup()
669
+ else
670
+ res_tx=result[key]
671
+ end
672
+ result[key]=Array.new()
673
+ result[key].push(res_tx)
674
+ result[key].push(value)
675
+ else
676
+ result[key].push(value)
677
+ end
678
+ else
679
+ result[key]=value
680
+ end
681
+ number_of_results[key]+=1
682
+ end
683
+ if value.kind_of?(Array) or value.kind_of?(Hash) then
684
+ n_result=get_values(value, keys)
685
+ if n_result!=:error then
686
+ n_result.each {|n_key, n_value|
687
+ if result.has_key?(n_key) then
688
+ if !result[n_key].kind_of?(Array) or
689
+ (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size) then
690
+ if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array) then
691
+ res_tx=result[n_key].dup()
692
+ else
693
+ res_tx=result[n_key]
694
+ end
695
+ result[n_key]=Array.new()
696
+ result[n_key].push(res_tx)
697
+ result[n_key].push(n_value)
698
+ else
699
+ result[n_key].push(n_value)
700
+ end
701
+ else
702
+ result[n_key]=n_value
703
+ end
704
+ number_of_results[n_key]+=1
705
+ }
706
+ end
707
+ end
708
+ }
709
+ else
710
+ return :error
711
+ end
712
+ if result.kind_of?(Hash) and caller[0]["get_values"].nil? then #no error or anything weird
713
+ (keys-result.keys).each {|k| #in case some keys don't exist in the hash
714
+ result[k]=nil
715
+ }
716
+ end
717
+ return result
718
+ end
719
+
720
+ end