💾 Archived View for d.moonfire.us › blog › 2021 › 07 › 10 › gallium-nitride-and-gemini captured on 2024-08-18 at 19:24:07. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-04-26)
-=-=-=-=-=-=-
Last November[1], I switched my static site generator from CobblestoneJS (my homebrew Gulp-based one) to Statiq[2]. There were a number of reasons, all of them still good but mainly to support what I want to do with fedran.com[3] and my other sites.
1: /blog/2020/11/20/website-update/
This holiday weekend, I ended up ripping out all of the Statiq and wrote another static site generator, this time in C# and using an Entity-Component-System[4] (ECS). I also migrated my site over to Gemini[5] as part of the effort.
4: https://en.m.wikipedia.org/wiki/Entity_component_system
I liked many of the ideas that Statiq provided:
However, I found myself struggling with concepts. It just didn't sit well with me and I would spend two weeks trying to implement a feature and getting stuck. When I realized I had spent a month not fixing something that was bothering me because I didn't want to delve into the code, I knew it was time to change.
That isn't to say Statiq isn't bad. It just isn't for me. That's it.
About once a year, I get 4-7 days of “alone time” to do what I want. This year, I decided to work on a new static site generator that did work the way I work today and that I hoped would carry me over for the next five years or so.
One thing that Statiq did (but differently) was implemented the system as an ECS. Basically, you have a lightweight object (the “entity”) and add various components into it. Those components are what provide the features: the text content, flags to say if it is HTML or Markdown, or the path.
While Statiq had a number of these elements built-into the `Document` call (basically their entity), there were a lot of assumptions that didn't always fit. Likewise, `Statiq.Web` had some nice opinionated ways of handling it, including a document type (binary, text, etc), but I couldn't find an easy way to extend it.
With my ECS, `Entity` is only a collection plus an integer identifier. Components can be added, removed, and replaced easily using generics to determine the type. Methods are chained together but not pseudo-English fluent (which I'm also not fond of). Entities are immutable, so all the operations return a clone of that entity with the new components added, removed, or otherwise changed. (Thanks to functional programming for some of those ideas.)
Entity entity = new(); Entity entityWithTwoComponents = entity .Set(new Uri("https://d.moonfire.us")) .Set<FileSystemInfo>(new FileInfo("/bob.txt")); Entity entityWithReplacedUri = entityWithTwoComponents .Set(new Uri("http://path-to-replace-d.moonfire.us/")); Entity entityWithOnlyUri = entityWithReplacedUri .Remove<FileSystemInfo>();
I also really like chained operations, so most of the processing looks like this:
IEnumerable<Entity> input; var onlyHtmlOutputs = input .OrderBy(x => x.Get<FileInfo>().FullPath) .ForComponents<Uri>((entity, uri) => this.Uri(entity, uri)) .WhereComponents<IsHtml>();
The whole idea is `ForComponents<T1, T2, T3>` will go through the list and for all entities that have `T1`, `T2`, and `T3`, it will do the callback, otherwise it will passs it on. Likewise `WhereComponents<T1>` is basically a `Where` that says only the entities with the given components.
Those ideas really simplified a lot of the difficulties I had with CobblestoneJS. Overall, most of the logic “felt” right for me, so I'm really happy with the results. Plus, it is based on a far more stable package ecosystem (NuGet) and in a language I enjoy greatly (C#).
Also, the language uses Autofac[6] as my preferred dependency injection of choice. I really like the library plus NodaTime[7] when coding, so I went with these. It's a bit opinionated, but… only a few people ever used Cobblestone, so I'm going to assume very few are going to use this.
Once I get it cleaned up, I'll probably call the ECS “Gallium” because my original name was “Gallium Nitride” (GaN, because it's a cool name and I like what the molecule does). The static site generator would be named Nitride.
Nitride is just a multi-threaded pipeline static generator. It uses pipelines much like Statiq but based on C#'s thread control (`ManualReset`, `ReaderWriterLockSlim`).
The rough code looks like this:
List<Entity> list = entities .Run(new IdentifyMarkdown()) .Run(new ParseYamlHeader<PageModel>()) .ForComponents<PageModel>(this.SetHandlebarsFromPageModel) .Run(this.setInstantFromComponent) .Run(this.filterOutFutureInstants) .Run(this.createCategoryIndexes) .Run(this.createTagIndexes) .ToList();
Again, DI or direct instantiate of modules, it all works the same and really ties into using Linq and C# generics. All of the pipelines are `async` but most of the operations (`createTagIndexes`, `ParseYamlHeader`) are not. But since the pipelines are, it is easy to make something `await` without changing signatures.
I really like the pipelines. For my site, I have the following:
That's it, but I'm happy with the result because I've taken lessons learned from my previous attempts to created something that will handle Fedran's massive cross-linking and project pages, MfGames's pulling in of separate Git repositories, and also some of the more complex formatting of my new sci-fi fiction website.
I like the idea of Gemini[8]. It is a low-overhead protocol that has almost no extra features, no cookies, and basically focused on presenting content. In my case, I really want to see all of my sites on Gemini because I think it has some significant merits, more so as I want to get away from heavily styled content written by people who like tiny fonts or don't have my color contrast issues.
8: //gemini.circumlunar.space/
To do that, I ended up taking inspiration from md2gemini[9] and wrote a C# library that converts Markdown into Gemtext (Gemini's markup format inspired by Markdown). The end result is pretty nice[10], I think and I'm really happy with the results.
9: https://pypi.org/project/md2gemini/
Of course, it meant I had to get a virtual machine to host a Gemini server next to a HTTP one, but that was going to happen sooner or later anyways.
I write a lot libraries that I think are interesting but very few people worry about. They rise up, either I stick with them or I trail off, but they always scratch my itches. On that front, the following things are left to do:
I haven't found the money to get a developer's signing certificate, so I'll probably just put everything up on my public MyGet repository[11].
Categories:
Tags:
Below are various useful links within this site and to related sites (not all have been converted over to Gemini).
https://d.moonfire.us/blog/2021/07/10/gallium-nitride-and-gemini/