π Waking Up In Geelong
Facinating website for the casual Melbourne train buff. A lot of great photos of rail infrastructure, plus what appears to be routine βphotos from 10 years agoβ posts, which brings me back. Link via my dad.
The electrification of large vehicles is continuing. For the first time, I just saw and not heard an EV bus. Must say that seeing a bus accelerate out of a roundabout without hearing a loud diesel engine was a unique experience.
Itβs funny seeing marking images of devices intended for βworkβ. Thereβs always one shot featuring either a graph or a spreadsheet.
Just had an hour long Slack call with someone who was drafting a huge list of tasks in a single Slack message. Thank goodness Slack didn’t fall over, or we would have lost an hour of work. Talk about living on the edge.
MacOS’s virtualisation framework looks interesting and I’d like to get my hands on it. Problem is that I have an Intel mac and it only works on Apple silicon. Now I’m wondering if I’m interested enough to actually open my wallet for it.
More On Scripting In Dynamo-Browse
Making some more progress on the adding scripting of dynamo-browse. It helps being motivated to be able to write some scripts for myself, just to be able to make my own working life easier. So far the following things can be done within user scripts:
- Define new commands
- Display messages to, and get input from, the user
- Read the attributes of, and set the attributes of, the currently selected row (only string attributes at this stage)
- Run queries and get result sets, which can optionally be displayed to the user
Here’s a modified example of a script I’ve been working on that does most of what is listed above:
const db = require("audax:dynamo-browse");
db.session.registerCommand("cust", () => {
db.ui.prompt("Enter user ID: ").then((userId) => {
return db.session.query(`userId="${userId}"`);
}).then((resultSet) => {
db.session.currentResultSet = resultSet;
});
});
db.session.registerCommand("sub", () => {
let currentItem = db.session.selectedRow;
let currentUserId = currentItem.item["userId"];
if (!currentUserId) {
db.ui.print("No 'userId' on current record");
return;
}
db.session.query(`pk="ID#${currentUserId}"`, {
table: "billing-subscriptions"
}).then((rs) => {
db.session.currentResultSet = rs;
});
});
The process of loading this script is pretty basic at the moment. I’ve added a loadscript
command, which reads and executes a particular JavaScript file taken as an argument. Scripts can be loaded on startup by putting these loadscript
commands (along with any other commands) in a simple RC file that is read on launch.
I’m not sure how much I like this approach. I would like to put some structure around how these scripts are written and loaded, so that they’re not just running atop of one another. However, I don’t want too much structure in place either. To me, having the floor for getting a script written and executed too high is a little demotivating, and sort of goes against the ability to automate these one-off tasks you sometimes come across. That’s the downsides I see with IDEs like GoLand and, to a lesser degree, VS Code: they may have vibrant plugin ecosystems, but the minimum amount of effort to automate something is quite high, that it may not be worth the hassle.
A nice middleground I’m considering is to make it to have two levels of automation: one using JavaScript, and the other closer to something like Bash or TCL. The JavaScript would be akin to plugins, and would have a rich enough API and runtime to support a large number of sophisticated use-cases, with the cost being a fair degree of effort to get something usable. The Bash or TCL level would have a lower level of sophistication, but it would be quicker to do these one-off tasks that you can bang out and throw away. This would mean splitting the effort between the scripting API and command line language, which would fall on me to do, so I’m not sure if I’d go ahead with this (the command line “language” is little more than a glorified whitespace split at the moment).
In the meantime, I’ll focus on making sure the JavaScript environment is good enough to use. This will take some time and dog-fooding so they’ll be plenty to do.
I’ve also started sharing the tool with those that I work with, which is exciting. I’ve yet to get any feedback from them yet, but they did seem quite interested when they saw me using it. I said this before, but I’ll say it again: I’m just glad that I put a bit of polish into the first release and got the website up with a link I can share with them. I guess this is something to remember for next time: what you’re working on doesn’t need to be complete, but it helps that it’s in a state that is easy enough to share with others.
Currently enjoying the sound of an eastern rosella singing it’s heart out, helping to make a long tiring week just that little bit easier to deal with.
It wasn’t that long ago where a Google search would return useful results that were not found by either Ecosia or DuckDuckGo. That’s really not happening as often nowadays. In fact, if Ecosia/DDG cannot find anything, there’s very little reason to bother trying Google at all.
Trying to find which countries require 3D Secure for online payments is annoying. Each website only list two or three countries as examples: “3DS is required in some countries like X and Y⦔, and do not link to any sources. I need to do like a set union to get the full list.
For the past several weeks, these two have been foraging in front of my place. They’re quite skittish, and will fly off when anyone approaches (it’s difficult to get a picture of them). But after a while, they’ll always return to the same spot.

Evening walk, with the wattles in full bloom.
Kyneton Botanical Gardens
Went to Kyneton with Mum and Dad today. While they went off for a bike ride, I had the opportunity to go for a walk around the botanical gardens. This was my first time there, and although the gardens themselves were not very big, it was still a pleasant experience. Here are some photos I took of that visit.
This was followed by lunch at Little Swallow Cafe. The place was quite busy β I suspect that their reputation is such that it would be busy most of the time β but the food was very nice. We then went for a short walk around Kyneton and then drove to Malmsbury for a coffee and Devonshire tea.
It was a little cold but the rain held off. All in all, a good day for it.
Kyneton Railway Station.



