hashly 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +5 -5
  2. data/lib/hashly/hashly.rb +70 -15
  3. metadata +3 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7130cc1a7db063eb80ad86fb00490d0cd5e37324
4
- data.tar.gz: 7ff720049720f1c1be26fe1bf1b2a8bf23b31610
2
+ SHA256:
3
+ metadata.gz: 607087c6024a8f89cabb9f5a09a7c7a5079be000daa607ee6856466f4d6ca035
4
+ data.tar.gz: 5bb0089b7071bf71552613b29abf26afa5794036673c069013d2683db750e835
5
5
  SHA512:
6
- metadata.gz: 221f421dc3ce0f67f815280d24b67207aa5b67d931df06215b897db8e4521ca7fe4887a1d7286e635ef981c7d114f2d436df1293fe4ab55c533bc1c9b4f0e066
7
- data.tar.gz: d81c99065f2a3c54c5b155efd8d9ffa05770ebfd8bf9e843dd3aac38a641132ec8a1122dbe2b131a0c96bcda66b999d8e330811f73d9fed834785cb149af89d9
6
+ metadata.gz: 56fdc2ee4b1380646c1c15609db4b348a68ba5ae363f97ca452e76b81225763b5ccbdc0d57d4bb153bf6065a9cfb2b760e565d985cd4bacc5eeaa8e19851309f
7
+ data.tar.gz: 8ad8c31293c22582e2e3daee146c820bcbe6be6d9841dd7ec156a451b51a6d6d29fa9abba694024d8e3a923d74c28257500ddb5bdeb7d266b063cc4516611d09
data/lib/hashly/hashly.rb CHANGED
@@ -1,12 +1,6 @@
1
1
  module Hashly
2
2
  module_function
3
3
 
4
- def safe_value(hash, *keys)
5
- return nil if hash.nil? || hash[keys.first].nil?
6
- return hash[keys.first] if keys.length == 1 # return the value if we have reached the final key
7
- safe_value(hash[keys.shift], *keys) # recurse until we have reached the final key
8
- end
9
-
10
4
  def stringify_all_keys(hash)
11
5
  stringified_hash = {}
12
6
  hash.each do |k, v|
@@ -54,30 +48,37 @@ module Hashly
54
48
  # Returns:
55
49
  # Hash with the merged data.
56
50
  # Parameters:
57
- # boolean_or: use a boolean || operator on the base and override if they are not a Hash or Array instead of stomping with the override
51
+ # boolean_or: use a boolean || operator on the base and override if they are not a Hash or Array instead of stomping with the override.
58
52
  # left_outer_join_depth: Only merge keys that already exist in the base for the first X levels specified.
59
- def deep_merge(base, override, boolean_or: false, left_outer_join_depth: 0)
53
+ # modify_by_reference: Hashes will be modified by reference which will modify the actual parameters.
54
+ # selected_overrides: Allows for specifying a regex or value(s) that should ONLY be merged into the base Hash.
55
+ # excluded_overrides: Allows for specifying a regex or value(s) that should NOT be merged into the base Hash.
56
+ def deep_merge(base, override, boolean_or: false, left_outer_join_depth: 0, modify_by_reference: false, selected_overrides: [], excluded_overrides: [])
60
57
  left_outer_join_depth -= 1 # decrement left_outer_join_depth for recursion
58
+ return base if reject_value?(override, excluded_overrides)
59
+ return base unless select_value?(override, selected_overrides)
61
60
  if base.nil?
62
61
  return nil if left_outer_join_depth >= 0
63
- return override.is_a?(::Hash) ? override.dup : override
62
+ return modify_by_reference || !override.is_a?(::Hash) ? override : override.dup
64
63
  end
65
64
 
66
65
  case override
67
66
  when nil
68
- base = base.dup if base.is_a?(::Hash) # duplicate hash to avoid modification by reference issues
67
+ base = base.dup unless modify_by_reference || !base.is_a?(::Hash) # duplicate hash to avoid modification by reference issues
69
68
  base # if override doesn't exist, simply return the existing value
70
69
  when ::Hash
71
- base = base.dup
70
+ return override unless base.is_a?(::Hash)
71
+ base = base.dup unless modify_by_reference || !base.is_a?(::Hash)
72
72
  override.each do |src_key, src_value|
73
73
  next if base[src_key].nil? && left_outer_join_depth >= 0 # if this is a left outer join and the key does not exist in the base, skip it
74
- base[src_key] = base[src_key] ? deep_merge(base[src_key], src_value, boolean_or: boolean_or, left_outer_join_depth: left_outer_join_depth) : src_value # Recurse if both are Hash
74
+ base[src_key] = base[src_key] ? deep_merge(base[src_key], src_value, boolean_or: boolean_or, left_outer_join_depth: left_outer_join_depth, selected_overrides: selected_overrides, excluded_overrides: excluded_overrides) : src_value # Recurse if both are Hash
75
75
  end
76
76
  base
77
77
  when ::Array
