go /

veun: two months (or so) in

.md | permalink | Published on January 25, 2024

I've been working on a demo-server for the veun library, and this has been interesting. The goal of the demo is to have some real-world facsimile of something that one would build and I wanted to figure out some of the warts and patterns of using the library for this use case.

A list of things in no particular order

  • I've stopped actively updating the series on how I'm building view-trees. It's its own thing now.

  • The demo-server is a practice in literate programming. I don't know why exactly, as this is a ton more work to get things to be ok, but having demo components with no real explanation didn't feel right. I knew what was possible, but wanted the server to be fully self-documenting as a demo.

    This is basically the only part of the demo repo that isn't shown in the UI, but you can read the lit-gen command source. I may move this out to its own package.

  • There's a veun/el package. Originally, the library was built only using templates. I wanted functional composition, and for testing and really small renderable pieces, using veun.Raw was sufficient, but it felt wrong.

    The first version of the package was a function that looked like el.El("h1"), but eventually I added codegen so that you could do el.H1(). There is actually no way to directly instantiate an HTML element that isn't already defined as a function. There's also support for void elements, and you can't add child nodes to them (because the type doesn't implement the functionality).

    The package also does HTML encoding for attributes (el.Attrs) and the text primitive el.Text, which veun.Raw does not.

    An added benefit of this is that it should be more performant to generate views using this package than going through template invocation in pathological cases: recursive tree views (which the demo nav is an example of). Going through recursive dynamic slot function dispatch in text/template is slower.

  • Also added veun/template. The functions and types in this package were all originally in veun. After introducing veun/el, which I started using a lot more than expected, it felt like the right API decision to have both of these packages be separate.

  • Built-in handlers. Something I think is elegant: handler.Checked, and its usage. Basically, Checked goes through a cascade of handlers and if any of them are 404s will go to the next one. This allows for a fall through of dynamic pages, static urls, and custom 404s while still being able to use built-ins like http.HandlerNotFound().

    There are a couple of other ones, like handler.OnlyRoot, that are great for the / base case when using the standard libary's http.ServeMux.

  • When adding the <!DOCTYPE html> to my server, I learned that my implementation of handler checked didn't write headers in the write order and this ended up serving all static files as text/plain, which was a fun bug to track down and fix.

  • When adding the notFoundHandler to the demo server, I realized that if the server was being crawled, rendering a 404 would be kind of expensive.

    I wanted this handler to be much closer to static and be doing as little compute as possible. This view isn't going to change for every single not found request.

    Adding MustMemo was really useful here. The fact that veun.Raw is just a string that directly becomes template.HTML made it trivial to keep all of the interfaces working transparently.