This page permanently redirects to gemini://d.moonfire.us/blog/2021/07/10/gallium-nitride-and-gemini/.

Gallium Nitride and Gemini

=> Up a Level

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/ | 2: https://statiq.dev/ | 3: https://fedran.com/

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 | 5: gemini://d.moonfire.us/

Why?

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.

Entity-Component-Systems (ECS)

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.

Gallium

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(new FileInfo("/bob.txt"));
Entity entityWithReplacedUri = entityWithTwoComponents
  .Set(new Uri("http://path-to-replace-d.moonfire.us/"));
Entity entityWithOnlyUri = entityWithReplacedUri
  .Remove();

I also really like chained operations, so most of the processing looks like this:

IEnumerable input;
var onlyHtmlOutputs = input
  .OrderBy(x => x.Get().FullPath)
  .ForComponents((entity, uri) => this.Uri(entity, uri))
  .WhereComponents();

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.

=> 6: https://autofac.org/ | 7: https://nodatime.org/

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

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 list = entities
    .Run(new IdentifyMarkdown())
    .Run(new ParseYamlHeader())
    .ForComponents(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.

Gemini

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/ | 10: gemini://d.moonfire.us/

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.

Next Steps

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].

=> 11: https://www.myget.org/

Metadata

Categories:

=> Programming

Tags:

=> Gallium | Gemini | Gitlab | MfGames.Nitride | Statiq

Footer

Below are various useful links within this site and to related sites (not all have been converted over to Gemini).

=> Now | Contact | Biography | Bibliography | Support

=> Fiction | Fedran | Coding | The Moonfires

=> Categories | Tags

=> Privacy | Colophon | License

=> Mailing List

=> https://d.moonfire.us/blog/2021/07/10/gallium-nitride-and-gemini/

Proxy Information
Original URL
gemini://d.moonfire.us/blog/2021/07/10/gallium-nitride-and-gemini
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
907.72413 milliseconds
Gemini-to-HTML Time
4.188025 milliseconds

This content has been proxied by September (ba2dc).