Module: Smplkit::Config::Discovery

Defined in:
lib/smplkit/config/client.rb

Overview

Module-level helpers for the runtime config client. Extracted so they can be unit-tested without spinning up the full client.

Class Method Summary collapse

Class Method Details

.apply_change_to_target(target, dotted_key, value) ⇒ Object

Apply a server-pushed value to a bound target in place. Walks the dotted key path to the leaf’s parent and assigns the value via Hash#[]= or Struct#[]=. Bails silently if any intermediate is missing or not a supported container — the server may have items that don’t line up with what the bound target declared.



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/smplkit/config/client.rb', line 67

def apply_change_to_target(target, dotted_key, value)
  parts = dotted_key.split(".")
  current = walk_to_leaf_parent(target, parts[0..-2])
  return if current.nil?

  last = parts.last
  if current.is_a?(Struct)
    assign_struct_member(current, last, value)
  elsif current.is_a?(Hash)
    current[last] = value
  end
end

.assign_struct_member(struct, name, value) ⇒ Object



100
101
102
103
104
105
# File 'lib/smplkit/config/client.rb', line 100

def assign_struct_member(struct, name, value)
  sym = name.to_sym
  return unless struct.members.include?(sym)

  struct[sym] = value
end

.iter_hash_items(hash, prefix: "") ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/smplkit/config/client.rb', line 35

def iter_hash_items(hash, prefix: "")
  out = []
  hash.each do |raw_key, value|
    flat_key = "#{prefix}#{raw_key}"
    if value.is_a?(Hash) || value.is_a?(Struct)
      out.concat(iter_items(value, prefix: "#{flat_key}."))
    else
      out << [flat_key, value_to_item_type(value), value, nil]
    end
  end
  out
end

.iter_items(target, prefix: "") ⇒ Object

Walk a bound target, returning [key, type, value, description] tuples flattened to dot-notation. Nested Hashes / Structs are descended into; everything else is treated as an opaque leaf.



25
26
27
28
29
30
31
32
33
# File 'lib/smplkit/config/client.rb', line 25

def iter_items(target, prefix: "")
  if target.is_a?(Hash)
    iter_hash_items(target, prefix: prefix)
  elsif target.is_a?(Struct)
    iter_struct_items(target, prefix: prefix)
  else
    []
  end
end

.iter_struct_items(struct, prefix: "") ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/smplkit/config/client.rb', line 48

def iter_struct_items(struct, prefix: "")
  out = []
  struct.members.each do |member|
    value = struct[member]
    flat_key = "#{prefix}#{member}"
    if value.is_a?(Hash) || value.is_a?(Struct)
      out.concat(iter_items(value, prefix: "#{flat_key}."))
    else
      out << [flat_key, value_to_item_type(value), value, nil]
    end
  end
  out
end

.value_to_item_type(value) ⇒ Object

Map a runtime value to a Config item type. Used both when binding a Hash/Struct target and when supplying a default to get(id, key, default). true/false are checked first because Ruby’s Numeric/Integer tests would not accidentally claim them.



14
15
16
17
18
19
20
# File 'lib/smplkit/config/client.rb', line 14

def value_to_item_type(value)
  case value
  when true, false then "BOOLEAN"
  when Numeric then "NUMBER"
  else "STRING"
  end
end

.walk_to_leaf_parent(target, parts) ⇒ Object



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

def walk_to_leaf_parent(target, parts)
  current = target
  parts.each do |part|
    case current
    when Struct
      sym = part.to_sym
      return nil unless current.members.include?(sym)

      current = current[sym]
    when Hash
      return nil unless current.key?(part)

      current = current[part]
    else
      return nil
    end
  end
  current
end