Sunday, January 08, 2023

Blogging lite with Blogg

Trying something new, and blogging smaller shorter posts for a while. Not sure if it’ll stick but we’ll see!

The driver for this is from a few thoughts tonight:

  • Pondering life pre-Twitter, my posts were much more casual. Less produced. More off the cuff. So why?
  • Then I remembered how I used to blog on this Blogger site. I would send an email to blogger that turned into a post. Then I’d sign in on a desktop to touch it up. 
  • Or I’d start a blog with a web browser bookmarklet. And finish on the full site. 
  • But in the past few years I would mentally want to sit down at my laptop, plan it all out, hash out some code samples, etc. 
  • So what if I found a way to type blogs from my phone again? Maybe there’s a Blogger app still out there?

And low and behold I found Blogg on the iOS app store 
Blogg iOS App

So yeah, I kick myself for not migrating to the latest and greatest static site generator. But since I’m still on a hosted solution, I might as well try out some convenient tools!

We’ll see how it goes! But I like the casualness of this approach, blogging from my phone on the couch, holding my dog, and watching some football (SKOL Vikings! 💜🏈).

Now if I can find an auto-mastodon syndicator we might be onto something special

Wednesday, September 28, 2022

Language Version Managers - the Developer Parachutes

Originally posted on the DevObsessed blog at

Installing multiple language versions (e.g. Java 11, Java 19, Java 8 | Node 8, Node 16 | Python 2, Python 3 | etc) on your machine is scary, and not for the faint of heart. Conquer your fears with the benefit of a developer parachute, by using a Language Version Manager!

As software engineering consultants at DevObsessed, we get asked to help with a various assortment of projects – from legacy codebases running in maybe Java 8 or NodeJS 6, to greenfield creations using the newest versions of Java 19 or NodeJS 16. This isn’t only an issue between companies though, as most organizations have various versions of languages running at any given time too. Managing local installations of Java, Node, Python, Ruby, Elixir, etc can be daunting. In the past, trying out the latest version to check for bugs could lead to a developer’s machine being down for hours at a time. And if a rollback to a previous version was needed that could take even longer! Wouldn’t it be great if you could have a parachute with you on your mission, so that if anything goes wrong you can easily save yourself and proceed with confidence? Language Version Managers are your answer. Many version managers exist, and in this post we’ll show you how Sdkman for Java, and NVM for Node can provide you the courage to easily utilize multiple language versions without any worry in the process.


To set the scene, you should know that originally this blogpost was going to be about the recent release of Java 19. It would be a story about the newest and latest Java features, maybe how to use them, and probably why they would make it great for you to upgrade.

Without thinking about it, using Sdkman with a quick sdk install java 19-open and my MacbookPro was running the newest Java within seconds. It was so easy to upgrade to the cutting edge version, that it was summed up easily in a single tweet:

Tweet saying Living on the edge with an image showing sdkman installing Java 19


Not until this moment did I dive in to see that Java 19 was more of an incremental release. Some bugfixes for sure, but mostly it includes just some Preview and Incubator features (hiding behind feature flags) that won’t be fully live until Java 20 or beyond.

So after a few moments of trying out Gradle and IntelliJ support, I quickly reverted back to my previous Java version with sdk default java No fuss and no worry. Then it dawned on me how before Sdkman this would have taken me probably many minutes if not hours of work to flip back and forth of Java installation versions.

Sdkman for Java

Sdkman logo


Sdkman is the best Language Version manager for Java and all JVM languages, to include Groovy, Kotlin, and more. Don’t use brew or Oracle or some other installer on your macbook. Instead, install Sdkman and use a few commands to manage the version that you use.

  • Use the install instructions for Sdkman from
  • sdk ls java to see the various versions of Java that are available
  • sdk install java to install the Java18 OpenJdk version (or pick your favorite distribution from Azul or Microsoft or Amazon, etc)
  • The use keyword can switch the version for a single shell window, like sdk use java 19-open
  • The default keyword will setup a default for your whole machine, like sdk default java
  • Similarly use the same commands for Groovy sdk ls groovy, or Kotlin, or any of the 20+ various JVM languages!