Work was the reason I was unable to go for an afternoon walk last two Fridays. The wet weather was not going to be the reason why I was unable go for one today.

This week’s earworm: The Songs Of Distant Earth by Mike Oldfield π΅
Not necessarily a new one in my rotation, but I’ve been listening to it non-stop all week.
In this new work-from-home world we now inhabit, it’s coming to the point where my productivity is directly proportional to how many good podcasts are available today.
DynamoDB JSON Attribute Types Quick Reference
Because apparently it’s too difficult for AWS to provide an easy way to find this information.
Atomic Types
Type | JSON | Value |
---|---|---|
Binary | B |
String value containing the Base64-encoded binary data. |
Boolean | BOOL |
Either true or false |
String | S |
String value |
Number | N |
String with the numerical value |
Null | NULL |
Should always be true |
Collection Types
Type | JSON | Value |
---|---|---|
List | L |
A JSON array, with each element being an attribute with a type. |
Map | M |
A JSON object, with the keys being the map keys, and the values being an attribute with a type. |
Set Types
Type | JSON | Value |
---|---|---|
Binary Set | BS |
A JSON array of Base-64-encoded binary data. |
Number Set | NS |
A JSON array of string with the numerical values. |
String Set | SS |
A JSON array of strings. |
Sources:
- AttributeValue API documentation: this the definitive guide from AWS.
- DynamoDB Item size and format: more info about the particulars of each attribute type.
Scripting In Dynamo-Browse
Making some progress with adding scripting capabilities to dynamo-browse. I’ve selected a pretty full-featured JS interpreter called goja. It has full support for ECMAScript 5.1 and some pretty good coverage of ES6 as well. It also has an event loop, which means that features such as setTimeout
and promises come out of the box, which is nice. Best of all, it’s written in Go, meaning a single memory space and shared garbage collector.
I’ve started working the extension interface. I came up with a bit of a draft during the weekend and now just implementing the various bindings. I don’t want to go too fast here. I’m just thinking about all the cautionary tales from those that were responsible for making APIs, and introducing mistakes that they had to live with. I imagine one way to avoid this is to play with the extension API for as long as it make sense, smoothing out any rough edges when I find them.
It’s still early days, but here are some of the test scripts I’ve been writing:
// Define a new "hello" command which will ask for your name and
// print it at the bottom.
const dynamobrowse = require("audax:dynamo-browse");
dynamobrowse.session.registerCommand("hello", () => {
dynamobrowse.ui.prompt("What is your name? ").then((name) => {
dynamobrowse.ui.alert("Hello, " + name);
});
});
The prompt
method here asks the user for a line of text, and will return the user’s input. It acts a bit like the prompt function in the browser, except that it uses promises. Unfortunately, Goja does not yet support async
/await
so that means that anonymous functions will need to do for the time being (at least Goja supports arrow functions).
Here’s another script, which prints out the name of the table, and set the value of the address
field on the first row:
const dynamobrowse = require("audax:dynamo-browse");
dynamobrowse.session.registerCommand("bla", () => {
let rs = dynamobrowse.session.currentResultSet;
let tableName = rs.table.name;
dynamobrowse.ui.alert("Current table name = " + tableName);
rs.rows[0].item.address = "123 Fake St.";
});
The rows
array, and item
object, makes use of DynamicArray and DyanmicObject, which is a nice feature in Goja in which you do not need to set the elements in the array, or fields on the object explicitly. Instead, when a field or element is looked up, Goja will effectively call a method on a Go interface with the field name or element index as a parameter. This is quite handy, particularly as it cuts down on memory copies.
These scripts work but they do require some improvements. For example, some of these names could possibly be shortened, just to reduce the amount of typing involved. They also need a lot of testing: I have no idea if the updated address in the example above will be saved properly.
Even so, I’m quite happy about how quickly this is coming along. Hopefully soon I’ll be in a position to write the test script I’m planning for work.
Also, I got my first beta user. Someone at work saw me use dynamo-browse today and asked where he could get a copy. I’m just so glad I got the website finished in time.
Coming up with a naming convention for things like metadata keys, customer HTTP headers, or log tags; and not writing it down and sharing it with others, is a mistake I’m paying for in spades at the moment.
A little while ago, when we were setting up Stripe and it came time to decide how metadata keys should look, I thought it would be enough to come up with a style in my head (e.g. snake case with a particular prefix) and simply start using it. I guess I was expecting that others would follow it, simply because they would be motivated to maintain the observed style, with the keys I were defining being a bit of an example here.
Well, I now know that not only did this not happen, but when someone does want to know how these names should look (and I include myself here) there’s no definitive guide available to refer to. The result: no convention at all. Different cases, keys without a prefix, etc. Not be a huge problem, but being unable to rely on a convention does mean more cognitive load when it comes time to figuring out how a name is expected to be formed. Plus, it just looks untidy.
I guess it would have been a good idea to document it somewhere. Doing this is not being overly specific or dictating convention from above. It doesn’t have to be as elaborate as an IETF standards: just a few paragraphs and examples that is accessible to everyone. And if others disagree with the proposed convention, then work it through until everyone is happy with it.
I’ll try to remember this for next time.
P.S. More services really need to support user-defined metadata fields like Stripe does.
I discovered Goomics by Manu Cornet this morning after reading this Ars Technica article. There are some pretty amusing comics there about “life at Google.” Here’s a few that I particuarily liked:
Credit: Manu Cornet