Class: Ace::Lint::Molecules::FrontmatterValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/lint/molecules/frontmatter_validator.rb

Overview

Validates frontmatter schema and required fields

Constant Summary collapse

DEFAULT_REQUIRED_FIELDS =
%w[doc-type purpose].freeze

Class Method Summary collapse

Class Method Details

.lint(file_path, required_fields: DEFAULT_REQUIRED_FIELDS) ⇒ Models::LintResult

Validate frontmatter in a file

Parameters:

  • file_path (String)

    Path to file with frontmatter

  • required_fields (Array<String>) (defaults to: DEFAULT_REQUIRED_FIELDS)

    Required frontmatter fields

Returns:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ace/lint/molecules/frontmatter_validator.rb', line 23

def self.lint(file_path, required_fields: DEFAULT_REQUIRED_FIELDS)
  content = File.read(file_path)
  lint_content(file_path, content, required_fields: required_fields)
rescue Errno::ENOENT
  Models::LintResult.new(
    file_path: file_path,
    success: false,
    errors: [Models::ValidationError.new(message: "File not found: #{file_path}")]
  )
rescue => e
  Models::LintResult.new(
    file_path: file_path,
    success: false,
    errors: [Models::ValidationError.new(message: "Error reading file: #{e.message}")]
  )
end

.lint_content(file_path, content, required_fields: DEFAULT_REQUIRED_FIELDS) ⇒ Models::LintResult

Validate frontmatter in content

Parameters:

  • file_path (String)

    Path for reference

  • content (String)

    File content

  • required_fields (Array<String>) (defaults to: DEFAULT_REQUIRED_FIELDS)

    Required frontmatter fields

Returns:



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
100
101
102
103
104
105
106
107
108
109
# File 'lib/ace/lint/molecules/frontmatter_validator.rb', line 45

def self.lint_content(file_path, content, required_fields: DEFAULT_REQUIRED_FIELDS)
  errors = []

  # Extract frontmatter
  extraction = Atoms::FrontmatterExtractor.extract(content)

  unless extraction[:has_frontmatter]
    if frontmatter_free_file?(file_path)
      return Models::LintResult.new(
        file_path: file_path,
        success: true,
        errors: [],
        warnings: []
      )
    end

    error_msg = extraction[:error] || "No frontmatter found"
    return Models::LintResult.new(
      file_path: file_path,
      success: false,
      errors: [Models::ValidationError.new(line: 1, message: error_msg)]
    )
  end

  # Parse frontmatter YAML
  parse_result = Atoms::YamlValidator.parse(extraction[:frontmatter])

  unless parse_result[:success]
    parse_errors = parse_result[:errors].map do |msg|
      Models::ValidationError.new(line: 1, message: "Frontmatter YAML error: #{msg}")
    end
    return Models::LintResult.new(
      file_path: file_path,
      success: false,
      errors: parse_errors
    )
  end

  frontmatter = parse_result[:data]

  # Validate required fields
  unless frontmatter.is_a?(Hash)
    return Models::LintResult.new(
      file_path: file_path,
      success: false,
      errors: [Models::ValidationError.new(line: 1, message: "Frontmatter must be a hash/object")]
    )
  end

  required_fields.each do |field|
    next if frontmatter.key?(field)

    errors << Models::ValidationError.new(
      line: 1,
      message: "Missing required field: '#{field}'"
    )
  end

  Models::LintResult.new(
    file_path: file_path,
    success: errors.empty?,
    errors: errors,
    warnings: []
  )
end