NVM for NodeJS

NVM logo


In the Node world, there are a few options like NVM, N, FNM, and more. I’ve been a longtime user of NVM, so we’ll detail those instructions for use here:

  • While unofficially supported, nvm can be installed with homebrew using brew install nvm
  • nvm ls to see the available Node versions
  • nvm install --lts to get the latest stable LTS (Long Term Support) version
  • nvm install v8.17.0 or similar to get a specific version
  • nvm use v8.17.0 or similar to use a specific version in a single shell window
  • nvm alias default v16.17.0 or similar to set a default version


There are other various version managers for other languages. Ruby has rbenv or RVM, Python has pyenv, Elixir has kiex, and many more. No matter what language you are on, the key is to find a language installation manager to handle the various versions that you may need to use.

In the Java and Node worlds, Sdkman and Nvm are the parachutes you need for confidence, courage, and to save you from configuration management worry!

Tuesday, August 30, 2022

Export Events to a Github Pages Jekyll site

For a long time, our main Omaha Java Users Group communication has been through, however we do still have an website too. At first, we would nicely copy the event to both Meetup and, however within a few months you can see that we simply stopped duplicating effort and the events on quickly got out of date. Not a big deal at the time. But it would be nice to archive our past events to the site (especially as we explore alternatives in the near future [1]).

