Class: NiceHash
- 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
-
.values ⇒ Object
readonly
Returns the value of attribute values.
Class Method Summary collapse
-
.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.
-
.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.
-
.delete_nested(hash, nested_key) ⇒ Object
Deletes the supplied nested key.
-
.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.
-
.get_all_keys(h) ⇒ Object
Get all the keys of a hash.
-
.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.
-
.nice_filter(hash, keys) ⇒ Object
Filter the hash supplied and returns only the specified keys.
-
.pattern_fields(pattern_hash, *select_hash_key) ⇒ Object
It will return an array of the keys where we are using string patterns.
-
.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".
-
.select_key(pattern_hash, select_hash_key) ⇒ Object
It will filter the hash by the key specified on select_hash_key.
-
.set_nested(hash, nested_key, value, only_if_exist = false) ⇒ Object
sets the supplied value on the supplied nested key.
-
.set_values(hash_array, hash_values) ⇒ Object
It will search for the keys supplied and it will set the value specified.
-
.transtring(hash) ⇒ Object
Translate a hash of hashes into a string separted by .
-
.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.
Class Attribute Details
.values ⇒ Object (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 |