Files
volumen/lib/volumen/frontmatter.rb
T

69 lines
1.8 KiB
Ruby

# frozen_string_literal: true
require "toml-rb"
module Volumen
# Parses and serialises the TOML frontmatter block of a Markdown file.
#
# A post file starts with a `+++` line, contains a TOML document, is closed
# by another `+++` line, and is followed by the Markdown body:
#
# +++
# title = "Hello"
# +++
#
# Body in **Markdown**.
module Frontmatter
DELIMITER = "+++"
# Returns `[metadata_hash, body_string]`. If the content has no
# frontmatter, the metadata hash is empty and the body is the full input.
def self.parse(content)
text = normalize(content)
lines = text.lines
return [{}, text] unless delimiter?(lines.first)
closing = closing_index(lines)
return [{}, text] if closing.nil?
toml = lines[1...closing].join
body = lines[(closing + 1)..]&.join.to_s
metadata = toml.strip.empty? ? {} : TomlRB.parse(toml)
[metadata, body.sub(/\A\r?\n/, "")]
end
# Serialises metadata + body back into a single post file string.
def self.dump(metadata, body)
toml = serialize(metadata)
buffer = "#{DELIMITER}\n"
buffer << toml
buffer << "\n" unless toml.empty? || toml.end_with?("\n")
buffer << "#{DELIMITER}\n\n"
buffer << body.to_s.sub(/\A\s+/, "")
buffer.end_with?("\n") ? buffer : "#{buffer}\n"
end
def self.normalize(content)
content.to_s.gsub("\r\n", "\n")
end
def self.closing_index(lines)
index = lines[1..].index { |line| delimiter?(line) }
index.nil? ? nil : index + 1
end
def self.delimiter?(line)
line&.chomp&.strip == DELIMITER
end
def self.serialize(metadata)
data = metadata || {}
return "" if data.empty?
TomlRB.dump(data)
end
private_class_method :normalize, :closing_index, :delimiter?, :serialize
end
end