(if you want the TL&DR: see all the details at

Exporting Events from

The first step was to find a way to export our past events. I had hoped for a CSV export through the admin UI, but didn't see anything. I then thought it might take some web scraping, or maybe some network dev tools api call watching. But noticed that there was a grayed out setting in the admin UI for the Meetup API 🤔. A quick google found the Meetup GraphQL API Playground page. And to my surprise, sending a test query just worked! 🎉 After a little trial and error from their API docs, and increasing the result count to 100 to get all of our events without paging, I was able to export all of the past events to JSON with:
  query($meetupId: String!) {
    groupByUrlname(urlname: $meetupId) {
      pastEvents(input: {first: 100}) {
        edges {
          node {
And some inputs of:
Which gave a nice JSON result, like this:
    "data": {
      "groupByUrlname": {
        "description": "Omaha's Java User Group [@omahajug]( yadda yadda yadda",
        "pastEvents": {
          "count": 65,
          "edges": [
              "node": {
                "title": "Angular JS for Java Developers",
                "description": "This month //etc etc etc",
                "dateTime": "2014-05-20T17:30-05:00",
                "going": 27
..... Tech

Before showing you how the event blog post pages were generated, a quick note on the tech for (src at It is running on a standard Github Pages Jekyll workflow stack. While we have thoughts to move that over to 11ty, for now Jekyll is working fine. Create a new markdown file in the _posts folder, merge to the main branch, and the workflow auto-kicks off and redeploys our site like magic.

Generating Meetup event Jekyll posts

With this in mind, the blog needs .md files generated for each event in the events JSON. Using a little Groovy, this was done pretty quickly with a GSP template, some functions to prettify date formats, and a filename creation function. The groovy post.gsp template looks like:
layout: post
title:  "<%= longDate %> <%= title %>"

<%= description %>

(This past event was exported from
(<%= attended %> people had RSVP'd to this event in Meetup)
Then the code that generates the template for each JSON event is in PostGenerator.groovy:
    void generatePosts() {
        def events = new JsonSlurper().parse(getClass().getResource(SRC_JSON)).data.groupByUrlname.pastEvents.edges
        events.each {
            def event = it.node
            def filename = makeFilename(event.title, event.dateTime)
            def outfile = new File("$DEST_FOLDER/$filename")
            def filecontents = new SimpleTemplateEngine()
                            title      : event.title.replaceAll('"', '\"'),
                            description: event.description,
                            longDate   : convertToLongDate(event.dateTime),
                            attended   : event.going
            outfile.write filecontents
When executed, nice markdown files are generated in the output directory, similar to
layout: post
title:  "May 20, 2014 Angular JS for Java Developers"

This month //etc etc etc

(This past event was exported from
(27 people had RSVP'd to this event in Meetup)
The last step was to copy all of the new markdown files into the _posts directory, create a PR, merge it, and see the final results up at!

Wrap up

And that's it! Writing this blogpost probably took longer than the process to export Meetup to JSON, and generate new markdown files for the Jekyll blog! You can view all of the full source on my github at

[1] - A quick footnote, about Over the years Meetup has been great for advertising our group, attracting new members, having a great user interface, and easily collecting RSVP's for events. We've always been lucky enough to have great sponsors to pay the ever increasing fee, which is now up to $197.98/year. However Meetup's decision to not allow us (or any group) to "freeze" the account, means that our sponsor has been paying that fee for 2.5 years with little benefit. Talking to other local tech meetup organizers, it became apparent that many of us are pondering ways to free ourselves from these fees. Our sponsors could throw some pretty great user group parties with the savings! There's a lot of functionality we'd have to replicate though, so I'll leave that full discussion for another time...

Tuesday, May 11, 2021

Mock Intl and Date globals in Jest

In Javascript land, mocking the browser global objects can be a bit of a pain for tests. Searching StackOverflow gives plenty of complicated answers. Some suggesting using 3rd party mock libraries. Some that overwrite the global object itself.... But Jest already has this capability built-in and it isn't so bad:

So let's say you have a method that gets the user's timezone or the timezone offset. (the timezone offset is used sometimes since IE11 doesn't support easily reading the timezone, but I digress)

Now to test this, we'll need to mock out both the Intl and Date Javascript globals. We can do this using Jest's spyOn method to temporarily replace the global method with our own implementation. Notice that we setup the spy in the beforeEach and reset everything in the afterEach. The setup works something like this:

But that's it! No need to import an extra library. This is all supplied directly in Jest itself!

Tuesday, February 09, 2021

Blogger CSS Grid Responsive Layout

Yeah, a post about how to build a blog. Because owning a blog means that at least 20% of your posts need to be about the tech used in it's own creation. But this one is different. It's the tech used to upgrade a legacy Blogger site to a more modern responsive layout 😃


Well, while simultaneously an item of pride and embarrassment, I've had this Uncommented Bytes blog running for 17+ years somehow. (And more like 18-19 years but I lost the first bit of it when jRoller, the best hosted blog engine, went under and required the original migration to Blogger.) 

I wrote a post for the first time in a while, and was pretty annoyed with how it looked on mobile

I originally had a green-ish theme, and thought I'd tweak it to be responsive. But even though it was simple-ish, it was using some images for backgrounds and shading that were just overwhelming to try to update in the super-wordy blogger html template. (look at that beautifully retro Google+ link)

So I decided to "upgrade" to a cleaner look theme in Awesome, Inc --- which despite the genius name, was lacking in a couple areas:

  1. Fixed margins -- meaning not responsive to browser width
  2. No mobile friendly view -- mostly because it wasn't responsive, but also because I had used a Blogger switch to render a super generic mobile view too (which I think was triggered by user agent headers, yikes)


Trial-and-error isn't fun to describe, especially when it mostly involves browser devtools CSS hacking. But basically I took the super-wordy HTML template and hacked in some CSS Grid layout. 90% of the hack-magic was accomplished with this bit:

        div.content {
            display: grid;
            gap: 20px;
            max-width: 100vw;
            margin: 0 auto;

Plus a tiny bit of responsive handling:

        @media (min-width: 800px) {
            div.content {
                grid-template-columns: 2fr 1fr;
                    "header header"
                    "content sidebar"
                    "footer footer";
            	max-width: 1280px;
        @media (min-width: 1024px) {
            div.content {
                grid-template-columns: 4fr 1fr;
        .gist .blob-code-inner {
            white-space: pre-wrap !important;

And the last 10% was a few margin and padding tweaks sprinkled in other places. You can see the full code in my Github blogger-css-grid-layout project. And just in case I redesign again in the future, the full inception final product looks like this today: