Chris Jarling

Fullstack Engineer
13th Feb, 2022

Escpae liquid tags in jekyll code blocks

In my last post I learned that jekyll processes all liquid tags on a page, no matter if they are in a code block or somewhere else on the page.

So if I write something like

{% for index in (0..3) %} {{ index }} {% endfor %}

the actual output would be

0 1 2 3

as the liquid tags get executed.

To stop this behaviour, the jekyll documentation offers two solutions:

  1. Wrap the code block in {% raw %} and {% endraw %} tags. The example above would become
{% raw %} ```html {% for index in (0..3) %} {{ index }} {% endfor %} ``` {%
endraw %}
  1. Add render_with_liquid: false to the posts front matter, to disable it for the whole document

12th Feb, 2022

Building a simple tag system for jekyll

I wanted a simple tagging system for this site. It had to do two things:

  1. Display one or muliple tags on posts
  2. On click, navigate to a page that lists all the posts tagged with that tag

In the future, I might want to add an index page that holds a list of all the tags used on the site as well, but for now these two points will be enough.

Looking into the topic, I found a good starting point here. However, I did not like the fact that a hook would generate an empty markdown file for every tag used on this site in a folder. I understand that this might come in handy if you wanted to have some custom markup for special tags. Since this is not something I plan on doing, I tried something different.

The tagging system I built for this site works like this:

  • I have a tag layout defined
  • Upon building the site, go through all posts and fetch all the tags
  • For each tag, create a new html site in the _site folder with the tag layout
  • Done

This allows me to not have any markdown file lying around for the tags. For full disclosure, I have to admit that I don't know how performant this will be for large sites. I simply did not write enought yet...

In detail

My tag layout is pretty much the same as in the post linked above:

{% raw %}

---
layout: default
---

<div>
  <h2>All posts tagged <span>#{{ page.tag-name }}</span></h2>

  <ul class="a-articles-list">
    {% for post in site.posts %} {% if post.tags contains page.tag-name %}
    <li>
      <h2><a href="{{post.url}}"> {{ post.title }} </a></h2>
      <span>Published on {{ post.date | date_to_long_string }}</span>
    </li>
    {% endif %} {% endfor %}
  </ul>
</div>

{% endraw %}

For the page generation, I used a local generator plugin. I called it generate_tags.rb and it lives in the _plugins folder. You can name it whatever you want as long as everything lives in one file. Jekyll does not care. It loads the plugin and runs it.

In the generator, I have my custom Jekyll::Page defined:

class TagPage < Jekyll::Page
  def initialize(site, tag)
    @site = site
    @dir = 'tags'
    @basename = tag
    @ext = '.html'
    @name = "#{@basename}#{@extension}"
    @data = {
      'tag-name' => tag,
      'layout' => 'tag'
    }
  end
end

This does two things (besides some boilerplate Jekyll needs):

  1. Create a file named like the tag passed in, in the tags folder, with the .html extension
  2. Define tag-name and layout in the data hash. Thats what we usually define via the frontmatter in .md files

For the generator itself, I have two short functions:

def tags_on_posts(posts)
  posts.docs.map{ |p| p.data["tags"]}.flatten.uniq
end

def generate(site)
  tags_on_posts(site.posts).each do |tag|
    site.pages << TagPage.new(site, tag)
  end
end

tags_on_posts just loops through all posts and returns the unique tags used on the site.
generate then loops through all of those, creates a new TagPage for each tag and adds them to the sites pages.

That's really all there is. Here's the complete code from my generate_tags.rb:

module GenerateTags
  class TagPageGenerator < Jekyll::Generator
    safe true

    def tags_on_posts(posts)
      posts.docs.map{ |p| p.data["tags"]}.flatten.uniq
    end

    def generate(site)
      tags_on_posts(site.posts).each do |tag|
        site.pages << TagPage.new(site, tag)
      end
    end

    class TagPage < Jekyll::Page
      def initialize(site, tag)
        @site = site
        @dir = 'tags'
        @basename = tag
        @ext = '.html'
        @name = "#{@basename}#{@extension}"
        @data = {
          'tag-name' => tag,
          'layout' => 'tag'
        }
      end
    end
  end
