Class: Pangea::Resources::ResourceInput
- Defined in:
- lib/pangea/resources/resource_input.rb
Overview
Partitions resource attributes into validated literals and opaque references.
Types stay pure — they model the domain (CIDRs, ports, arrays). ResourceInput handles the serialization concern: some values are known at synthesis time (literals), some are opaque (Terraform $… refs).
Equivalent to Rust’s serde boundary: the type is strict, the serialization layer handles the wire format.
Equivalent to substrate’s convergence typestate: data is tagged with its partition at the boundary, inner types are uncontaminated.
Usage (internal to ResourceBuilder — not called directly):
input = ResourceInput.partition(VpcAttributes, { cidr: "10.0.0.0/16", id: "${aws_vpc.x.id}" })
input[:cidr] # => "10.0.0.0/16" (validated by Dry::Struct)
input[:id] # => "${aws_vpc.x.id}" (opaque, passed through)
input.to_h # => { cidr: "10.0.0.0/16", id: "${aws_vpc.x.id}" }
Constant Summary collapse
- REF_PATTERN =
Terraform interpolation reference — opaque string resolved at plan time. Strict: must match $… syntax exactly. Random strings rejected.
/\A\$\{.+\}\z/.freeze
Instance Attribute Summary collapse
-
#refs ⇒ Object
readonly
Returns the value of attribute refs.
-
#validated ⇒ Object
readonly
Returns the value of attribute validated.
Class Method Summary collapse
-
.partition(attributes_class, raw_hash) ⇒ ResourceInput
Partition a raw attribute hash into validated literals and opaque refs.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Access an attribute value.
-
#initialize(validated, refs) ⇒ ResourceInput
constructor
A new instance of ResourceInput.
-
#method_missing(name, *args) ⇒ Object
Template-author DSL: ‘input.priority` resolves like `input`.
- #respond_to_missing?(name, include_private = false) ⇒ Boolean
-
#to_h ⇒ Hash
Merge validated attrs and refs into a single hash for Terraform JSON.
Constructor Details
#initialize(validated, refs) ⇒ ResourceInput
Returns a new instance of ResourceInput.
103 104 105 106 107 |
# File 'lib/pangea/resources/resource_input.rb', line 103 def initialize(validated, refs) @validated = validated @refs = refs freeze end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args) ⇒ Object
Template-author DSL: ‘input.priority` resolves like `input`. Refs win over validated literals (same as `[]`). Unknown attribute names fall through to `super` → NoMethodError with full message.
130 131 132 133 134 135 136 |
# File 'lib/pangea/resources/resource_input.rb', line 130 def method_missing(name, *args) if args.empty? && attribute?(name) self[name] else super end end |
Instance Attribute Details
#refs ⇒ Object (readonly)
Returns the value of attribute refs.
28 29 30 |
# File 'lib/pangea/resources/resource_input.rb', line 28 def refs @refs end |
#validated ⇒ Object (readonly)
Returns the value of attribute validated.
28 29 30 |
# File 'lib/pangea/resources/resource_input.rb', line 28 def validated @validated end |
Class Method Details
.partition(attributes_class, raw_hash) ⇒ ResourceInput
Partition a raw attribute hash into validated literals and opaque refs.
Literals are validated strictly by Dry::Struct. Refs are frozen and passed through. Required attributes that carry refs are excluded from validation (they can’t be validated at synthesis time — Terraform resolves them at plan time).
We use Dry::Struct.load instead of .new for the literals hash because .load bypasses the missing-key check — ref-carrying required fields are intentionally absent from the literals hash.
44 45 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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/pangea/resources/resource_input.rb', line 44 def self.partition(attributes_class, raw_hash) literals = {} refs = {} raw_hash.each do |k, v| sym = k.to_sym if v.is_a?(String) && v.match?(REF_PATTERN) refs[sym] = v else literals[sym] = v end end # Validate that every required attribute is accounted for # (present in either literals or refs, not missing from both). # Keys with a `.default(...)` type are NOT user-required — Dry::Struct # fills them in on `.load` when omitted. `Schema::Key#required?` # reports `true` for every attribute regardless of default, so we # additionally filter by `k.type.default?`. required_keys = attributes_class.schema .select { |k| k.required? && !k.type.default? } .map(&:name) .to_set provided_keys = literals.keys.to_set | refs.keys.to_set missing = required_keys - provided_keys unless missing.empty? raise ArgumentError, "#{attributes_class}: missing required attributes #{missing.to_a.inspect}. " \ "Provide literal values or Terraform references for all required fields." end # Validate each literal value against its declared type. # We can't use .new (raises on missing required keys that are refs) # and can't use .load (skips ALL validation). # Instead: validate each field individually, then load the validated hash. schema_keys = attributes_class.schema.each_with_object({}) do |k, h| h[k.name] = k.type end literals.each do |key, value| type = schema_keys[key] next unless type # unknown keys already caught by ResourceBuilder begin type.call(value) rescue Dry::Types::ConstraintError, Dry::Types::CoercionError => e raise e.class, "#{attributes_class}: attribute :#{key} — #{e.}" end end # .load bypasses missing-key enforcement (refs are intentionally absent) # but we've validated every literal value above. validated = attributes_class.load(literals) new(validated, refs.freeze) end |
Instance Method Details
#[](key) ⇒ Object
Access an attribute value. Refs take priority over validated literals. This is the serialization merge: opaque values override typed values.
114 115 116 117 |
# File 'lib/pangea/resources/resource_input.rb', line 114 def [](key) k = key.to_sym refs.fetch(k) { validated[k] } end |
#respond_to_missing?(name, include_private = false) ⇒ Boolean
138 139 140 |
# File 'lib/pangea/resources/resource_input.rb', line 138 def respond_to_missing?(name, include_private = false) attribute?(name) || super end |
#to_h ⇒ Hash
Merge validated attrs and refs into a single hash for Terraform JSON. Refs override validated values (they are the source of truth at plan time).
123 124 125 |
# File 'lib/pangea/resources/resource_input.rb', line 123 def to_h validated.to_h.merge(refs) end |