💾 Archived View for d.moonfire.us › blog › 2021 › 07 › 10 › gallium-nitride-and-gemini captured on 2023-03-20 at 18:13:31. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

➡️ Next capture (2023-04-26)

-=-=-=-=-=-=-

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

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

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

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/