end

The last thing to do is to add links the the posts tags in the layout (once again, taken from the blogpost linked above):

{% raw %}

{% for tag in page.tags %} {% assign tag_slug = tag | slugify: "raw" %}
<a><span>#{{tag}}</span></a>
{% endfor %}

{% endraw %}

Now I can just add tags to to frontmatter of a post and everything else will be taken care of when the site is built:

---

title: Building a simple tag system for jekyll
date: 2022-02-12 22:38 +0100
tags: web-dev jekyll
---

11th Feb, 2022

Getting into Rust

Got into the Rust programming language over the last few weeks. Since I mostly work with dynamic typed languages, it's a steep learning curve to get into a static typed language again. I enjoy it a lot though, always liked working with typescript in the past.
Started with reading the The Rust Programming Language which was pretty theoratical. Did not make it all the way through as I wanted to have something more hands on. Will probably consult it from time to time in the future when I stumble across things I don't understand.

For now, started with https://bfnightly.bracketproductions.com/ which is a lot more practical. Also peaked into game dev a few times over the past year, so this is something that really interests me. Repo


24th Sep, 2020

Conditional classes for the :host in Angular

Working with Angular is... interesting. Coming from React, I find it hard to grasp because there is a lot of functionality in it, it is verbose and in general feels like enterprise software. The flip side of that is, that for every problem you encounter, the framework probably already has a solution integrated. The challanging part is to find it.

The other day I was working on a new component. It was similar in styling to a component that we already had in our project, but different enough in the way it was used to be its own component with its own name. So I created a new component and imported the stylesheet of the component I wanted to use as a basis so I could extend its styling.

The component I was building on did not render more than an <ng-content></ng-content> in its template and thus was using the :host pseudo-class for styling1.

The :host pseudo-class

To understand what my problem was, we first need to understand the concept of the :host pseudo-class.
The :host pseudo-class only is effective inside the shadow DOM and as the name suggests, it selects the host of a custom element (in our case, our component is this custom element).

Even though the :host class is not an Angular concept per se, lets use Angular to illustrate the usage of :host with a concrete example.

Let's image we have a class like the one I was describing above. It is a simple UI component that does not accept any properties and handles no logic other than taking some content and wrapping some styling around it. Something like a card might be a realistic example.

In this case, the component.ts file might look like this:

import { Component, Input } from '@angular/core'

@Component({
  selector: 'app-card',
  template: '<ng-content></ng-content>',
  styleUrls: ['./card.component.scss'],
})
export class CardComponent {}

We can now call this component in our app:

<app-card>
  <h2>Hello Wrold</h2>
  <p>How's it going?</p>
</app-card>

Here, the host of our component would be the <app-card> selector; it hosts the content of the component.
You may have guessed it by now, but this is also what we can target using the :host class in css. If we wanted to give our card a background, this is the css we could use:

:host {
  background-color: #f8f8f8;
}

In essence, the :host selector allows us to style a component form the inside out.

Why not divs and classes?

The short answer to this question is: Because we don't have to, so why should we.

You could achieve the same effect with wrapping the <ng-content> in a div with a class and then apply the styling through this. Out template then might look like this:

template: '<div class="card"><ng-content></ng-content></div>',

While there is nothing inherently wrong with this, there are a couple of reasons against this in my opinion.

First: KISS. There is absolutely no need to introduce a DOM-Node for styling, so we have a great opportunity of avoiding complexity here. I'm all for using it.

Second, we are now encapsulating the content of our card inside another DOM-Node, limiting the amount of styling we can apply to it from the outside. Styling the host does not only work from the inside of the component, it also works from the parent component. Inside the parents styles, we can select our card as well, since it is basically just a DOM-Node:

