Module: Textus::Manifest::Schema::Validator

Defined in:
lib/textus/manifest/schema/validator.rb

Overview

Orchestrates structural validation (dry-schema Contract) then cross-field semantic checks (Semantics). Public interface unchanged: Validator.validate!(raw).

Class Method Summary collapse

Class Method Details

.format_first_error(messages) ⇒ Object

Format the first dry-schema error to match the legacy path-prefixed style: “unknown key ‘x’ at ‘$.lanes’” for extra-key errors; “manifest structure error at <path>: <msg>” for type/value errors.



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/textus/manifest/schema/validator.rb', line 28

def format_first_error(messages)
  msg = messages.first
  return "manifest structure error: unknown" unless msg

  parent = format_path(msg.path[0..-2])
  key    = msg.path.last

  if msg.text == "is not allowed"
    "unknown key '#{key}' at '#{parent}'"
  else
    "manifest structure error at #{format_path(msg.path)}: #{msg.text}"
  end
end

.format_path(parts) ⇒ Object



42
43
44
# File 'lib/textus/manifest/schema/validator.rb', line 42

def format_path(parts)
  "$" + Array(parts).map { |p| p.is_a?(Integer) ? "[#{p}]" : ".#{p}" }.join
end

.validate!(raw) ⇒ Object

Raises:



11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/textus/manifest/schema/validator.rb', line 11

def validate!(raw)
  raise BadManifest.new("manifest must be a hash") unless raw.is_a?(Hash)

  # Root unknown-key check before Contract so it fires even when lanes: is empty.
  Semantics.walk(raw, ROOT_KEYS, "$")

  result = Contract.call(raw)
  raise BadManifest.new(format_first_error(result.errors.messages)) unless result.success?

  raise BadManifest.new("manifest must declare lanes:") if Array(raw["lanes"]).empty?

  Semantics.check!(raw)
end