Class: Ace::Support::Markdown::Atoms::FrontmatterExtractor

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

Overview

Pure function to extract YAML frontmatter from markdown content Separates frontmatter and body content for safe editing

Class Method Summary collapse

Class Method Details

.body_only(content) ⇒ String

Extract only the body content without frontmatter

Parameters:

  • content (String)

    The markdown content

Returns:

  • (String)

    The body content



90
91
92
93
# File 'lib/ace/support/markdown/atoms/frontmatter_extractor.rb', line 90

def self.body_only(content)
  result = extract(content)
  result[:body]
end

.empty_resultObject



105
106
107
108
109
110
111
112
# File 'lib/ace/support/markdown/atoms/frontmatter_extractor.rb', line 105

def self.empty_result
  {
    frontmatter: {},
    body: "",
    valid: false,
    errors: ["Empty content"]
  }
end

.extract(content) ⇒ Hash

Extract frontmatter from markdown content

Parameters:

  • content (String)

    The markdown content with optional frontmatter

Returns:

  • (Hash)

    Result with :frontmatter (Hash), :body (String), :valid (Boolean), :errors (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
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
# File 'lib/ace/support/markdown/atoms/frontmatter_extractor.rb', line 15

def self.extract(content)
  return empty_result if content.nil? || content.empty?

  # Check if content starts with YAML frontmatter delimiter
  unless content.start_with?("---\n")
    return {
      frontmatter: {},
      body: content,
      valid: false,
      errors: ["No frontmatter found"]
    }
  end

  # Find the ending delimiter (searching from position 4 to skip the opening ---)
  end_index = content.index("\n---\n", 4)

  unless end_index
    return {
      frontmatter: {},
      body: content[4..-1] || "",
      valid: false,
      errors: ["Missing closing '---' delimiter for frontmatter"]
    }
  end

  # Extract YAML content (between delimiters) and body content (after delimiters)
  yaml_content = content[4...end_index]
  body_content = content[(end_index + 5)..-1] || ""

  # Parse YAML with safe_load
  begin
    frontmatter = YAML.safe_load(
      yaml_content,
      permitted_classes: [Date, Time, Symbol],
      permitted_symbols: [],
      aliases: true
    ) || {}

    # Ensure frontmatter is a hash
    unless frontmatter.is_a?(Hash)
      return {
        frontmatter: {},
        body: body_content,
        valid: false,
        errors: ["Frontmatter must be a hash/object, got #{frontmatter.class}"]
      }
    end

    {
      frontmatter: frontmatter,
      body: body_content,
      valid: true,
      errors: []
    }
  rescue Psych::SyntaxError => e
    {
      frontmatter: {},
      body: body_content,
      valid: false,
      errors: ["YAML syntax error: #{e.message}"]
    }
  end
end

.frontmatter_only(content) ⇒ Hash

Extract only the frontmatter hash

Parameters:

  • content (String)

    The markdown content

Returns:

  • (Hash)

    The frontmatter data or empty hash



82
83
84
85
# File 'lib/ace/support/markdown/atoms/frontmatter_extractor.rb', line 82

def self.frontmatter_only(content)
  result = extract(content)
  result[:frontmatter]
end

.has_frontmatter?(content) ⇒ Boolean

Check if content has valid frontmatter

Parameters:

  • content (String)

    The markdown content

Returns:

  • (Boolean)

    true if valid frontmatter exists



98
99
100
101
# File 'lib/ace/support/markdown/atoms/frontmatter_extractor.rb', line 98

def self.has_frontmatter?(content)
  result = extract(content)
  result[:valid]
end