Class: Ace::Support::Markdown::Atoms::DocumentValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/support/markdown/atoms/document_validator.rb

Overview

Pure function to validate markdown documents and frontmatter v0.1.0: Uses hardcoded validation rules v0.2.0: Will support JSON Schema validation (see task 080)

Class Method Summary collapse

Class Method Details

.can_round_trip?(content) ⇒ Boolean

Validate that content can be round-trip parsed

Parameters:

  • content (String)

    The markdown content

Returns:

  • (Boolean)

    true if content can be safely parsed and rebuilt



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/ace/support/markdown/atoms/document_validator.rb', line 96

def self.can_round_trip?(content)
  return false if content.nil? || content.empty?

  begin
    # Extract frontmatter
    result = FrontmatterExtractor.extract(content)
    return false unless result[:valid]

    # Serialize back
    serialized = FrontmatterSerializer.serialize(
      result[:frontmatter],
      body: result[:body]
    )

    serialized[:valid]
  rescue
    false
  end
end

.invalid_result(errors) ⇒ Object



118
119
120
121
122
123
124
# File 'lib/ace/support/markdown/atoms/document_validator.rb', line 118

def self.invalid_result(errors)
  {
    valid: false,
    errors: errors,
    warnings: []
  }
end

.validate(content, rules: {}) ⇒ Hash

Validate complete markdown content (frontmatter + body)

Parameters:

  • content (String)

    The complete markdown content

  • rules (Hash) (defaults to: {})

    Optional validation rules

Returns:

  • (Hash)

    Result with :valid (Boolean), :errors (Array), :warnings (Array)



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ace/support/markdown/atoms/document_validator.rb', line 15

def self.validate(content, rules: {})
  return invalid_result(["Empty content"]) if content.nil? || content.empty?

  errors = []
  warnings = []

  # Parse frontmatter
  extractor_result = FrontmatterExtractor.extract(content)

  unless extractor_result[:valid]
    errors.concat(extractor_result[:errors])
    return {valid: false, errors: errors, warnings: warnings}
  end

  # Validate frontmatter structure
  fm_errors = validate_frontmatter(extractor_result[:frontmatter], rules)
  errors.concat(fm_errors)

  # Validate body exists
  if extractor_result[:body].nil? || extractor_result[:body].strip.empty?
    warnings << "Empty body content"
  end

  {
    valid: errors.empty?,
    errors: errors,
    warnings: warnings
  }
end

.validate_frontmatter(frontmatter, rules = {}) ⇒ Hash

Validate only frontmatter hash

Parameters:

  • frontmatter (Hash)

    The frontmatter data

  • rules (Hash) (defaults to: {})

    Optional validation rules

Returns:

  • (Hash)

    Result with :valid (Boolean), :errors (Array)



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
# File 'lib/ace/support/markdown/atoms/document_validator.rb', line 49

def self.validate_frontmatter(frontmatter, rules = {})
  errors = []

  unless frontmatter.is_a?(Hash)
    errors << "Frontmatter must be a hash"
    return errors
  end

  # Apply required fields validation
  if rules[:required_fields]
    rules[:required_fields].each do |field|
      unless frontmatter.key?(field) || frontmatter.key?(field.to_s)
        errors << "Missing required field: #{field}"
      end
    end
  end

  # Apply field type validation
  if rules[:field_types]
    rules[:field_types].each do |field, expected_type|
      value = frontmatter[field] || frontmatter[field.to_s]
      next unless value

      unless value.is_a?(expected_type)
        errors << "Field '#{field}' must be #{expected_type}, got #{value.class}"
      end
    end
  end

  # Apply enum validation
  if rules[:enums]
    rules[:enums].each do |field, allowed_values|
      value = frontmatter[field] || frontmatter[field.to_s]
      next unless value

      unless allowed_values.include?(value)
        errors << "Field '#{field}' must be one of #{allowed_values.join(", ")}, got '#{value}'"
      end
    end
  end

  errors
end