nice_hash 1.18.7 → 1.19.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c8cb3e741e17cf400b34bc3a31ec874fb030466b2f56431f0ec5aa79e37af7f
4
- data.tar.gz: f023c96b5a5768bac968765032c782d1d01e9705f778748903fb75a0b91d582e
3
+ metadata.gz: 7eae7fae66c4ee14c40481157a8cd6e90ec43781b092779a3344c36a7d0d6308
4
+ data.tar.gz: f3071e0977d3e7e0f0e7ced9699f4823535c1daaa880cbfd7d5693d8329e9513
5
5
  SHA512:
6
- metadata.gz: 448740fdbc4843c705ac79d5d33ba6fcfdf453e7ff76d1a0cf77fa2a648c37ca99d441fcef8378030e2de77a100d907832e216938948c9f3b8ebba3e8ef75b8a
7
- data.tar.gz: 6d3acf6139d0612d7d3ae45b5fec8ca19ba66262f9b4f6b71c26e66c4ca1df68f60a7d6f120307e267d06752d3db3fec43265e82004bf1ef9143ec4ea89531ed
6
+ metadata.gz: 72ced814e688ca65212c8812969947ce77f0496c2e7dbc12e2ab9ef9c396db77da473e5c079788206c6fcc24e05d92b1186c85bfe986c634e75e3b7894208b5a
7
+ data.tar.gz: bce43cdcaa33a6e50a06a364737e3a723156e052b34ba3423574dc4f296743f47c7b089723f72ba2ad617ece70f3ec129617dcf500fc6c5d5cddd8cf8f7bba27
data/README.md CHANGED
@@ -22,31 +22,37 @@ To generate the strings following a pattern take a look at the documentation for
22
22
  To use nice_hash on Http connections take a look at nice_http gem: https://github.com/MarioRuiz/nice_http
23
23
 
24
24
  ## Table of contents
