Class: NiceHash

Inherits:
Object show all
Defined in:
lib/nice_hash.rb,
lib/nice/hash/generate.rb,
lib/nice/hash/validate.rb,
lib/nice/hash/get_values.rb,
lib/nice/hash/select_key.rb,
lib/nice/hash/set_nested.rb,
lib/nice/hash/set_values.rb,
lib/nice/hash/transtring.rb,
lib/nice/hash/nice_filter.rb,
lib/nice/hash/get_all_keys.rb,
lib/nice/hash/delete_nested.rb,
lib/nice/hash/select_fields.rb,
lib/nice/hash/pattern_fields.rb,
lib/nice/hash/change_one_by_one.rb,
lib/nice/hash/compare_structure.rb

Overview

NiceHash creates hashes following certain patterns so your testing will be much easier. You can easily generates all the hashes you want following the criteria you specify. 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. You will be able to generate thousands of different hashes just declaring one and test easily APIs based on JSON for example. To generate the strings following a pattern take a look at the documentation for string_pattern gem: https://github.com/MarioRuiz/string_pattern This is the Hash we will be using on the examples declared on the methods source code documentation: my_hash={ name: 'Peter', address: '#$$$$$', correct: :'10-30:L_', city: 'Germany', correct: :'Madrid|Barcelona|London|Akureyri', products: [ { name: :'10:Ln', price: '-20', correct: :'1-10:N' }, { name: :'10:Ln', price: '-20', correct: :'1-10:N' }, ] }

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.valuesObject (readonly)

Returns the value of attribute values.



48
49
50
# File 'lib/nice_hash.rb', line 48

def values
  @values
end

Class Method Details

.change_one_by_one(patterns_hash, values_hash) ⇒ Object

