Module: K8s::Util

Defined in:
lib/k8s/util.rb

Overview

Miscellaneous helpers

Defined Under Namespace

Modules: HashBackport

Constant Summary collapse

PATH_TR_MAP =
{ '~' => '~0', '/' => '~1' }.freeze
PATH_REGEX =
%r{(/|~(?!1))}.freeze

Class Method Summary collapse

Class Method Details

.compact_map(args) {|args| ... } ⇒ Array<nil, Object>

Yield with all non-nil args, returning matching array with corresponding return values or nils.

Args must be usable as hash keys. Duplicate args will all map to the same return value.

Parameters:

  • args (Array<nil, Object>)

Yields:

  • args

Yield Parameters:

  • args (Array<Object>)

    omitting any nil values

Returns:

  • (Array<nil, Object>)

    matching args array 1:1, containing yielded values for non-nil args



108
109
110
111
112
113
114
115
116
117
# File 'lib/k8s/util.rb', line 108

def self.compact_map(args)
  func_args = args.compact

  values = yield func_args

  # Hash{arg => value}
  value_map = Hash[func_args.zip(values)]

  args.map{ |arg| value_map[arg] }
end

.deep_merge(input, other, overwrite_arrays: true, union_arrays: false, keep_existing: false, merge_nil_values: false, merge_non_hash: false) ⇒ Object

Deep merge hashes

Parameters:

  • input (Hash)
  • other (Hash)
  • overwrite_arrays (Boolean) (defaults to: true)

    when encountering an array, replace the array with the new array

  • union_arrays (Boolean) (defaults to: false)

    when encountering an array, use Array#union to combine with the existing array

  • keep_existing (Boolean) (defaults to: false)

    prefer old value over new value

  • merge_nil_values (Boolean) (defaults to: false)

    overwrite an existing value with a nil value

  • merge_non_hash (Boolean) (defaults to: false)

    calls .merge on objects that respond to .merge

Raises:

  • (ArgumentError)


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
# File 'lib/k8s/util.rb', line 25

def self.deep_merge(input, other, overwrite_arrays: true, union_arrays: false, keep_existing: false, merge_nil_values: false, merge_non_hash: false)
  raise ArgumentError, "input expected to be Hash, was #{input.class.name}" unless input.is_a?(Hash)
  raise ArgumentError, "other expected to be Hash, was #{other.class.name}" unless other.is_a?(Hash)

  input.merge(other) do |key, old_value, new_value|
    case old_value
    when Hash
      raise "#{key} : #{new_value.class.name} can not be merged into a Hash" unless new_value.is_a?(Hash)

      deep_merge(
        old_value,
        new_value,
        overwrite_arrays: overwrite_arrays,
        union_arrays: union_arrays,
        keep_existing: keep_existing,
        merge_nil_values: merge_nil_values,
        merge_non_hash: merge_non_hash
      )
    when Array
      if overwrite_arrays
        new_value
      elsif union_arrays
        raise "#{key} : #{new_value.class.name} can not be merged into an Array" unless new_value.is_a?(Array)

        old_value | new_value
      else
        old_value + new_value
      end
    else
      if keep_existing
        old_value
      elsif new_value.nil? && merge_nil_values
        nil
      elsif merge_non_hash && old_value.respond_to?(:merge)
        old_value.merge(new_value)
      else
        new_value.nil? ? old_value : new_value
      end
    end
  end
end

.deep_transform_keys(value = nil, transform_method = nil, &block) ⇒ Object

Deep transform hash keys or hashes inside arrays

Examples:

Stringify hash keys using a symbol

deep_transform_keys(hash, :to_s)

Stringify hash keys using a block

deep_transform_keys(hash) { |key| key.to_s.upcase }

Parameters:

  • value (Hash, Array, Object) (defaults to: nil)
  • transform_method (Symbol) (defaults to: nil)

    for example :to_s



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/k8s/util.rb', line 80

def self.deep_transform_keys(value = nil, transform_method = nil, &block)
  case value
  when Array
    value.map { |v| deep_transform_keys(v, transform_method, &block) }
  when Hash
    {}.tap do |result|
      value.each do |key, value|
        new_key = if key.is_a?(String) || key.is_a?(Symbol)
                    transform_method ? key.send(transform_method) : block.call(key)
                  else
                    key
                  end
        result[new_key] = deep_transform_keys(value, transform_method, &block)
      end
    end
  else
    value
  end
end

.json_patch(patch_to, patch_from) ⇒ Object

Produces a set of json-patch operations so that applying the operations on a, gives you the results of b Used in correctly patching the Kube resources on stack updates

Parameters:

  • patch_to (Hash)

    Hash to compute patches against

  • patch_from (Hash)

    New Hash to compute patches “from”



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
# File 'lib/k8s/util.rb', line 139

def self.json_patch(patch_to, patch_from)
  diffs = Hashdiff.diff(patch_to, patch_from, array_path: true)
  ops = []
  # Each diff is like:
  # ["+", ["spec", "selector", "food"], "kebab"]
  # ["-", ["spec", "selector", "drink"], "pepsi"]
  # or
  # ["~", ["spec", "selector", "drink"], "old value", "new value"]
  # the path elements can be symbols too, depending on the input hashes
  diffs.each do |diff|
    operator = diff[0]
    # substitute '/' with '~1' and '~' with '~0'
    # according to RFC 6901
    path = diff[1].map { |p| p.to_s.gsub(PATH_REGEX, PATH_TR_MAP) }
    if operator == '-'
      ops << {
        op: "remove",
        path: "/" + path.join('/')
      }
    elsif operator == '+'
      ops << {
        op: "add",
        path: "/" + path.join('/'),
        value: diff[2]
      }
    elsif operator == '~'
      ops << {
        op: "replace",
        path: "/" + path.join('/'),
        value: diff[3]
      }
    else
      raise "Unknown diff operator: #{operator}!"
    end
  end

  ops
end

.recursive_compact(hash_or_array) ⇒ Hash, Array

Recursive compact for Hash/Array

Parameters:

  • hash_or_array (Hash, Array)

Returns:

  • (Hash, Array)


123
124
125
126
127
128
129
130
131
# File 'lib/k8s/util.rb', line 123

def self.recursive_compact(hash_or_array)
  p = proc do |*args|
    v = args.last
    v.delete_if(&p) if v.respond_to?(:delete_if) && !v.is_a?(Array)
    v.nil? || v.respond_to?(:empty?) && (v.empty? && (v.is_a?(Hash) || v.is_a?(Array)))
  end

  hash_or_array.delete_if(&p)
end