25
- - [Installation](#installation)
26
- - [Usage](#usage)
27
- * [How to access the different keys](#how-to-access-the-different-keys)
28
- * [Change all values on the keys we specified](#change-all-values-on-the-keys-we-specified)
29
- * [Filtering / Selecting an specific key on the hash and subhashes](#filtering---selecting-an-specific-key-on-the-hash-and-subhashes)
30
- * [How to generate the hash with the criteria we want](#how-to-generate-the-hash-with-the-criteria-we-want)
31
- * [How to generate the hash with wrong values for the string patterns specified on the hash](#how-to-generate-the-hash-with-wrong-values-for-the-string-patterns-specified-on-the-hash)
32
- * [Return the select_fields or the pattern_fields](#return-the-select-fields-or-the-pattern-fields)
33
- * [dig and bury Hash methods](#dig-and-bury-hash-methods)
34
- * [Validating hashes](#validating-hashes)
35
- * [Change only one value at a time and return an Array of Hashes](#change-only-one-value-at-a-time-and-return-an-array-of-hashes)
36
- * [Adding other values on run time when calling `generate` method](#adding-other-values-on-run-time-when-calling--generate--method)
37
- + [Accessing other values of the hash on run time](#accessing-other-values-of-the-hash-on-run-time)
38
- * [Compare the structure of a replica with the supplied structure](#compare-the-structure-of-a-replica-with-the-supplied-structure)
39
- * [Other useful methods](#other-useful-methods)
40
- + [Time stamp](#time-stamp)
41
- + [Random dates](#random-dates)
42
- + [Deep copy of a hash](#deep-copy-of-a-hash)
43
- + [Nested deletion](#nested-deletion)
44
- + [Deep merge of two hashes](#deep-merge-of-two-hashes)
45
- + [Boolean class](#boolean-class)
46
- * [Other tools integration](#other-tools-integration)
47
- + [Tabulo](#tabulo)
48
- - [Contributing](#contributing)
49
- - [License](#license)
25
+ - [NiceHash](#nicehash)
26
+ - [Table of contents](#table-of-contents)
27
+ - [Installation](#installation)
28
+ - [Configuration](#configuration)
29
+ - [Usage](#usage)
30
+ - [How to access the different keys](#how-to-access-the-different-keys)
31
+ - [Change all values on the keys we specified](#change-all-values-on-the-keys-we-specified)
32
+ - [Filtering / Selecting an specific key on the hash and subhashes](#filtering--selecting-an-specific-key-on-the-hash-and-subhashes)
33
+ - [How to generate the hash with the criteria we want](#how-to-generate-the-hash-with-the-criteria-we-want)
34
+ - [How to generate the hash with wrong values for the string patterns specified on the hash](#how-to-generate-the-hash-with-wrong-values-for-the-string-patterns-specified-on-the-hash)
35
+ - [Return the select\_fields or the pattern\_fields](#return-the-select_fields-or-the-pattern_fields)
36
+ - [dig and bury Hash methods](#dig-and-bury-hash-methods)
37
+ - [Validating hashes](#validating-hashes)
38
+ - [Change only one value at a time and return an Array of Hashes](#change-only-one-value-at-a-time-and-return-an-array-of-hashes)
39
+ - [Adding other values on run time when calling `generate` method](#adding-other-values-on-run-time-when-calling-generate-method)
40
+ - [Accessing other values of the hash on run time](#accessing-other-values-of-the-hash-on-run-time)
41
+ - [Compare the structure of a replica with the supplied structure](#compare-the-structure-of-a-replica-with-the-supplied-structure)
42
+ - [Other useful methods](#other-useful-methods)
43
+ - [Hash diff](#hash-diff)
44
+ - [Flatten / unflatten keys](#flatten--unflatten-keys)
45
+ - [Time stamp](#time-stamp)
46
+ - [Random dates](#random-dates)
47
+ - [Deep copy of a hash](#deep-copy-of-a-hash)
48
+ - [Nested deletion](#nested-deletion)
49
+ - [Deep merge of two hashes](#deep-merge-of-two-hashes)
50
+ - [Boolean class](#boolean-class)
51
+ - [in?(array)](#inarray)
52
+ - [Other tools integration](#other-tools-integration)
53
+ - [Tabulo](#tabulo)
54
+ - [Contributing](#contributing)
55
+ - [License](#license)
50
56
 
51
57
  ## Installation
52
58
 
@@ -64,9 +70,34 @@ Or install it yourself as:
64
70
 
65
71
  $ gem install nice_hash
66
72
 
73
+ ## Configuration
74
+
75
+ Set these constants **before** `require 'nice_hash'` if you need to change default behavior:
76
+
77
+ - **`SP_ADD_TO_RUBY`** (default: `true`): When `true`, the gem adds methods to core classes (`Hash`, `Array`, `String`, `Object`, `Date`, `Time`, etc.). Set to `false` to avoid patching core classes and use only `NiceHash` class methods.
78
+ - **`SP_COMPARE_NUMBERS_AS_STRINGS`** (default: `true`): When `true`, `String#==` is overridden so that strings are compared with numbers as strings (e.g. `'300' == 300` and `'' == nil`). Set to `false` if this causes issues with other gems (e.g. some versions of `net/ldap`).
79
+
80
+ When using **string_pattern** 2.4+, you can also set **`StringPattern.logger`** (e.g. `Logger.new($stderr)`) to redirect generation/validation messages, and **`StringPattern.raise_on_error = true`** to raise on invalid patterns or impossible generation instead of returning empty strings.
81
+
82
+ Example:
83
+
84
+ ```ruby
85
+ SP_COMPARE_NUMBERS_AS_STRINGS = false
86
+ require 'nice_hash'
87
+ ```
88
+
67
89
  ## Usage
68
90
 
69
- Remember!! To generate the strings following a pattern take a look at the documentation for string_pattern gem: https://github.com/MarioRuiz/string_pattern. You can also generate Spanish or English words. We added support for generating strings from regular expressions but it is only working for the ´generate´ method, use it with caution since it is still on an early stage of development. All you have to do is to add to a key the value as a Regular expression, for example the key uuid in here will generate a random value like this: "E0BDE5B5-A738-49E6-83C1-9D1FFB313788"
91
+ Remember!! To generate the strings following a pattern take a look at the documentation for string_pattern gem: https://github.com/MarioRuiz/string_pattern. You can also generate Spanish or English words. We added support for generating strings from regular expressions but it is only working for the ´generate´ method, use it with caution since it is still on an early stage of development.
92
+
93
+ For **UUID v4** you can use the shorthand `:uuid` (requires string_pattern 2.4+):
94
+
95
+ ```ruby
96
+ my_hash = { id: :uuid, key: "Wsdf88888", doomId: :"10:N" }
97
+ # id will be like "550e8400-e29b-41d4-a716-446655440000"
98
+ ```
99
+
100
+ Or use a Regular expression for custom formats:
70
101
 
71
102
  ```ruby
72
103
  my_hash = {
@@ -371,6 +402,23 @@ new_hash = my_hash.generate(:correct)
371
402
  new_hash = my_hash.select_key(:correct).generate
372
403
  ```
373
404
 
405
+ **Reproducible generation (seed)**
406
+ With **string_pattern** 2.4+, pass `seed:` to get the same hash every time (useful for tests):
407
+
408
+ ```ruby
409
+ new_hash = my_hash.generate(:correct, seed: 42)
410
+ # Same result every time for the same seed
411
+ ```
412
+
413
+ **Generate n hashes at once**
414
+ Use `generate_n` (alias `gen_n`) to build several different hashes in one call (e.g. for bulk or boundary tests):
415
+
416
+ ```ruby
417
+ hashes = NiceHash.generate_n(my_hash, 5, :correct)
418
+ hashes = my_hash.generate_n(5, :correct) # or my_hash.gen_n(5, :correct)
419
+ # With seed: hashes = my_hash.generate_n(5, :correct, seed: 123)
420
+ # => array of 5 hashes
421
+ ```
374
422
 
375
423
  In case of filtering by :correct new_hash would have a value like this for example:
376
424
 
@@ -838,9 +886,35 @@ Valid patterns:
838
886
  - 10.. (from 10 to infinite) Only from Ruby 2.6
839
887
  - DateTime: it will verify if the value is following Time stamp string '2019-06-20T12:01:09.971Z' or if the object is a Time, Date or DateTime class
840
888
  - selectors, one of the values. Example: "uno|dos|tres"
889
+ - **:uuid** (string_pattern 2.4+): UUID v4 format
841
890
 
842
891
  ### Other useful methods
843
892
 
893
+ #### Hash diff
894
+ Compare two hashes and get differences with dot-notation paths (useful for API or test assertions):
895
+
896
+ ```ruby
897
+ expected = { user: { address: { city: "Madrid" } } }
898
+ actual = { user: { address: { city: "London" } } }
899
+ NiceHash.diff(expected, actual)
900
+ #=> { "user.address.city" => { expected: "Madrid", got: "London" } }
901
+
902
+ # Or on a hash instance:
903
+ expected.diff(actual)
904
+ ```
905
+
906
+ #### Flatten / unflatten keys
907
+ Convert between nested hashes and flat hashes with dot-notation keys:
908
+
909
+ ```ruby
910
+ h = { user: { address: { city: "Madrid" } } }
911
+ h.flatten_keys
912
+ #=> { "user.address.city" => "Madrid" }
913
+
914
+ { "user.address.city" => "Madrid" }.unflatten_keys
915
+ #=> { user: { address: { city: "Madrid" } } }
916
+ ```
917
+
844
918
  #### Time stamp
845
919
  In case you need the time stamp, we added the method `stamp` to the `Time` class
846
920
 
@@ -166,6 +166,16 @@ class Hash
166
166
  # my_hash.city="Paris"
167
167
  # my_hash.products[1].price.wrong="AAAAA"
168
168
  ###########################################################################
169
+ def respond_to_missing?(method_name, include_private = false)
170
+ sym = (method_name.to_s[0] == "_" ? method_name.to_s[1..-1].to_sym : method_name)
171
+ return true if key?(sym) || key?(sym.to_s)
172
+ if method_name.to_s[-1] == "="
173
+ setter_key = method_name.to_s.chop
174
+ return true if key?(setter_key) || key?(setter_key.to_sym)
175
+ end
176
+ false
177
+ end
178
+
169
179
  def method_missing(m, *arguments, &block)
170
180
  m = m[1..-1].to_sym if m[0] == "_"
171
181
  if key?(m)
@@ -231,6 +241,13 @@ class Hash
231
241
  NiceHash.generate(self, select_hash_key, expected_errors: expected_errors, **synonyms)
232
242
  end
233
243
 
244
+ ###########################################################################
245
+ # Generates n different hashes from the same pattern. More info: NiceHash.generate_n
246
+ ###########################################################################
247
+ def generate_n(n, select_hash_key = nil, expected_errors: [], **synonyms)
248
+ NiceHash.generate_n(self, n, select_hash_key, expected_errors: expected_errors, **synonyms)
249
+ end
250
+
234
251
  ###########################################################################
235
252
  # Validates a given values_hash_to_validate with string patterns and select fields
236
253
  # More info: NiceHash.validate
@@ -298,6 +315,28 @@ class Hash
298
315
  NiceHash.nice_filter(self, keys)
299
316
  end
300
317
 
318
+ ###########################################################################
319
+ # Compares with another hash and returns differences with dot-notation paths.
320
+ # More info: NiceHash.diff
321
+ ###########################################################################
322
+ def diff(other)
323
+ NiceHash.diff(self, other)
324
+ end
325
+
326
+ ###########################################################################
327
+ # Flattens the hash to dot-notation keys. More info: NiceHash.flatten_keys
328
+ ###########################################################################
329
+ def flatten_keys
330
+ NiceHash.flatten_keys(self)
331
+ end
332
+
333
+ ###########################################################################
334
+ # Unflattens a hash with dot-notation keys into nested hash. More info: NiceHash.unflatten_keys
335
+ ###########################################################################
336
+ def unflatten_keys
337
+ NiceHash.unflatten_keys(self)
338
+ end
339
+
301
340
  ###########################################################################
302
341
  # Merging multi-dimensional hashes
303
342
  ###########################################################################
@@ -322,6 +361,7 @@ class Hash
322
361
  end
323
362
 
324
363
  alias gen generate
364
+ alias gen_n generate_n
325
365
  alias val validate
326
366
  alias patterns pattern_fields
327
367
  alias nice_copy deep_copy
@@ -365,6 +405,11 @@ class Array
365
405
  # my_array.city
366
406
  # my_array._name
367
407
  ###########################################################################
408
+ def respond_to_missing?(method_name, include_private = false)
409
+ sym = (method_name.to_s[0] == "_" ? method_name.to_s[1..-1].to_sym : method_name)
410
+ any? { |elem| elem.is_a?(Hash) && (elem.key?(sym) || elem.key?(sym.to_s)) }
411
+ end
412
+
368
413
  def method_missing(m, *arguments, &block)
369
414
  m = m[1..-1].to_sym if m[0] == "_"
370
415
  array = []
@@ -21,6 +21,7 @@ class NiceHash
21
21
  # #>{:user=>{:address=>{:city=>"Madrid", :country=>"Spain"}, :name=>"Peter", :age=>33}, :customer=>true}
22
22
  ##################################################
23
23
  def self.deep_clone(obj)
24
+ return obj.clone unless obj.is_a?(Array) || obj.is_a?(Hash)
24
25
  obj.clone.tap do |new_obj|
25
26
  if new_obj.is_a?(Array)
26
27
  new_obj.each_with_index do |val, i|
@@ -30,8 +31,6 @@ class NiceHash
30
31
  new_obj.each do |key, val|
31
32
  new_obj[key] = deep_clone(val)
32
33
  end
33
- else
34
- new_obj = obj.clone
35
34
  end
36
35
  end
37
36
  end
@@ -27,11 +27,16 @@ class NiceHash
27
27
  if keys.size == 1
28
28
  hash.delete(nested_key.to_sym)
29
29
  else
30
- last_key = keys[-1]
31
- eval("hash.#{keys[0..(keys.size - 2)].join(".")}.delete(:#{last_key})")
30
+ parent = hash
31
+ keys[0..-2].each do |k|
32
+ sym = k.to_sym
33
+ return hash unless parent.is_a?(Hash) && parent.key?(sym)
34
+ parent = parent[sym]
35
+ end
36
+ parent.delete(keys[-1].to_sym) if parent.is_a?(Hash)
32
37
  end
33
38
  return hash
34
39
  end
35
40
 
36
-
37
- end
41
+
42
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NiceHash
4
+ ##################################################
5
+ # Compares two hashes and returns differences with dot-notation paths.
6
+ #
7
+ # @param expected [Hash, Array] Expected structure (hash or array of hashes)
8
+ # @param actual [Hash, Array] Actual structure to compare
9
+ # @param prefix [String] Internal: current path prefix for recursion
10
+ #
11
+ # @return [Hash] Path => { expected: value, got: value } for each difference
12
+ #
13
+ # @example
14
+ # expected = { user: { address: { city: "Madrid" } } }
15
+ # actual = { user: { address: { city: "London" } } }
16
+ # NiceHash.diff(expected, actual)
17
+ # #=> { "user.address.city" => { expected: "Madrid", got: "London" } }
18
+ ##################################################
19
+ def self.diff(expected, actual, prefix = "")
20
+ result = {}
21
+ exp_keys = expected.is_a?(Hash) ? expected.keys : (expected.is_a?(Array) ? (0...expected.size) : nil)
22
+ act_keys = actual.is_a?(Hash) ? actual.keys : (actual.is_a?(Array) ? (0...actual.size) : nil)
23
+
24
+ if expected.is_a?(Hash) && actual.is_a?(Hash)
25
+ all_keys = (expected.keys + actual.keys).uniq
26
+ all_keys.each do |k|
27
+ path = prefix.empty? ? k.to_s : "#{prefix}.#{k}"
28
+ exp_v = expected[k]
29
+ act_v = actual[k]
30
+ if !expected.key?(k)
31
+ result[path] = { expected: nil, got: act_v }
32
+ elsif !actual.key?(k)
33
+ result[path] = { expected: exp_v, got: nil }
34
+ elsif exp_v.is_a?(Hash) && act_v.is_a?(Hash)
35
+ result.merge!(diff(exp_v, act_v, path))
36
+ elsif exp_v.is_a?(Array) && act_v.is_a?(Array)
37
+ max_len = [exp_v.size, act_v.size].max
38
+ max_len.times do |i|
39
+ result.merge!(diff(exp_v[i], act_v[i], "#{path}[#{i}]"))
40
+ end
41
+ elsif exp_v != act_v
42
+ result[path] = { expected: exp_v, got: act_v }
43
+ end
44
+ end
45
+ elsif expected.is_a?(Array) && actual.is_a?(Array)
46
+ max_len = [expected.size, actual.size].max
47
+ max_len.times do |i|
48
+ result.merge!(diff(expected[i], actual[i], prefix.empty? ? "[#{i}]" : "#{prefix}[#{i}]"))
49
+ end
50
+ elsif expected != actual
51
+ result[prefix] = { expected: expected, got: actual } unless prefix.empty?
52
+ end
53
+ result
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NiceHash
4
+ ##################################################
5
+ # Flattens a nested hash so keys become dot-notation strings.
6
+ #
7
+ # @param hash [Hash] The hash to flatten
8
+ # @param prefix [String] Internal: current path prefix for recursion
9
+ #
10
+ # @return [Hash] Flat hash with string keys like "user.address.city"
11
+ #
12
+ # @example
13
+ # NiceHash.flatten_keys({ user: { address: { city: "Madrid" } } })
14
+ # #=> { "user.address.city" => "Madrid" }
15
+ ##################################################
16
+ def self.flatten_keys(hash, prefix = "")
17
+ return {} unless hash.is_a?(Hash)
18
+ result = {}
19
+ hash.each do |k, v|
20
+ path = prefix.empty? ? k.to_s : "#{prefix}.#{k}"
21
+ if v.is_a?(Hash)
22
+ result.merge!(flatten_keys(v, path))
23
+ else
24
+ result[path] = v
25
+ end
26
+ end
27
+ result
28
+ end
29
+
30
+ ##################################################
31
+ # Unflattens a hash with dot-notation keys into a nested hash.
32
+ #
33
+ # @param hash [Hash] Flat hash with string keys like "user.address.city"
34
+ #
35
+ # @return [Hash] Nested hash
36
+ #
37
+ # @example
38
+ # NiceHash.unflatten_keys({ "user.address.city" => "Madrid" })
39
+ # #=> { user: { address: { city: "Madrid" } } }
40
+ ##################################################
41
+ def self.unflatten_keys(hash)
42
+ return {} unless hash.is_a?(Hash)
43
+ result = {}
44
+ hash.each do |key_str, value|
45
+ keys = key_str.to_s.split(".")
46
+ keys = keys.map { |k| k.match?(/\A\d+\z/) ? k.to_i : k.to_sym }
47
+ current = result
48
+ keys[0..-2].each do |k|
49
+ current[k] = {} unless current[k].is_a?(Hash)
50
+ current = current[k]
51
+ end
52
+ current[keys[-1]] = value
53
+ end
54
+ result
55
+ end
56
+ end
@@ -58,6 +58,7 @@ class NiceHash
58
58
  if expected_errors.kind_of?(Symbol)
59
59
  expected_errors = [expected_errors]
60
60
  end
61
+ sp_opts = { expected_errors: expected_errors }.merge(synonyms)
61
62
 
62
63
  if pattern_hash.kind_of?(Hash) and pattern_hash.size > 0
63
64
  pattern_hash.each { |key, value|
@@ -70,12 +71,14 @@ class NiceHash
70
71
  if value.keys.include?(select_hash_key)
71
72
  value = value[select_hash_key]
72
73
  else
73
- value = NiceHash.generate(value, select_hash_key, expected_errors: expected_errors)
74
+ value = NiceHash.generate(value, select_hash_key, expected_errors: expected_errors, **synonyms)
74
75
  end
75
76
  end
76
- if value.kind_of?(String) or value.kind_of?(Symbol)
77
+ if value == :uuid
78
+ hashv[key] = expected_errors.empty? ? StringPattern.uuid : (StringPattern.uuid[0..-2] + "X")
79
+ elsif value.kind_of?(String) or value.kind_of?(Symbol)
77
80
  if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
78
- hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
81
+ hashv[key] = StringPattern.generate(value, **sp_opts)
79
82
  elsif ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^([\w\s\-]+\|)+[\w\s\-]+$/).size > 0
80
83
  if expected_errors.include?(:min_length) or (expected_errors.include?(:length) and rand.round == 0)
81
84
  min = value.to_s.split("|").min { |a, b| a.size <=> b.size }
@@ -94,10 +97,11 @@ class NiceHash
94
97
  v = value.to_s.split("|").sample
95
98
  end
96
99
  unless expected_errors.include?(:string_set_not_allowed)
97
- v = StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]")
100
+ v = StringPattern.generate(:"#{v.size}:[#{value.to_s.split("|").join.split(//).uniq.join}]", **sp_opts)
98
101
  hashv[key] = v unless value.to_s.split("|").include?(v)
99
102
  end
100
103
  unless hashv.keys.include?(key)
104
+ # Need a valid 1-char not in the allowed set (do not pass expected_errors)
101
105
  one_wrong_letter = StringPattern.generate(:"1:LN$[%#{value.to_s.split("|").join.split(//).uniq.join}%]")
102
106
  v[rand(v.size)] = one_wrong_letter
103
107
  hashv[key] = v unless value.to_s.split("|").include?(v)
@@ -115,15 +119,15 @@ class NiceHash
115
119
  v = value[0]
116
120
  if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
117
121
  hashv[key] = []
118
- (rand(5)+1).times do
119
- hashv[key]<<StringPattern.generate(v, expected_errors: expected_errors)
122
+ (rand(5)+1).times do
123
+ hashv[key]<<StringPattern.generate(v, **sp_opts)
120
124
  end
121
125
  array_pattern = true
122
126
  end
123
127
  else
124
128
  value.each { |v|
125
129
  if (v.kind_of?(String) or v.kind_of?(Symbol)) and StringPattern.analyze(v, silent: true).kind_of?(StringPattern::Pattern)
126
- hashv[key] = StringPattern.generate(value, expected_errors: expected_errors)
130
+ hashv[key] = StringPattern.generate(value, **sp_opts)
127
131
  array_pattern = true
128
132
  break
129
133
  end
@@ -133,9 +137,9 @@ class NiceHash
133
137
  value_ret = Array.new
134
138
  value.each { |v|
135
139
  if v.is_a?(Hash)
136
- ret = NiceHash.generate(v, select_hash_key, expected_errors: expected_errors)
140
+ ret = NiceHash.generate(v, select_hash_key, expected_errors: expected_errors, **synonyms)
137
141
  else
138
- ret = NiceHash.generate({ doit: v }, select_hash_key, expected_errors: expected_errors)
142
+ ret = NiceHash.generate({ doit: v }, select_hash_key, expected_errors: expected_errors, **synonyms)
139
143
  ret = ret[:doit] if ret.is_a?(Hash) and ret.key?(:doit)
140
144
  end
141
145
  ret = v if ret.kind_of?(Hash) and ret.size == 0
@@ -208,7 +212,7 @@ class NiceHash
208
212
  elsif value.kind_of?(Proc)
209
213
  hashv[key] = value.call
210
214
  elsif value.kind_of?(Regexp)
211
- hashv[key] = value.generate(expected_errors: expected_errors)
215
+ hashv[key] = value.generate(**sp_opts)
212
216
  else
213
217
  hashv[key] = value
214
218
  end
@@ -225,5 +229,20 @@ class NiceHash
225
229
 
226
230
  return hashv
227
231
  end
228
-
229
- end
232
+
233
+ ###########################################################################
234
+ # Generates n different hashes from the same pattern (useful for bulk or boundary tests).
235
+ # input:
236
+ # pattern_hash: (Hash) Hash we want to use to generate the values
237
+ # n: (Integer) Number of hashes to generate
238
+ # select_hash_key: (key value) (optional) The key we want to select on the subhashes
239
+ # expected_errors: (Array) (optional) (alias: errors) Same as in generate
240
+ # output: (Array of Hash)
241
+ # examples:
242
+ # hashes = NiceHash.generate_n(my_hash, 5, :correct)
243
+ # hashes = my_hash.generate_n(5, :correct)
244
+ ###########################################################################
245
+ def NiceHash.generate_n(pattern_hash, n, select_hash_key = nil, expected_errors: [], **synonyms)
246
+ n.times.map { NiceHash.generate(pattern_hash, select_hash_key, expected_errors: expected_errors, **synonyms) }
247
+ end
248
+ end
@@ -28,27 +28,19 @@ class NiceHash
28
28
  if keys.size == 1
29
29
  hash[nested_key.to_sym] = value unless only_if_exist and !hash.key?(nested_key.to_sym)
30
30
  else
31
- exist = true
32
- if only_if_exist
33
- ht = hash.deep_copy
34
- keys.each do |k|
35
- unless ht.key?(k.to_sym)
36
- exist = false
37
- break
38
- end
39
- ht = ht[k.to_sym]
40
- end
31
+ parent = hash
32
+ keys[0..-2].each do |k|
33
+ sym = k.to_sym
34
+ return hash unless parent.is_a?(Hash) && parent.key?(sym)
35
+ parent = parent[sym]
41
36
  end
42
- if !only_if_exist or (only_if_exist and exist)
43
- if value.is_a?(String)
44
- eval("hash.#{nested_key}='#{value}'")
45
- else
46
- #todo: consider other kind of objects apart of strings
47
- eval("hash.#{nested_key}=#{value}")
48
- end
37
+ return hash unless parent.is_a?(Hash)
38
+ if only_if_exist
39
+ return hash unless parent.key?(keys[-1].to_sym)
49
40
  end
41
+ parent[keys[-1].to_sym] = value
50
42
  end
51
43
  return hash
52
44
  end
53
-
54
- end
45
+
46
+ end
@@ -59,7 +59,7 @@ class NiceHash
59
59
  end
60
60
  values = values_hash_to_validate
61
61
  if pattern_hash.keys.size == get_all_keys(pattern_hash).size and values.keys.size != get_all_keys(values) and
62
- pattern_hash.keys.size == pattern_hash.keys.flatten.size # dont't set_values for the case of same_value
62
+ pattern_hash.keys.size == pattern_hash.keys.flatten.size # dont't set_values for the case of same_value
63
63
  # all patterns on patterns_hash are described on first level, so no same structure than values
64
64
  pattern_hash = values.set_values(pattern_hash)
65
65
  end
@@ -83,7 +83,9 @@ class NiceHash
83
83
  end
84
84
 
85
85
  if values.keys.include?(key)
86
- if value.kind_of?(String) or value.kind_of?(Symbol)
86
+ if value == :uuid
87
+ results[key] = false unless StringPattern.valid_uuid?(values[key].to_s)
88
+ elsif value.kind_of?(String) or value.kind_of?(Symbol)
87
89
  if ((StringPattern.optimistic and value.kind_of?(String)) or value.kind_of?(Symbol)) and value.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
88
90
  res = StringPattern.validate(pattern: value, text: values[key])
89
91
  results[key] = res if res.size > 0
@@ -133,7 +135,7 @@ class NiceHash
133
135
  results[key] << res
134
136
  end
135
137
  if results[key].flatten.size == 0
136
- results.delete(key)
138
+ results.delete(key)
137
139
  end
138
140
  else
139
141
  res = StringPattern.validate(pattern: value, text: values[key])
@@ -213,5 +215,5 @@ class NiceHash
213
215
  return results
214
216
  end
215
217
 
216
-
217
- end
218
+
219
+ end
data/lib/nice_hash.rb CHANGED
@@ -5,6 +5,8 @@ require_relative "nice/hash/add_to_ruby" if SP_ADD_TO_RUBY
5
5
  require_relative "nice/hash/change_one_by_one"
6
6
  require_relative "nice/hash/compare_structure"
7
7
  require_relative "nice/hash/delete_nested"
8
+ require_relative "nice/hash/diff"
9
+ require_relative "nice/hash/flatten_keys"
8
10
  require_relative "nice/hash/generate"
9
11
  require_relative "nice/hash/get_all_keys"
10
12
  require_relative "nice/hash/get_values"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nice_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.7
4
+ version: 1.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Ruiz
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-03-19 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: string_pattern
@@ -16,20 +15,20 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '2.3'
18
+ version: '2.4'
20
19
  - - ">="
21
20
  - !ruby/object:Gem::Version
22
- version: 2.3.0
21
+ version: 2.4.0
23
22
  type: :runtime
24
23
  prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - "~>"
28
27
  - !ruby/object:Gem::Version
29
- version: '2.3'
28
+ version: '2.4'
30
29
  - - ">="
31
30
  - !ruby/object:Gem::Version
32
- version: 2.3.0
31
+ version: 2.4.0
33
32
  - !ruby/object:Gem::Dependency
34
33
  name: rspec
35
34
  requirement: !ruby/object:Gem::Requirement
@@ -71,6 +70,8 @@ files:
71
70
  - lib/nice/hash/compare_structure.rb
72
71
  - lib/nice/hash/deep_clone.rb
73
72
  - lib/nice/hash/delete_nested.rb
73
+ - lib/nice/hash/diff.rb
74
+ - lib/nice/hash/flatten_keys.rb
74
75
  - lib/nice/hash/generate.rb
75
76
  - lib/nice/hash/get_all_keys.rb
76
77
  - lib/nice/hash/get_values.rb
@@ -95,15 +96,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
96
  requirements:
96
97
  - - ">="
97
98
  - !ruby/object:Gem::Version
98
- version: '0'
99
+ version: '3.0'
99
100
  required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - ">="
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
105
  requirements: []
105
- rubygems_version: 3.4.19
106
- signing_key:
106
+ rubygems_version: 4.0.6
107
107
  specification_version: 4
108
108
  summary: NiceHash creates hashes following certain patterns so your testing will be
109
109
  much easier. Parse and filter JSON. Perfect to be used in test data factories