Mike Slinn
Mike Slinn

Plugins Are Not The Only Way To Maintain a Jekyll Website

Published 2022-03-26.
Time to read: 1 minutes.

This page is part of the jekyll collection, categorized under Jekyll, Ruby.

I wanted to put up a page that showed blog posts, ordered by most recently modified. After working on the problem for a bit, I realized that if every page had a last_modified_at entry in front matter the problem would be much easier. So I wrote a Ruby program, not a Jekyll plugin, and processed each post in less than two shakes of a lamb’s tail.

#!/usr/bin/env ruby

# Add date: and last_modified_at: fields to front matter for posts that do not have them.

# @return number of lines between the --- lines, which demark front matter
def front_matter_length(contents)
  return nil unless contents[0].start_with? "---"

  contents[1..].find_index { |line| line.start_with? "---" }
end

# @return date from front matter, or the filename if front matter entry is not present
def read_date(front_matter)
  date_line = front_matter
                .find { |line| line.start_with? "date:" }
  return nil unless date_line

  date_line.delete_prefix("date:")
             .strip
end

def process(directory)
  Dir[directory].each do |file_name|
    contents = File.readlines(file_name, chomp: true)
    last_front = front_matter_length contents
    next unless last_front

    front_matter = contents[1..last_front]
    date = read_date front_matter
    unless date
      date = File.basename(file_name)[0..9]
      contents.insert(last_front + 1, "date: #{date}") # insert before 2nd ---
      last_front += 1
      must_save = true
    end

    modified_at = front_matter.find { |line| line.start_with? "last_modified_at:" }
    unless modified_at
      contents.insert(last_front + 1, "last_modified_at: #{date}") # insert before 2nd ---
      must_save = true
    end

    File.write(file_name, contents.join("\n")) if must_save
  end
end

[
  "collections/_posts/**/*.html",
  "collections/_drafts/**/*.html",
].each { |dir| process dir }

If you need to do something similar, you could modify the last bit of the program, and type in the paths to your Jekyll website posts.

[
  "collections/_posts/**/*.html",
  "collections/_drafts/**/*.html",
].each { |dir| process dir }

BTW, I did not convert front matter to YAML because I did not want to change it in any way. Transforming it from text to YAML, and then transforming it back to text again almost certainly would alter the text. I did not want to disturb what was there before, so I just considered front matter as text, and it all went very nicely.