Make front matter easier to understand, extensible, and programmatically editable #777
Replies: 3 comments 2 replies
-
This seems to be extremely well thought-out, so I hope my reply is not half-baked, but it seems to me that if we are going to programmatically write front matter, we'll want to know
(My apologies if you made either of those points and I overlooked them.) I am 100% persuaded that having the ability to write the front matter is a good thing, although the use case that I find compelling is slightly different from the ones you mention. Writing posts/articles/whatever in markdown files seems optimized—as far as I can see—only for single-person workflows. If you ever want multi-person workflows, where you add a proof-reader, an editor, an approver, a co-author, a different contributor, etc., that could be quite tricky without the ability to programmatically write front matter. It would be nice to be able to have this sort of workflow without throwing out the standard approach of markdown files with front matter. (I assume using a headless CMS allows you to have multi-person workflows only if any content handled by multiple people lives 100% in the CMS and not in your (e.g.) Obsidian vault. Someone please correct me if I'm mistaken in this.) |
Beta Was this translation helpful? Give feedback.
-
I have been digging into how we could make front matter programmatically update front matter, in particular, YAML front matter so far because that's what I currently use on my site. The news is ... not good. Psych, Ruby's standard YAML parser and the one that Bridgetown uses, is built upon libyaml. libyaml appears to have been unmaintained since mid-2020. That's not too much of a problem as long as it does what it needs to do. However, take this simple example: You have a post with the following front matter: ---
tags:
- ruby You want to programmatically add a ---
tags:
- ruby
- bridgetown This isn't possible with libyaml right now (and thus is not possible with Psych using only the AST). You can only output: ---
tags:
- ruby
- bridgetown because libyaml does not allow controlling the indentation of sequences. There is an an open issue about this problem and a pull request that may resolve the issue, but those have been open since late 2021 and early 2022, respectively. That means that for YAML, at least, we'd have to shave many yaks to accomplish the goal. What comes to mind to me is:
I'm not throwing in the towel yet, but I wanted to share my findings in case anyone else has thoughts. |
Beta Was this translation helpful? Give feedback.
-
If I understand the problem correctly, the choice of whether to add spaces before the hyphen is a personal aesthetic choice that doesn't change how the YAML is interpreted. There are other examples that might be more important to preserve, such as whether to put a collection inline instead of across multiple lines. I think the problem is not so much finding a YAML library that supports the extra indentation. It's a matter of finding a YAML library that supports targeted updating of the source YAML, which means it must preserve the exact source YAML and not merely parse it and then forget the source. For most applications the only thing that matters is the interpretation of the YAML, and the individual aesthetic choices that were made can be safely forgotten. In this case we want to preserve everything about the original YAML except the bit that we update. My guess is that this will require a new parser that preserves the literal form as well as the interpreted form of everything. |
Beta Was this translation helpful? Give feedback.
-
In the interest of furthering the Content Authoring Track goal of "[a]dding advanced interactions with file-based content structures and Git state," this proposal outlines a plan to make it possible to programmatically work with front matter.
Right now, the management of Bridgetown front matter has two key areas for improvement:
bridgetown-core
[1, 2, 3, 4] and not designed for being open to extension. This prevents adding new flavors of front matter (e.g. TOML).I will address these two pieces separately as they represent two separate, but related, flows of work.
Open to extension
The current state of front matter management was organically grown from its Jekyll lineage. It is something that has not had much attention so far other than adding Ruby front matter. This has led to patterns like the following snippet for checking whether a file is a resource or otherwise has front matter:
In order to add a new type of front matter to Bridgetown, we would have to go through and find all of the pertinent areas of
bridgetown-core
, update them individually, and ensure we keep them all in sync. In particular with theBrigetown::FrontMatterImporter
, this will lead to an increase in complexity for code that is already complex.To make front matter handling open to extension, we must:
Bridgetown::FrontMatter
module (or similar) to make the behavior discoverableReorganize the existing code
One approach we could take to this work that aligns with the core principle of "[g]row[ing] the Ruby ecosystem" would be to extract all front matter handling into a gem that is usable outside of Bridgetown. I think this is a laudable long-term goal, but one that we should wait we've proved the approach.
Another approach would be to extract front matter handling into a
bridgetown-frontmatter
gem. I think the value-add of this approach is small because front matter is core to Bridgetown and its resources. Since it is core behavior, I believe it belongs inbridgetown-core
as it does currently.The final approach, and the one that I recommend, is to use a simple
Bridgetown::FrontMatter
module to contain all front matter-related code. Specifically, I would map the following:Bridgetown::FrontmatterDefaults
→Bridgetown::FrontMatter::Defaults
Bridgetown::FrontMatter::Importer
→Bridgetown::FrontMatter::Importer
Bridgetown::Utils::RubyFrontMatterDSL
→Bridgetown::FrontMatter::RubyDSL
Bridgetown::Utils::RubyFrontMatter
→Bridgetown::FrontMatter::RubyFrontMatter
Introduce front matter loaders
Currently, the responsibility for loading front matter chiefly falls to the
Bridgetown::FrontMatterImporter#read_front_matter
method. This method extracts either YAML or Ruby front matter. By introducing a new concept, "front matter loaders," into the core domain, we can achieve the following:Bridgetown::FrontMatter::Loaders::Base
.Bridgetown::Utils.has_rbfm_headers?
andBridgetown::Utils.has_yaml_headers?
in favor ofBridgetown::FrontMatter::Loaders.front_matter?
which is closer to the usage of the headers checks.Why should we do this?
By making front matter open to extension, we can:
Programmatic updates
So far, this proposal only talks about the work that has to happen to enable the ability to programmatically update front matter. What does it mean to do that?
Currently, Bridgetown processes a resource by:
HashWithDotAccess::Hash
and setting it as the#data
attribute of the resource#content
attribute of the resourceStep (2) is the source of the friction in programmatically updating the front matter of a resource.
Take, for example, the following three examples of resources:
Example 1: Traditional YAML front matter
Example 2: Bridgetown's front matter DSL
Example 3: A straight Ruby hash of front matter
Each of these resources reduces to the same
HashWithDotAccess::Hash
once processed by the front matter system.When working with a
HashWithDotAccess::Hash
, you can easily tell what key(s) a programmatic update would add to the front matter. However, if you were to then go and attempt to serialize thatHashWithDotAccess::Hash
back into the file, there's no straightforward way to do so since the formats are so different.Why should we do this?
To "[add] advanced interactions with file-based content structures and Git state," of course! But what does that mean?
Consider two use cases:
Each of these use cases requires a programmatic update of front matter for a resource.
First steps
This section is more informal as I've only started thinking about this recently.
In order to be able to update front matter, we must have an abstraction exposed on the resource. The abstraction needs to track which type of front matter it is (e.g. YAML, Ruby, etc.). It also needs to be able to accept updates and appropriately render the update into the backing file.
Bridgetown::Resource#front_matter
could return a front matter object. It needs to be aware of the defaults from the collection and/or site. You should either be able to update the object and track whether it is dirty or it should track the updates and later commit them to the file.Beta Was this translation helpful? Give feedback.
All reactions