app-card {
  display: flex;
  flex-direction: column;
}

With a wrapping <div>, this would have no effect on the actual content we are passing in to the <app-card> component.


Now that we have a basic understanding of the :host pseudo-class, lets get back to the example I constructed in the beginning of this article.
The new component I was building needed to have conditional classes based on the value of an @Input. Applying conditional classes is no problem in Angular, as you can use the [ngClass] directive. But the only tags my new component rendered where <ng-content>, which does not accept classes. And because I wanted my component to be usable in the same way as the component I built upon, I could not add a wrapping element for styling.

Adding conditional styles to :host

Luckily, there is also a :host() pseudo-class function. The function accepts a class name an only applies the styling defined in the function to the host if the host has the given class set on it.

Sticking with the card example from above,

:host(.pink-text) {
  color: pink;
}

would only have an effect on the instances of card with the .pink-text class set on it:

<app-card class="pink-text">
  <p>I'm pink!</p>
</app-card>

Great, that basically solves our problem. However, this can be enhanced a little. If we were to use our component like this, we would have to remember to check the css for the correct class names and set them on our component in our parent. I'd prefer to control that behaviour with an @Input prop.

As I mentioned in the beginning of the article, Angular mostly has a solution for all your problems, the hard part is to find it. So after searching around the web for a few minutes, I stumbled across this StackOverflow answer. Turns out: You can conditionally set classes to the host element.

The @Component decorator inherits the host option from the @Directive decorator, so inside our component.ts file we can do the following:

import { Component, Input } from '@angular/core'

@Component({
  selector: 'app-card',
  template: '<ng-content></ng-content>',
  host: {
    '[class.pink-text]': 'isPink === true',
  },
  styleUrls: [
    './card.component.scss',
    '../base-component/base-component.component.scss',
  ],
})
export class CardComponent {
  @Input() isPink: boolean
}

and in our css:

:host(.pink-text) {
  color: pink;
}

which solves all of my problems. We can now modify our component with a property:

<app-card [isPink]="true">
  <p>Pink like a panther!</p>
</app-card>

Footnotes

  1. The <ng-content> will be replaced with whatever is passed into the component at runtime. Because of this, classes cannot be used here. ↩


8th Sep, 2019

Kill your darlings

Some time ago, William Faulkner said that "In writing, you must kill your darlings". You can make of the quote what you want, but I interpret it like this: There are times, in writing, when you have to kill a passage (or a character) you love, if it does not help to develop the story. Removing parts, even if you've grown attached to them, will make a better end product in that case.
I think the same is true for the development of a software project.

Earlier this week, I had to implement a new feature in one of our projects: For some entities, there should be a map displayed, showing where the entity is located1. My solution was to get the physical address of these entities from an external API, parsing them to geo coordinates and saving them to the database so we would be able to center the map on the correct point. It involved a service object that used dependency injection, a task to update existing locations, a modification to our current API calls and a good amount of new tests to keep the coverage up. I think it was a solid pice of software and to be honest, I was a little proud of it.

All of this decision making happened when the rest of the project team was on holiday. When they came back, I opened a Pull Request and waited for their feedback to come in. While the code seemed to be okay, in the midst of the review, the question every developer hates to hear came up: "Why do we need all of this, though?"
So we started discussing my solution and in the process it became obvious that, indeed, we did not need large parts of what I had written. There was a much simpler approach to the problem (that I had not seen on my own). It provided a better experience for the end user, was a closer fit for the customers needs and the code for it was a lot less complex.

I could have started arguing - and to be honest, I was tempted - so that the code I had written would go to production2. I liked what I had done after all and I had put a considerable amount of work into it up to this point. Going back to the problem would mean to to spend more time on the development of this feature, in addition to deleting huge parts of my work.
But trying to hold on to my solution because of my ego would have been wrong: The new solution was better for all stakeholders, so it would improve the whole product in comparison to my approach.