Change only one value at a time and return an Array of Hashes 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. 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 input: patterns_hash: (Hash) Hash where we have defined the patterns to follow. (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key values_hash: (Hash) Hash of values to use to modify the values generated on patterns_hash output: (Array of Hashes) example: wrong_min_length_hash = my_hash.generate(:correct, errors: :min_length) array_of_hashes = NiceHash.change_one_by_one([my_hash, :correct], wrong_min_length_hash) array_of_hashes.each {|hash_with_one_wrong_field| #Here your code to send through http the JSON data stored in hash_with_one_wrong_field #if you want to know which field is the one that is wrong: res = my_hash.validate(:correct, hash_with_one_wrong_field) }



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/nice/hash/change_one_by_one.rb', line 21

def NiceHash.change_one_by_one(patterns_hash, values_hash)
  if patterns_hash.kind_of?(Array)
    select_key = patterns_hash[1]
    pattern_hash = patterns_hash[0]
  else
    pattern_hash = patterns_hash
    select_key = []
  end
  array = Array.new
  good_values = NiceHash.generate(pattern_hash, select_key)
  select_keys = pattern_hash.pattern_fields(select_key) + pattern_hash.select_fields(select_key)
  select_keys.each { |field|
    new_hash = Marshal.load(Marshal.dump(good_values))
    # to deal with the case same values... like in pwd1, pwd2, pwd3
    if field[-1].kind_of?(Array)
      last_to_set = field[-1]
    else
      last_to_set = [field[-1]]
    end
    last_to_set.each { |f|
      keys = field[0..-2] << f
      new_hash.bury(keys, values_hash.dig(*keys))
    }
    array << new_hash
  }
  return array
end

.compare_structure(structure, replica, compare_only_if_exist_key = false, patterns = {}) ⇒ Object

Analyzes the supplied replica and verifies that the structure follows the one supplied on structure

@param structure [Array] [Hash] Contains the structure that should follow the replica. It can be a nested combination of arrays and hashes. @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. @param compare_only_if_exist_key Boolean If true, in case an element exist on structure but doesn't exist on replica won't be verified. @param patterns [Hash] add verification of data values following the patterns supplied on a one level hash

@return [Boolean] true in case replica follows the structure supplied

@example my_structure = [ { name: 'xxx', zip: 'yyyy', customer: true, product_ids: [1] } ] my_replica = [ 'Peter Ben', zip: '1121A', customer: false, product_ids: [], 'John Woop', zip: '74014', customer: true, product_ids: [10,120,301]] NiceHash.compare_structure(my_structure, my_replica) #>true



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/nice/hash/compare_structure.rb', line 25

def NiceHash.compare_structure(structure, replica, compare_only_if_exist_key = false, patterns = {})
  patterns = {} if patterns.nil?
  unless structure.class == replica.class or
          ((structure.is_a?(TrueClass) or structure.is_a?(FalseClass)) and (replica.is_a?(TrueClass) or replica.is_a?(FalseClass)))
    puts "NiceHash.compare_structure: different object type #{structure.class} is not #{replica.class}. expected: #{structure.inspect}. found: #{replica.inspect}."
    return false
  end
  success = true
  if structure.is_a?(Hash)
    patterns = {} unless patterns.is_a?(Hash)
    structure.each do |key, value|
      if patterns.key?(key) and replica.key?(key)
        unless (patterns[key].is_a?(Array) and replica[key].is_a?(Array) and replica[key].empty?) or 
          (compare_only_if_exist_key and replica.key?(key) and replica[key].nil?) or 
          {key => patterns[key]}.validate({key => replica[key]}).empty?
          puts "NiceHash.compare_structure: key :#{key} not following pattern #{patterns[key]}. value: #{replica[key]}"
          success = false
        end
      end

      if compare_only_if_exist_key and replica.key?(key) and !replica[key].nil?
        unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
          puts "NiceHash.compare_structure: key :#{key} different."
          success = false
        end
      elsif compare_only_if_exist_key == false
        unless replica.key?(key)
          puts "NiceHash.compare_structure: key :#{key} missing."
          success = false
        else
          unless compare_structure(value, replica[key], compare_only_if_exist_key, patterns)
            puts "NiceHash.compare_structure: key :#{key} different."
            success = false
          end
        end
      end
    end
  elsif structure.is_a?(Array)
    # compare all elements of replica with the structure of the first element on structure
    replica.each do |elem|
      unless compare_structure(structure[0], elem, compare_only_if_exist_key, patterns)
        success = false
      end
    end
  end
  return success
end

.delete_nested(hash, nested_key) ⇒ Object

Deletes the supplied nested key

@param hash [Hash] The hash we want @param nested_key [Hash] [String] String with the nested key: 'uno.dos.tres' or a hash { uno: :tres }

@return [Hash]

@example my_hash = { user: { address: { city: 'Madrid', country: 'Spain' }, name: 'Peter', age: 33 }, customer: true } NiceHash.delete_nested(my_hash, 'user.address.city') #>:user=>{:address=>{:country=>"Spain", :name=>"Peter", :age=>33}, :customer=>true}



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/nice/hash/delete_nested.rb', line 24

def self.delete_nested(hash, nested_key)
  nested_key = transtring(nested_key)
  keys = nested_key.split(".")
  if keys.size == 1
    hash.delete(nested_key.to_sym)
  else
    last_key = keys[-1]
    eval("hash.#{keys[0..(keys.size - 2)].join(".")}.delete(:#{last_key})")
  end
  return hash
end

.generate(pattern_hash, select_hash_key = nil, expected_errors: [], **synonyms) ⇒ Object

It will generate a new hash with the values generated from the string patterns and select fields specified. 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 If expected_errors specified the values will be generated with the specified errors. input: pattern_hash: (Hash) Hash we want to use to generate the values select_hash_key: (key value) (optional) The key we want to select on the subhashes expected_errors: (Array) (optional) (alias: errors) To generate the string patterns with the specified errors. The possible values you can specify is one or more of these ones: :length: wrong length, minimum or maximum :min_length: wrong minimum length :max_length: wrong maximum length :value: wrong resultant value :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern. :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. :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string. output: (Hash) The Hash with the select_hash_key selected and the values generated from the string patterns and select fields specified. examples: new_hash = NiceHash.generate(my_hash) #> :address=>{:wrong=>"#$$$$$", :correct=>"KZPCzxsWGMLqonesu wbqH", :city=>:correct=>"Barcelona", :products=> [ :price=>{:wrong=>"-20", :correct=>"34338330"}, :price=>{:wrong=>"-20", :correct=>"616066520"} ] } new_hash = NiceHash.generate(my_hash, :correct) #> :address=>"juQeAVZjIuWBPsE", :city=>"Madrid", :products=> [ {:name=>"G44Ilr0puV", :price=>"477813", :price=>"74820" ] } new_hash = NiceHash.generate(my_hash, :correct, expected_errors: [:min_length]) #> :address=>"ZytjefJ", :city=>"Madri", :products=>[ {:name=>"cIBrzeO", :price=>"", :price=>"" ] } Using it directly on Hash class, generate(select_hash_key=nil, expected_errors: []) (alias: gen): new_hash = my_hash.generate new_hash = my_hash.gen(:correct) new_hash = my_hash.generate(:correct, errors: [:min_length])



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/nice/hash/generate.rb', line 54

def NiceHash.generate(pattern_hash, select_hash_key = nil, expected_errors: [], **synonyms)
  hashv = Hash.new()
  same_values = Hash.new()
  expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
  if expected_errors.kind_of?(Symbol)
    expected_errors = [expected_errors]
  end

  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
    pattern_hash.each { |key, value|
      if key.kind_of?(Array)
        same_values[key[0]] = key.dup
        same_values[key[0]].shift
        key = key[0]
      end
      if value.kind_of?(Hash)
        if value.keys.include?(select_hash_key)
          value = value[select_hash_key]
        else
          value = NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
        end
      end
      if value.kind_of?(String) or value.kind_of?(Symbol)
        if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
          hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
        elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
          if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round == 0)
            min = value.to_s.split("|").min { |a, b| a.size <=> b.size }
            hashv[key] = min[0..-2] unless min == ""
          end
          if !hashv.keys.include?(key) and (expected_errors.include?(:max_length) or expected_errors.include?(:length))
            max = value.to_s.split("|").max { |a, b| a.size <=> b.size }
            hashv[key] = max + max[-1]
          end
          if expected_errors.include?(:value) or
             expected_errors.include?(:string_set_not_allowed) or
             expected_errors.include?(:required_data)
            if hashv.keys.include?(key)
              v = hashv[key]
            else
              v = value.to_s.split("|").sample
            end
            unless expected_errors.include?(:string_set_not_allowed)
              v = StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
              hashv[key] = v unless value.to_s.split("|").include?(v)
            end
            unless hashv.keys.include?(key)
              one_wrong_letter = StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
              v[rand(v.size)] = one_wrong_letter
              hashv[key] = v unless value.to_s.split("|").include?(v)
            end
          end
          unless hashv.keys.include?(key)
            hashv[key] = value.to_s.split("|").sample
          end
        else
          hashv[key] = value
        end
      elsif value.kind_of?(Array)
        array_pattern = false
        if value.size == 1
          v = value[0]
          if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
            hashv[key] = []
            (rand(5)+1).times do 
              hashv[key]<<StringPattern.generate(v, expected_errors: expected_errors)
            end
            array_pattern = true
          end
        else
          value.each { |v|
            if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
              hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
              array_pattern = true
              break
            end
          }
        end
        unless array_pattern
          value_ret = Array.new
          value.each { |v|
            if v.is_a?(Hash)
              ret = NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
            else
              ret = NiceHash.generate({ doit: v }, select_hash_key, expected_errors: expected_errors)
              ret = ret[:doit] if ret.is_a?(Hash) and ret.key?(:doit)
            end
            ret = v if ret.kind_of?(Hash) and ret.size == 0
            value_ret << ret
          }
          hashv[key] = value_ret
        end
      elsif value.kind_of?(Range)
        if expected_errors.empty?
          if value.size == Float::INFINITY
            value = (value.min..2**64)
          end
          hashv[key] = rand(value)
        else
          if value.size == Float::INFINITY
            infinite = true
            value = (value.min..2**64)
          else
            infinite = false
            hashv[key] = rand(value)
          end
          expected_errors.each do |er|
            if er == :min_length
              hashv[key] = rand((value.first - value.last)..value.first - 1)
            elsif er == :max_length and !infinite
              hashv[key] = rand((value.last + 1)..(value.last * 2))
            elsif er == :length
              if rand.round == 1 or infinite
                hashv[key] = rand((value.first - value.last)..value.first - 1)
              else
                hashv[key] = rand((value.last + 1)..(value.last * 2))
              end
            elsif er == :value
              hashv[key] = :"1-10:N/L/".gen
            end
          end
        end
      elsif value.kind_of?(Class) and value == DateTime
        if expected_errors.empty?
          hashv[key] = Time.now.stamp
        else
          hashv[key] = Time.now.stamp
          expected_errors.each do |er|
            if er == :min_length
              hashv[key] = hashv[key].chop
            elsif er == :max_length
              hashv[key] = hashv[key] + "Z"
            elsif er == :length
              if rand.round == 1
                hashv[key] = hashv[key].chop
              else
                hashv[key] = hashv[key] + "Z"
              end
            elsif er == :value
              hashv[key][rand(hashv[key].size - 1)] = "1:L".gen
            end
          end
        end
      elsif value.kind_of?(Module) and value == Boolean
        if expected_errors.empty?
          hashv[key] = (rand.round == 0)
        else
          hashv[key] = (rand.round == 0)
          expected_errors.each do |er|
            if er == :value
              hashv[key] = "1-10:L".gen
            end
          end
        end
      elsif value.kind_of?(Proc)
        hashv[key] = value.call
      elsif value.kind_of?(Regexp)
        hashv[key] = value.generate(expected_errors: expected_errors)
      else
        hashv[key] = value
      end

      if same_values.include?(key)
        same_values[key].each { |k|
          hashv[k] = hashv[key]
        }
      end

      @values = hashv
    }
  end

  return hashv
end

.get_all_keys(h) ⇒ Object

Get all the keys of a hash

@param hash [Hash] The hash

@return [Array]

@example my_hash = { uno: {tres: 3} } NiceHash.get_all_keys(my_hash) #>[:uno, :dos, :tres]



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/nice/hash/get_all_keys.rb', line 14

def self.get_all_keys(h)
  h.each_with_object([]) do |(k, v), keys|
    keys << k
    keys.concat(get_all_keys(v)) if v.is_a? Hash
    if v.is_a?(Array)
      v.each do |vv|
        keys.concat(get_all_keys(vv)) if vv.is_a? Hash or vv.is_a? Array
      end
    end
  end
end

.get_values(hashv, keys) ⇒ Object

Get values from the Hash structure (array of Hashes allowed) In case the key supplied doesn't exist in the hash then it will be returned nil for that one input: hashv: a simple hash or a hash containing arrays. Example: example={"id"=>344, "customer"=>{ "name"=>"Peter Smith", "phone"=>334334333 }, "tickets"=>[ "idt"=>345,"name"=>"myFavor1", "idt"=>3123, ticket" ] } keys: one key (string) or an array of keys. key can be a nested key output: a Hash of Arrays with all values found. Example of output with example.get_values("id","name") "name"=>["Peter Smith", ["myFavor1", "Special ticket"]] Example of output with example.get_values("idt") "idt"=>[345,3123,3145] Example of output with example.get_values(:'tickets.idt') :"tickets:"tickets.idt"=>[345,3123,3145]



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/nice/hash/get_values.rb', line 30

def NiceHash.get_values(hashv, keys)
  #todo: check if we should return {"id"=>344, "name"=>["Peter Smith", "myFavor1", "Special ticket"]} instead of
  # {"id"=>344, "name"=>["Peter Smith", ["myFavor1", "Special ticket"]]}
  if keys.kind_of?(String) or keys.kind_of?(Symbol)
    keys = [keys]
  end
  if (keys.grep(/\./)).empty?
    nested = false
  else
    nested = true
  end
  result = Hash.new()
  number_of_results = Hash.new()
  keys.each { |key|
    number_of_results[key] = 0
  }
  if hashv.kind_of?(Array)
    hashv.each { |tmp|
      if tmp.kind_of?(Array) or tmp.kind_of?(Hash)
        n_result = get_values(tmp, keys)
        if n_result != :error
          n_result.each { |n_key, n_value|
            if result.has_key?(n_key)
              if !result[n_key].kind_of?(Array) or
                 (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
                if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
                  res_tx = result[n_key].dup()
                else
                  res_tx = result[n_key]
                end
                result[n_key] = Array.new()
                result[n_key].push(res_tx)
                result[n_key].push(n_value)
              else
                result[n_key].push(n_value)
              end
            else
              result[n_key] = n_value
            end
            number_of_results[n_key] += 1
          }
        end
      end
    }
  elsif hashv.kind_of?(Hash)
    hashv.each { |key, value|
      #if keys.include?(key) then
      #added to be able to access the keys with symbols to strings and opposite
      if keys.include?(key) or keys.include?(key.to_s) or keys.include?(key.to_sym)
        #todo: check on next ruby versions since it will be not necessary to do it
        #added to be able to access the keys with symbols to strings and opposite
        key = key.to_s() if keys.include?(key.to_s)
        key = key.to_sym() if keys.include?(key.to_sym)

        if result.has_key?(key)
          if !result[key].kind_of?(Array) or
             (result[key].kind_of?(Array) and number_of_results[key] < result[key].size)
            if result[key].kind_of?(Hash) or result[key].kind_of?(Array)
              res_tx = result[key].dup()
            else
              res_tx = result[key]
            end
            result[key] = Array.new()
            result[key].push(res_tx)
            result[key].push(value)
          else
            result[key].push(value)
          end
        else
          result[key] = value
        end
        number_of_results[key] += 1
      end
      if value.kind_of?(Array) or value.kind_of?(Hash)
        if nested
          keys_nested = []
          keys.grep(/^#{key}\./).each do |k|
            keys_nested << k.to_s.gsub(/^#{key}\./,'').to_sym
          end
          n_result_tmp = get_values(value, keys_nested)
          n_result = {}
          n_result_tmp.each do |k,v|
            n_result["#{key}.#{k}".to_sym] = v
          end
        else
          n_result = get_values(value, keys)
        end
        if n_result != :error
          n_result.each { |n_key, n_value|
            if result.has_key?(n_key)
              if !result[n_key].kind_of?(Array) or
                 (result[n_key].kind_of?(Array) and number_of_results[n_key] < result[n_key].size)
                if result[n_key].kind_of?(Hash) or result[n_key].kind_of?(Array)
                  res_tx = result[n_key].dup()
                else
                  res_tx = result[n_key]
                end
                result[n_key] = Array.new()
                result[n_key].push(res_tx)
                result[n_key].push(n_value)
              else
                result[n_key].push(n_value)
              end
            else
              result[n_key] = n_value
            end
            number_of_results[n_key] += 1
          }
        end
      end
    }
  else
    return :error
  end
  if result.kind_of?(Hash) and caller[0]["get_values"].nil? #no error or anything weird
    (keys - result.keys).each { |k| #in case some keys don't exist in the hash
      result[k] = nil
    }
  end
  return result
end

.nice_filter(hash, keys) ⇒ Object

Filter the hash supplied and returns only the specified keys

@param hash [Hash] The hash we want to filter @param keys [Array] [Symbol] Array of symbols or symbol. Nested keys can be used: 'uno.dos.tres'

@return [Hash]

@example my_hash = { user: { address: { city: 'Madrid', country: 'Spain' }, name: 'Peter', age: 33, customers: ['Peter', currency: 'Euro', currency: 'Euro'] }, customer: true } NiceHash.nice_filter(my_hash, [:'user.address.city', :'customer', :'user.customers.name']) #> => {:address => {:city => "Madrid", :customers => [=> "Peter", => "John"]}, :customer => true}



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/nice/hash/nice_filter.rb', line 26

def self.nice_filter(hash, keys)
  result = {}
  keys = [keys] unless keys.is_a?(Array)
  keys.each do |k|
    kn = k.to_s.split('.')
    if hash.is_a?(Hash) and hash.key?(k)
        if hash[k].is_a?(Hash)
            result[k] = {} unless result.key?(k)
        else
            result[k] = hash[k]
        end
    elsif hash.is_a?(Hash) and hash.key?(kn.first.to_sym)
        keys_nested = []
        keys.grep(/^#{kn.first}\./).each do |k2|
          keys_nested << k2.to_s.gsub(/^#{kn.first}\./,'').to_sym
        end
        result[kn.first.to_sym] = nice_filter(hash[kn.first.to_sym], keys_nested)
    elsif hash.is_a?(Array)
        result = []
        hash.each do |a|
            res = nice_filter(a, keys)
            result << res unless res.empty?
        end
    end
  end
  return result
end

.pattern_fields(pattern_hash, *select_hash_key) ⇒ Object

It will return an array of the keys where we are using string patterns.

input: pattern_hash: (Hash) Hash we want to get the pattern_fields select_hash_key: (key value) (optional) The key we want to select on the subhashes output: (Array) Array of the kind: [ [key], [key, subkey, subkey] ] Each value of the array can be used as parameter for the methods: dig, bury examples: NiceHash.pattern_fields(my_hash) #> [ [:address, :correct], [:products, 0, :name], [:products, 0, :price, :correct], [:products, 1, :name], [:products, 1, :price, :correct] ] NiceHash.pattern_fields(my_hash, :correct) #> [ [:address], [:products, 0, :name], [:products, 0, :price], [:products, 1, :name], [:products, 1, :price] ] Using it directly on Hash class, pattern_fields(*select_hash_key) (alias: patterns): my_hash.pattern_fields my_hash.pattern_fields(:correct) my_hash.patterns(:correct)



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/nice/hash/pattern_fields.rb', line 34

def NiceHash.pattern_fields(pattern_hash, *select_hash_key)
  pattern_fields = Array.new

  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
    pattern_hash.each { |key, value|
      key = [key]
      if value.kind_of?(Hash)
        if select_hash_key.size == 1 and value.keys.include?(select_hash_key[0])
          value = value[select_hash_key[0]]
        else
          res = NiceHash.pattern_fields(value, *select_hash_key)
          if res.size > 0
            res.each { |r|
              pattern_fields << (r.unshift(key)).flatten
            }
          end
          next
        end
      end
      if value.kind_of?(String)
        if StringPattern.optimistic and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
          pattern_fields << key
        end
      elsif value.kind_of?(Symbol)
        if value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
          pattern_fields << key
        end
      elsif value.kind_of?(Array)
        array_pattern = false
        value.each { |v|
          if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
            pattern_fields << key
            array_pattern = true
            break
          end
        }
        unless array_pattern
          i = 0
          value.each { |v|
            res = NiceHash.pattern_fields(v, *select_hash_key)
            if res.size > 0
              res.each { |r|
                pattern_fields << (r.unshift(i).unshift(key)).flatten
              }
            end
            i += 1
          }
        end
      end
    }
  end

  return pattern_fields
end

.select_fields(pattern_hash, *select_hash_key) ⇒ Object

It will return an array of the keys where we are using select values of the kind: "value1|value2|value3".

input: pattern_hash: (Hash) Hash we want to get the select_fields select_hash_key: (key value) (optional) The key we want to select on the subhashes output: (Array) Array of the kind: [ [key], [key, subkey, subkey] ] Each value of the array can be used as parameter for the methods: dig, bury examples: NiceHash.select_fields(my_hash) #> [[:city, :correct]] NiceHash.select_fields(my_hash, :correct) #> [[:city]] Using it directly on Hash class, select_fields(*select_hash_key): my_hash.select_fields my_hash.select_fields(:correct)



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/nice/hash/select_fields.rb', line 21

def NiceHash.select_fields(pattern_hash, *select_hash_key)
  select_fields = Array.new

  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
    pattern_hash.each { |key, value|
      key = [key]
      if value.kind_of?(Hash)
        if select_hash_key.size == 1 and value.keys.include?(select_hash_key[0])
          value = value[select_hash_key[0]]
        else
          res = NiceHash.select_fields(value, *select_hash_key)
          if res.size > 0
            res.each { |r|
              select_fields << (r.unshift(key)).flatten
            }
          end
          next
        end
      end
      if value.kind_of?(String)
        if StringPattern.optimistic and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
          select_fields << key
        end
      elsif value.kind_of?(Symbol)
        if value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
          select_fields << key
        end
      elsif value.kind_of?(Array)
        array_pattern = false
        value.each { |v|
          if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
            array_pattern = true
            break
          end
        }
        unless array_pattern
          i = 0
          value.each { |v|
            res = NiceHash.select_fields(v, *select_hash_key)
            if res.size > 0
              res.each { |r|
                select_fields << (r.unshift(i).unshift(key)).flatten
              }
            end
            i += 1
          }
        end
      end
    }
  end

  return select_fields
end

.select_key(pattern_hash, select_hash_key) ⇒ Object

It will filter the hash by the key specified on select_hash_key. In case a subhash specified on a value it will be selected only the value of the key specified on select_hash_key

input: pattern_hash: (Hash) Hash we want to select specific keys select_hash_key: (key value) The key we want to select on the subhashes output: (Hash) 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 example: new_hash = NiceHash.select_key(my_hash, :wrong) #> :address=>"#$$$$$", :city=>"Germany", :products=> [{:name=>:"10:Ln", :price=>"-20", :price=>"-20"]} Using it directly on Hash class, select_key(select_hash_key): new_hash = my_hash.select_key(:wrong)



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/nice/hash/select_key.rb', line 17

def NiceHash.select_key(pattern_hash, select_hash_key)
  hashv = Hash.new()

  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
    pattern_hash.each { |key, value|
      if value.kind_of?(Hash)
        if value.keys.include?(select_hash_key)
          value = value[select_hash_key]
        else
          value = NiceHash.select_key(value, select_hash_key)
        end
      end
      if value.kind_of?(Array)
        array_pattern = false
        value.each { |v|
          if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
            hashv[key] = value
            array_pattern = true
            break
          end
        }
        unless array_pattern
          value_ret = Array.new
          value.each { |v|
            ret = NiceHash.select_key(v, select_hash_key)
            value_ret << ret
          }
          hashv[key] = value_ret
        end
      else
        hashv[key] = value
      end
    }
  else
    return pattern_hash
  end
  return hashv
end

.set_nested(hash, nested_key, value, only_if_exist = false) ⇒ Object

sets the supplied value on the supplied nested key

@param hash [Hash] The hash we want @param nested_key [Hash] [String] String with the nested key: 'uno.dos.tres' or a hash { uno: :tres } @param value [] value to set

@return [Hash]

@example my_hash = { user: { address: { city: 'Madrid', country: 'Spain' }, name: 'Peter', age: 33 }, customer: true } NiceHash.set_nested(my_hash, 'user.address.city', 'Barcelona') #>:country=>"Spain", :name=>"Peter", :age=>33}, :customer=>true}



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/nice/hash/set_nested.rb', line 25

def self.set_nested(hash, nested_key, value, only_if_exist = false)
  nested_key = transtring(nested_key)
  keys = nested_key.split(".")
  if keys.size == 1
    hash[nested_key.to_sym] = value unless only_if_exist and !hash.key?(nested_key.to_sym)
  else
    exist = true
    if only_if_exist
      ht = hash.deep_copy
      keys.each do |k|
        unless ht.key?(k.to_sym)
          exist = false
          break
        end
        ht = ht[k.to_sym]
      end
    end
    if !only_if_exist or (only_if_exist and exist)
      if value.is_a?(String)
        eval("hash.#{nested_key}='#{value}'")
      else
        #todo: consider other kind of objects apart of strings
        eval("hash.#{nested_key}=#{value}")
      end
    end
  end
  return hash
end

.set_values(hash_array, hash_values) ⇒ Object

It will search for the keys supplied and it will set the value specified

input: hash_array: (Hash/Array) Hash/Array we want to search on hash_values: (Hash) Hash that contains the keys and values to set output: (Hash/Array) The same hash/array but with the values we specifed changed example: new_hash = NiceHash.set_values(my_hash, 'London', price: '1000') #> :address=>"#$$$$$", :city=>"London", :products=> [{:name=>:"10:Ln", :price=>"1000", :price=>"1000"]} Using it directly on Hash class, set_values(hash_values): new_hash = my_hash.set_values('London', price: '1000') Setting specific nested keys new_hash = my_hash.set_values(75, 'data.lab.beep': false)



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/nice/hash/set_values.rb', line 18

def NiceHash.set_values(hash_array, hash_values)
  hashv = Hash.new()
  if hash_array.is_a?(Hash) and hash_array.size > 0
    #for the case same_values on hash_values
    #fex: ({pwd1: 'aaaa', pwd2: 'bbbbb', uno: 1}).set_values({[:pwd1, :pwd2]=>:'1-10:n'})
    # should return : {[:pwd1, :pwd2]=>:'1-10:n', uno: 1}
    #todo: it doesn't work for all cases, just simple one
    if hash_values.is_a?(Hash) and hash_values.keys.flatten.size != hash_values.keys.size
      hash_values.each do |k,v|
        if k.is_a?(Array)
          k.each do |kvk|
            if hash_array.key?(kvk)
                hash_array[kvk] = hash_values[k]
            end
          end
        end
      end

    end
    hash_array.each do |k, v|
      #for the case of using same_values: [:pwd1, :pwd2] => :'10:N' and supply hash_values: pwd1: 'a', pwd2: 'b'
      #instead of [:pwd1,:pwd2]=>'a'
      same_values_key_done = false
      if k.is_a?(Array) 
        #todo: we are treating here only a simple case, consider to add also nested keys, array of values...
        k.each do |kvk|
          if hash_values.keys.include?(kvk)
            hashv[k] = hash_values[kvk]
            same_values_key_done = true
          end
        end
      end
      unless same_values_key_done
        if hash_values.keys.include?(k)
          hashv[k] = hash_values[k]
        elsif v.is_a?(Array)
          if hash_values.has_rkey?('\.') # the kind of 'uno.dos.tres'
            new_hash_values = {}
            hash_values.each do |kk,vv|
              if kk.to_s.match?(/^#{k}\./)
                kk = kk.to_s.gsub(/^#{k}\./, '').to_sym
                new_hash_values[kk] = vv
              end
            end
            hashv[k] = NiceHash.set_values(v, new_hash_values)
          else
            hashv[k] = NiceHash.set_values(v, hash_values)
          end
        elsif v.is_a?(Hash)
          if hash_values.has_rkey?('\.') # the kind of 'uno.dos.tres'
            new_hash_values = {}
            hash_values.each do |kk,vv|
              if kk.to_s.match?(/^#{k}\./)
                kk = kk.to_s.gsub(/^#{k}\./, '').to_sym
                new_hash_values[kk] = vv
              end
            end
            hashv[k] = NiceHash.set_values(v, new_hash_values)
          else
            hashv[k] = NiceHash.set_values(v, hash_values)
          end
        else
          hashv[k] = v
        end
      end
    end
    hash_values.each do |k, v|
      hashv = NiceHash.set_nested(hashv, k, v, true) if k.is_a?(Hash)
    end
    return hashv
  elsif hash_array.is_a?(Array) and hash_array.size > 0
    hashv = Array.new
    hash_array.each do |r|
      hashv << NiceHash.set_values(r, hash_values)
    end
    return hashv
  else
    return hash_array
  end
end

.transtring(hash) ⇒ Object

Translate a hash of hashes into a string separted by .

@param hash [Hash] The hash we want to translate

@return [String]

@example my_hash = { uno: :tres } NiceHash.transtring(my_hash) #>"uno.dos.tres"



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/nice/hash/transtring.rb', line 14

def self.transtring(hash)
  keys = []
  if hash.is_a?(Hash)
    hash.each do |k, v|
      if v.is_a?(Hash)
        keys << k
        keys << transtring(v)
      else
        keys << k
        keys << v
      end
    end
  else
    keys << hash
  end
  return keys.join(".")
end

.validate(patterns_hash, values_hash_to_validate, only_patterns: true) ⇒ Object

Validates a given values_hash_to_validate with string patterns and select fields from pattern_hash input: patterns_hash: (Hash) Hash where we have defined the patterns to follow. (Array) In case of array supplied, the pair: [pattern_hash, select_hash_key]. select_hash_key will filter the hash by that key values_hash_to_validate: (Hash) Hash of values to validate only_patterns: (TrueFalse) (by default true) If true it will validate only the patterns and not the other fields output: (Hash) A hash with the validation results. It will return only the validation errors so in case no validation errors found, empty hash. The keys of the hash will be the keys of the values hash with the validation error. The value in case of a pattern, will be an array with one or more of these possibilities: :length: wrong length, minimum or maximum :min_length: wrong minimum length :max_length: wrong maximum length :value: wrong resultant value :required_data: the output string won't include all necessary required data. It works only if required data supplied on the pattern. :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. :string_set_not_allowed: it will include one or more characters that are not supposed to be on the string. The value in any other case it will be false if the value is not corresponding to the expected. examples: values_to_validate = :address=>"fnMuKW", :city=>"Dublin", :products=>[{:name=>"V4", :price=>"344", :price=>"a"] } results = NiceHash.validate([my_hash, :correct], values_to_validate) #> :length], :products=> [{:name=>[:min_length, :length], :length], :price=>[:value, :string_set_not_allowed] ] } results = NiceHash.validate([my_hash, :correct], values_to_validate, only_patterns: false) #> :length], :city=>false, :products=> [{:name=>[:min_length, :length], :length], :price=>[:value, :string_set_not_allowed] ] } Using it directly on Hash class: validate(select_hash_key=nil, values_hash_to_validate) (alias: val) validate_patterns(select_hash_key=nil, values_hash_to_validate)

results = my_hash.validate_patterns(:correct, values_to_validate) results = my_hash.validate(:correct, values_to_validate)



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/nice/hash/validate.rb', line 49

def NiceHash.validate(patterns_hash, values_hash_to_validate, only_patterns: true)
  if patterns_hash.kind_of?(Array)
    pattern_hash = patterns_hash[0]
    select_hash_key = patterns_hash[1]
  elsif patterns_hash.kind_of?(Hash)
    pattern_hash = patterns_hash
    select_hash_key = nil
  else
    puts "NiceHash.validate wrong pattern_hash supplied #{patterns_hash.inspect}"
    return { error: :error }
  end
  values = values_hash_to_validate
  if pattern_hash.keys.size == get_all_keys(pattern_hash).size and values.keys.size != get_all_keys(values) and
    pattern_hash.keys.size == pattern_hash.keys.flatten.size # dont't set_values for the case of same_value 
    # all patterns on patterns_hash are described on first level, so no same structure than values
    pattern_hash = values.set_values(pattern_hash)
  end
  results = {}
  same_values = {}
  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
    pattern_hash.each { |key, value|
      if key.kind_of?(Array)
        same_values[key[0]] = key.dup
        same_values[key[0]].shift
        key = key[0]
      end
      if value.kind_of?(Hash)
        if !select_hash_key.nil? and value.keys.include?(select_hash_key)
          value = value[select_hash_key]
        elsif values.keys.include?(key) and values[key].kind_of?(Hash)
          res = NiceHash.validate([value, select_hash_key], values[key], only_patterns: only_patterns)
          results[key] = res if res.size > 0
          next
        end
      end

      if values.keys.include?(key)
        if value.kind_of?(String) or value.kind_of?(Symbol)
          if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
            res = StringPattern.validate(pattern: value, text: values[key])
            results[key] = res if res.size > 0
          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
            results[key] = false unless value.to_s.split("|").include?(values[key])
          elsif !only_patterns
            results[key] = false unless value.to_s == values[key].to_s
          end
        elsif value.kind_of?(Range)
          if value.size.to_s == 'Infinity'
            if values[key].class != value.first.class
              results[key] = false
            elsif values[key] < value.first
              results[key] = false
            end
          else
            if values[key].class != value.first.class or values[key].class != value.last.class
              results[key] = false
            elsif values[key] < value.first or values[key] > value.last
              results[key] = false
            end
          end
        elsif value.kind_of?(Class) and value == DateTime
          if values[key].is_a?(String) and values[key].size == 24
            d = Date.strptime(values[key], "%Y-%m-%dT%H:%M:%S.%LZ") rescue results[key] = false
          elsif values[key].is_a?(Time) or values[key].is_a?(Date) or values[key].is_a?(DateTime)
            # correct
          else
            results[key] = false
          end
        elsif value.kind_of?(Module) and value == Boolean
          results[key] = false unless values[key].is_a?(Boolean)
        elsif value.kind_of?(Regexp)
          rex = Regexp.new("^#{value}$")
          unless values[key].to_s.match?(rex)
            results[key] = false
          end
        elsif value.kind_of?(Array)
          array_pattern = false
          complex_data = false
          value.each { |v|
            if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
              if value.is_a?(Array) and value.size == 1 and values[key].is_a?(Array)
                results[key] ||= []
                values[key].each do |vk|
                  res = StringPattern.validate(pattern: value[0], text: vk)
                  results[key] << res
                end
                if results[key].flatten.size == 0
                  results.delete(key) 
                end
              else
                res = StringPattern.validate(pattern: value, text: values[key])
                results[key] = res if res == false
              end
              array_pattern = true
              break
            elsif v.kind_of?(Hash) or v.kind_of?(Array) or v.kind_of?(Struct)
              complex_data = true
              break
            end
          }
          unless array_pattern or results.include?(key)
            if value.size == 1 and values[key].is_a?(Array) and values[key].size > 0
              # for the case value == ['Ford|Newton|Seat'] and values == ['Ford', 'Newton', 'Ford']
              i = 0
              if values[key].class == value.class
                values[key].each do |v|
                  if value[0].is_a?(Hash)
                    res = NiceHash.validate([value[0], select_hash_key], v, only_patterns: only_patterns)
                  else
                    # for the case {cars: ['Ford|Newton|Seat']}
                    res = NiceHash.validate([{ key => value[0] }, select_hash_key], { key => v }, only_patterns: only_patterns)
                    #res = {key => res[:doit]} if res.is_a?(Hash) and res.key?(:doit)
                    array_pattern = true
                  end
                  if res.size > 0
                    results[key] = Array.new() if !results.keys.include?(key)
                    results[key][i] = res
                  end
                  i += 1
                end
              else
                results[key] = false
              end
            else
              i = 0
              value.each { |v|
                if v.is_a?(Hash)
                  res = NiceHash.validate([v, select_hash_key], values[key][i], only_patterns: only_patterns)
                elsif v.is_a?(Array)
                  # for the case {cars: ['Ford|Newton|Seat']}
                  res = NiceHash.validate([{ key => v }, select_hash_key], { key => values[key][i] }, only_patterns: only_patterns)
                  array_pattern = true
                else
                  res = []
                end
                if res.size > 0
                  results[key] = Array.new() if !results.keys.include?(key)
                  results[key][i] = res
                end
                i += 1
              }
            end
          end
          unless array_pattern or only_patterns or results.include?(key) or complex_data
            results[key] = false unless value == values[key]
          end
        else
          unless only_patterns or value.kind_of?(Proc)
            results[key] = false unless value == values[key]
          end
        end
        if same_values.include?(key)
          same_values[key].each { |k|
            if values.keys.include?(k)
              if values[key] != values[k]
                results[k] = "Not equal to #{key}"
              end
            end
          }
        end
      end
    }
  end

  return results
end