78
+ return override unless base.is_a?(::Array)
78
79
  base |= override
79
80
  base
80
- when ::String, ::Integer, ::Time, ::TrueClass, ::FalseClass
81
+ when ::String, ::Integer, ::Time, ::TrueClass, ::FalseClass, ::Symbol
81
82
  boolean_or ? base || override : override
82
83
  else
83
84
  throw "Implementation for deep merge of type #{override.class} is missing."
@@ -102,8 +103,9 @@ module Hashly
102
103
  # For a hash, returns keys found in both hashes where the values don't match.
103
104
  # If a key exists in the base, but NOT the comparison, it is NOT considered a difference so that it can be a one way comparison.
104
105
  # For an array, returns an array with values found in the comparison array but not in the base array.
105
- def deep_diff(base, comparison)
106
+ def deep_diff(base, comparison, existing_keys_only: false)
106
107
  if base.nil? # if base is nil, entire comparison object is different
108
+ return {} if comparison.nil?
107
109
  return comparison.is_a?(Hash) ? comparison.dup : comparison
108
110
  end
109
111
 
@@ -114,11 +116,13 @@ module Hashly
114
116
  differing_values = {}
115
117
  base = base.dup
116
118
  comparison.each do |src_key, src_value|
117
- difference = deep_diff(base[src_key], src_value)
119
+ next if existing_keys_only && base.is_a?(::Hash) && !base.keys.include?(src_key)
120
+ difference = deep_diff(base[src_key], src_value, existing_keys_only: existing_keys_only)
118
121
  differing_values[src_key] = difference unless difference == :no_diff
119
122
  end
120
123
  differing_values.reject { |_k, v| v.is_a?(::Hash) && v.empty? }
121
124
  when ::Array
125
+ return comparison unless base.is_a?(::Array)
122
126
  difference = comparison - base
123
127
  difference.empty? ? :no_diff : difference
124
128
  else
@@ -134,6 +138,19 @@ module Hashly
134
138
  end
135
139
  end
136
140
 
141
+ # Reject hash keys however deep they are. Provide a block and if it evaluates to true for a given key/value pair, it will be rejected.
142
+ def deep_select(hash, &block)
143
+ hash.each_with_object({}) do |(k, v), h|
144
+ if v.is_a?(::Hash)
145
+ h[k] = deep_select(v, &block)
146
+ h.delete(k) if h[k].is_a?(::Hash) && h[k].empty?
147
+ next
148
+ end
149
+ next unless yield(k, v) # skip the current key/value pair unless the block given evaluates to true
150
+ h[k] = v
151
+ end
152
+ end
153
+
137
154
  # Deep diff two Hashes
138
155
  # Remove any keys in the first hash also contained in the second hash
139
156
  # If a key exists in the base, but NOT the comparison, it is kept.
@@ -157,4 +174,42 @@ module Hashly
157
174
  def reject_keys_with_nil_values(base)
158
175
  deep_reject(base) { |_k, v| v.nil? }
159
176
  end
177
+
178
+ def safe_value(hash, *keys)
179
+ return nil if hash.nil? || hash[keys.first].nil?
180
+ return hash[keys.first] if keys.length == 1 # return the value if we have reached the final key
181
+ safe_value(hash[keys.shift], *keys) # recurse until we have reached the final key
182
+ end
183
+
184
+ def reject_value?(value, rejected_values)
185
+ return false if rejected_values == :disabled
186
+
187
+ rejected_values = [rejected_values] if rejected_values.is_a?(String) || !rejected_values.is_a?(::Array)
188
+ rejected_values.each do |rejected_value|
189
+ case rejected_value
190
+ when Regexp
191
+ next if value.is_a?(::Hash) || value.is_a?(::Array) # Don't evaluate regexp on Hash or Array
192
+ return true if (value.to_s =~ rejected_value) == 0
193
+ else
194
+ return true if value == rejected_value
195
+ end
196
+ end
197
+ false
198
+ end
199
+
200
+ def select_value?(value, selected_values)
201
+ return true if selected_values == :disabled || selected_values.nil? || selected_values.empty?
202
+
203
+ selected_values = [selected_values] if selected_values.is_a?(String) || !selected_values.is_a?(::Array)
204
+ selected_values.each do |selected_value|
205
+ case selected_value
206
+ when Regexp
207
+ next if value.is_a?(::Hash) || value.is_a?(::Array) # Don't evaluate regexp on Hash or Array
208
+ return true if (value.to_s =~ selected_value) == 0
209
+ else
210
+ return true if value == selected_value
211
+ end
212
+ end
213
+ false
214
+ end
160
215
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Munoz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-27 00:00:00.000000000 Z
11
+ date: 2021-07-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -39,8 +39,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
- rubyforge_project:
43
- rubygems_version: 2.5.2.3
42
+ rubygems_version: 3.0.3
44
43
  signing_key:
45
44
  specification_version: 4
46
45
  summary: Ruby library for ease of merging nested hashes recursively, diffing hashes,