There I was: I had to kill my darling. And I did so, because I knew it was the right thing. It did not feel good. At all. But after implementing the new solution, after seeing the simpler code, after using the feature myself and after showing it to the customer, it's clear that it was the right call. I'm glad I did it.

I think this is an important lesson. You should never take your work personal and get attached to it on an emotional level, as it is hard to stay objective in that situation. Always allow the mere existence of it to be questioned.
If you think it is the objectively better solution, argue for it, hell, fight for it. But always ask yourself if you just like it because it is something you crafted. And if that is the case, be ready to let it go.

Footnotes

  1. I'm Sorry if I'm not really concrete here, but I haven't asked for permission to write about the project, so this what we'll have to work with. ↩

  2. I'm sure that my team would not have let this happen. They're great and I'm positive they would have called me out on that. ↩


26th Jun, 2019

On using Feature Flags

Imagine working on on a redesign inside an existing product. Maybe it's just a small feature that needs an overhaul, or maybe it's a complete redesign of something that may takes months to finish.
Knee deep into it, with many changes done and halfway through it, you and your team notice that gnarly bug in production that is costing the product lots of money. It needs to be fixed as soon as possible.
This is where things get tricky. It's not an unsolvable problem, but chances are that the are rollbacks involved or the bug fix cannot be tested on your staging environment because the half-finished redesign is deployed there. Things get confusing for the team.
Maybe this does not sound like a problem to you, but I get sweaty palms doing things like these. Now you have a lot of internal knowledge that has to be passed between team members: Which branch is deployed on production? Which one on staging? And what has to be done in order to deploy to either of the environments? How do we make sure that nothing is lost or gets corrupted in this process?

Enter: Feature Flags. To get a grasp of what they are, I suggest you read this article by Martin Fowler, which explains it in detail, but in a Nutshell: Feature flags are small code snippets that regulate who can see which feature under what circumstances. For example, only admins can see the new feature. Or only the user with the ID 5. Or just every 10th visitor.

We started working with Feature Flags in the late summer of 2017, while doing a complete redesign of an online shop that was getting a good amount of traffic and needed to stay maintainable while we were working on the redesign. When I first used Feature Flags, I wasn't a big fan. There were a couple of reasons for it.

First, using Feature Flags was adding an additional layer of complexity to the project that I was feeling was not bringing us closer to our goal. The fact that I never worked with the concept and it took me a while to wrap my head around it may have had some influence on that perception. But it is a fact that this is extra code in your project you need to maintain and think about.

Second, I no longer had the feeling that our code was deployed from a single source of truth. Before, there was the master branch, and what was in the master branch was visible to the public, period. Now, there was techbically still only the master branch, but what it revealed to the public was out of the direct control of the code I wrote. It was dynamic, and that was making me a little nervous.

However, we have now been working with Feature Flags for about a year and I have gotten attached to the concept since. I only started to notice a lot of the benefits after using them for a while, so I now have a different view on the concept:

What I thought about the single source of truth was actually wrong. Coming back to the example above, there is no single source of truth anymore once you start to deploy different branches on different environments. Feature Flags allow you to always deploy master, no matter what. Couple that with a good CI and this feels really stable.

It is also awesome for testing, especially for testing with production data. You can set up your staging environment with a production database dump and get the environment to mimic production very closely, but with a Feature Flags, you could deploy to production, only make the feature available to yourself and test with real data.
It's also great for letting customers test things. No need for remembering two credentials for two environments, just clear the customers account for the feature.

What I like most is that a change can be deployed immediately. Even if it is not available to all users right away (or to none at all), there is no need to worry about a "Big Bang" deploy from staging to production on day X and hoping that nothing breaks. You just change a flag from "some users" to "all users" and you are done.
If things get nasty, you just disable the feature again and everything is fine. No need for someone sitting in front of a laptop on a Saturday, trying to roll back a broken feature with sweat on their forehead. Depending on how you set it up, you can just disable the feature from your mobile phone.

