Module: GraphWeaver::InputStruct::ClassMethods

Includes:
Kernel
Defined in:
lib/graph_weaver/input_struct.rb

Instance Method Summary collapse

Instance Method Details

#coerce(value) ⇒ Object

Build from a plain hash (underscored keys, Symbol or String): enums accept their wire values, nested inputs accept hashes; the struct's types are enforced on construction, and unknown keys raise with a spellchecked hint.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/graph_weaver/input_struct.rb', line 46

def coerce(value)
  return value if value.is_a?(self)

  # a caller passing a non-Hash (a bare string, or a Hash where a nested
  # list was expected) is bad input — surface a branded 422, not a raw
  # NoMethodError from validate_keys!'s `.keys`
  unless value.is_a?(Hash)
    raise GraphWeaver::InputError.new("expected a Hash or #{self}, got #{value.class}", struct: self)
  end

  # a typo'd key must not silently drop off the wire
  GraphWeaver::Hints.validate_keys!(self, value)

  fields = T.unsafe(self).const_get(:FIELDS)
  T.unsafe(self).new(**fields.to_h do |field|
    raw = value.key?(field.prop) ? value[field.prop] : value[field.prop.to_s]
    [field.prop, raw.nil? || field.coercer.nil? ? raw : field.coercer.call(raw)]
  end)
rescue GraphWeaver::InputError
  raise # already contextualized by a nested input / enum coercion
rescue ::TypeError, ::ArgumentError, KeyError => e
  # a wrong-typed field, a missing required field, or an out-of-range
  # enum — surface one branded, structured error for a 422. (`::` so the
  # rescue catches Ruby's TypeError, not GraphWeaver::TypeError.)
  raise GraphWeaver::InputError.new("invalid input for #{self}: #{e.message}", struct: self)
end