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.
- checksums.yaml +5 -5
- data/lib/hashly/hashly.rb +70 -15
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 607087c6024a8f89cabb9f5a09a7c7a5079be000daa607ee6856466f4d6ca035
|
4
|
+
data.tar.gz: 5bb0089b7071bf71552613b29abf26afa5794036673c069013d2683db750e835
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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.
|
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:
|
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
|
-
|
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,
|