Right now, I'm at a point where I'd rather have no staging environment at all and just flip all the features. This is something past me from two years ago would have hit me in the face for. But what does that guy know, anyways?


15th Jun, 2019

Getting lost

It can be helpful in life to have a goal in mind and decide what you do based on the contribution that thing makes towards reaching that goal. This is, by nature, very limiting, which is exactly what you want. It helps you only do the meaningful things. But this will also bear the danger of not letting change happen.

From time to time, it is nice to just get lost. When you read something, and that sparks an Idea in your head, just explore that idea further. Get into the topic, find another interesting thing and then start exploring that. Just enjoy the process of not knowing what you are doing and why. Enjoy wondering and learning things and the joy of experiencing how many things are out there.

Just don't forget to go back on the path you were on before, and focus on what is essential to reach you goal. Also, challange that goal every now and then.


17th May, 2019

On Chesterton's fence

A while ago, I came across a Hacker News thread asking for advice on dealing with a large, legacy codebase. In the comments, someone mentioned Chesterton's fence.
Chesterton's fence is an interesting concept, that I tend to forget on the regular. So I am writing it down here, to help me remember.

Chesterton's fence is a principle that originates from G. K. Chesterton, who I had not heard of before, but who apparently wrote a lot of books. The basic idea is quite simple:

[...] let us say, for the sake of simplicity, a fence or gate erected across a road. The more modern type of reformer goes gaily up to it and says, "I don't see the use of this; let us clear it away." To which the more intelligent type of reformer will do well to answer: "If you don't see the use of it, I certainly won't let you clear it away. Go away and think. Then, when you can come back and tell me that you do see the use of it, I may allow you to destroy it.

When writing code, this happens to me a lot. I see something that is not as clean as it could be or that I have a better idea on how to solve. Maybe it is a weird function, maybe it is just a CSS rule I don't think is necessary. I try to change this, because look at me, how good I am in what I am doing, how much better I can make this code. But I often times do not understand why this thing is in place.

Most of the time, this results in a mildly embarrassing (but earned) "Yes, I know this is strange, but we need this because of X" in a code review1. Sometimes, it results in me, wasting time changing something only to discover that it should never have been changed. It has not resulted in something important breaking on production. Yet.

To keep is this way, this is my reminder to only change things that I understand. Maybe it can work as a reminder for you as well, if you need it.

Footnotes

  1. Which is no problem when asking about why this is there. With an "This is not necessary, is it?", however, its a different story. ↩


12th May, 2019

Being the best version of myself

We are all trying to get better at something. We are all trying to improve. We may not have thought about it carefully or written it out, but we are trying. Maybe it is being a better parent, be better in our field of work, or maybe it is just being better in a video game. It does not matter what it is, but we all are constantly trying to improve. Or at least this is the way I like to think about humanity. Maybe I am naive.

I, too, try to get better at various things. I try to be better as a developer, which is what I earn my money with. I try to get better in relationships with other people. I try to get better in the sports I do. I try to be a better human in general. This might not be what you are trying to improve in, and that is okay. It does not matter what it is we are trying to improve in, as long as it matters to us.

For all the things I try to get better in, I tend to be quite ambitious. I've heard from others that I am rather disciplined, and while I often times do not think so, this might be true. Being ambitious can be good, but it also comes with a dark side. This side emerges once I fail at something I wanted to improve on. When I miss one day of writing morning pages. When I do not exercise for a week or so. When I eat meat, even though I think a vegan diet is the only viable diet from a logical standpoint. I tend to beat myself up then. I tend to think that I am a failure.

This is bullshit. There is no reason to think this way. As long as I realise that I did not act like the version I'd like to be and get back it the next day, it is okay1. Of course, I would have liked it better have I acted in the right way. But nobody is perfect, there is no person in this world that is constantly acting the way they would like to. Our characters are never finished.

The important thing is this: In the grand picture, I got better. I made progress. Missing one shot does not move me back to where I started. It is a hiccup, but the progress is not lost. Compared to who I was a day, a week or a year ago, I am so much better now, because I put in the effort.

I recently read "Ego is the Enemy" by Ryan Holiday (which is a great book I can only recommend), and in it was a quote by John Wooden:

"Success is peace of mind, which is a direct result of self-satisfaction in knowing you made the effort to do your best to become the best you are capable of becoming"

In the long run, we are getting better. Small setbacks are normal.

I know this, but I have to keep telling it myself every time I am in a situation like this. It is okay to slip, as long as you get back up after it.

Footnotes

  1. It is something entirely different if this does not make me feel bad at all. In this case, I am probably wasting my time on something that does not matter to me and I should let it go. ↩


22nd Apr, 2019

Buying Things

We all buy a lot of stuff. Small things, big things, expensive things and cheap things, things we need and things we don’t need, but think we need. I think it is safe to assume that I buy something every day. Now, most of the time, this might just be food. But there is also a fair share of things I buy that are not consumables.

Ever since I started to learn about minimalism, I try to be mindful about what I buy. It is hard do identify things that you don’t need anymore and get rid of them. It is work. You need to do the mental work of letting that thing go. Then, if you don’t want to throw it to the trash, you need to deal with getting rid of it. More often than not, that means dealing with people on online sales places. Those people have a tendency to stress me out. All of this is added to the the actual cost of owning a thing. I feel like even letting go of a thing is not as free as I like to imagine at times.

There is a simple way of avoiding the whole process of letting things go and getting rid of them, of course. It is not to get the thing you don’t really need in the first place. This is something that I introduced to my life over the past year or so and it is helping me a lot. If you know me in person, you will probably catch me talking about this thing and that thing that I’d really like to buy. I like to talk about that a lot (which is a whole different thing that I should improve on), but I won’t buy the things all that often.

I tend to categorise things I buy. I don’t have these categories written out anywhere (well, until now, I guess) and I am not too picky about the definition. But those categories are somewhat like the following:

  • Things I need to survive or for everyday life. This is food, obviously, and things like cleaning utensils for the apartment. Toilet paper. Shower gel. There is no real need to think too much about wether I need that can of beans or not. Yes, I need it. Otherwise I will die. I do not think about wether I should buy those things or not.
  • Things that directly benefit my well being. For example, I love riding my road bike. The weather got warmer, and since I started the hobby in autumn, I only had long clothes. In order to be able to keep riding my bike, I needed short clothes. I just bought them, as they did directly benefit my happiness in allowing me to continue to exercise in my preferred way. Books are another example. I do not think too much about buying these things.
  • Things that I think I need at the moment, but may not need at all. These are things that I see and really want to have. Like a new cellphone, or an Apple Watch or a new couch. Those are things where I cannot trust myself in the moment. I think I really need it and it will make my life just so much better. But it might as well not. I tend to think a lot about these things and also have a system to make sure that I actually need those things.

The system is taken from (I think) an episode of The Minimalists Podacst and adapted for my needs.
When ever I get this feeling that I need a thing, I write it down in my ToDo-List and set a reminder for 30 Days. I cannot buy it before that timespan has passed. Once it has, I get reminded of the thing and I can then take a closer look of how the last 30 days without this thing went. Did I miss it at all? Would it actually have made my life better? Most of the time, the answer is no.
If it is yes though, I give myself permission to buy it. It is no longer an impulsive purchase and I know that this will most likely enhance my life in some way. If it is not too expensive and I can afford it, I might buy it right away. Otherwise, I save up the money and buy it when I can.

This might sound like a lot of control, and it probably is. However, I feel in a society where you are encouraged to buy, buy, buy every day, it is a good idea to reflect on wether this is something you want, or something that society wants you to want.

Newer posts
Older posts
© 2024 Chris Jarling