Note: Using tmux and other more-or-less built for agent multiplexers is a slightly different approach to the one I describe here - I’m talking about using tmux from an agent instead of the agent running shell commands directly. Just saying that upfront to avoid confusion! Coding agents spawn a fresh shell by default. That shell is usually set up slightly differently than how I set up ones I use…
I’ve written before about passion projects and how taking care of them feels more like tending a garden than running a product. Mowing the lawn and weeding, with the occasional prune. That metaphor still feels right to me, but I’ve started thinking more concretely about what happens when a project reaches the point where I actually use it most days, and what “resilient” means in that situation.…
Aotearoa, Again is a Rails app that publishes a daily colourised photo edition and fans it out to email, Instagram, Facebook, YouTube Shorts, and now Bluesky. I wanted those Bluesky posts to pick up the enhanced link cards Bluesky renders for Standard.site documents, instead of a plain Open Graph preview. Standard.site is a set of AT Protocol lexicons for publications and documents. Bluesky reads…
I patched Virtualtrails for GHSA-xr9x-r78c-5hrm (CVE-2026-66066), the Active Storage arbitrary file read via libvips. The fix has Active Storage call Vips.block_untrusted(true) at boot, which turns off libvips loaders marked “unfuzzed” (unsafe for untrusted content). That includes SVG. On Virtualtrails I render share cards as SVG and convert them to PNG with libvips. After the upgrade, that…
I occasionally use openfortivpn on macOS. The VPN comes up fine, but internal hostnames still fail in apps. curl can’t resolve them. Browsers can’t either. Meanwhile dig internal.example.com returns the right answer. openfortivpn has added VPN nameservers to /etc/resolv.conf , but on macOS that file isn’t what curl , browsers, or ping use. dig still reads it, which is why lookups can work in the…
When you look up XML nodes by attribute value in Nokogiri, it’s tempting to interpolate the value straight into the XPath: doc . at_xpath ( "//item[@id=' #{ id } ']" ) That works when id is predictable. It’s shakier when the value comes from a user upload, an API payload, or anything else you don’t control. A value like foo" or @id="bar can break out of the string literal and match a different…
I was building an achievement editor in Virtualtrails where route creators can place a milestone either as a distance along the route, or as a latitude/longitude pair that gets converted server-side. The form needs to offer both options, but only one should be active at a time — and only the active panel’s fields should be submitted. My first iteration was a purpose-built controller with targets…
Turbo Drive intercepts link clicks and form submissions, fetching the next page with fetch() and swapping the <body> in place rather than doing a full browser reload. That’s great for smoothness, but it means the browser never performs a real navigation — and some things depend on a real navigation to work. Cloudflare challenge pages are one of those things. When Cloudflare decides a request needs…
I recently build out a data processing pipeline that uses Github Actions, and so needed to add several new workflows. I couldn’t for the life of me figure out how to test them. They were going to be initiated either as part of a pipeline (using workflow_call ), or manually, using workflow_dispatch . In most cases, each workflow had inputs which needed to be provided. Github Actions shows workflows…
Last night I had a great opportuntity to talk about Plain Language Law , a site that I built using my own time and personal development time provided by Ackama . Plain Language Law is a site that turns New Zealand legislation into plain language using LLMs . This was the first meetup I’d presented at, so I really appreciated the invite, and the chance to do a bit of personal development with…
This morning, while checking (yet again) how to install Postgres inside a Debian container , I stumbled across a file that I didn’t know existed - /etc/os-release - this file contains a bunch of handy variables which can be sourced to chuck them into your environment as environment variables: PRETTY_NAME = "Debian GNU/Linux 12 (bookworm)" NAME = "Debian GNU/Linux" VERSION_ID = "12" VERSION = "12…
I’ve recently started on a new Rails application which will define at least one MCP servers, and potentially 2-3. To support this, I wanted to set up a new namespace in my app/ folder to contain all the MCP ‘stuff’ - I also do the same thing for GraphQL mutations, resolvers, queries etc. Zeitwerk has made autoloading and eager loading much easier in Rails, but this pattern is still something I…
I listened to https://bikeshed.thoughtbot.com/470 in the weekend. There was a fair amount of talking about judoscale, but there was also some interesting stuff in there about measuring queue performance. A couple of key takeaways for me: It seemed like a really good suggestion to me was to name queues by their SLA, rather than their purpose - for example, instead of naming queues “emails”,…
title: Connecting to a locally-running app with Hotwire Native category: TIL – I’ve just started a new Hotwire Native application. The Getting Started docs for Android use a deployed domain name, so while the demo app worked just fine, I had a bit of trouble figuring out how to connect to my locally-running web app from an Android emulator. In my case, it was a Rails app, but any other web…
In Rails controllers, it’s pretty common to redirect somewhere when an action is completed. Sometimes this location is unknown, and therefore straightforward. Other times I just want to redirect ‘back’ - wherever that might have been - redirect_back(fallback_location: wherenver_path) is perfect for that. Occasionally though, I’ll want to determine the next path from the caller - often this will be…
This little technique is a great example of how simple *nix utilities can be (mis)used to accomplish some interesting things: ( echo "./deploy.sh" && cat && exit && exit ) | \ aws ssm start-session --target $INSTANCE_ID This: Echos the command into the shell session once started ( echo "./deploy.sh" - obviously whatever command you want to run goes here) Runs cat to capture any output from that…
I think this is neat - very succinct. This controller expects an input that calls formatOnSubmit on input, which removes invalid chracters, strips the input (e.g. of spaces), and then automatically submits it. Works great with pasting, manually typing, and provides fast feedback to users. import { Controller } from " @hotwired/stimulus " ; export default class extends Controller { static targets =…
I’ve just open sourced a reference implementation of native iOS Google sign-in using Hotwire Native . I developed this flow when building Virtualtrails , where I wanted to support both web and native authentication flows for Google Sign-in. Using Hotwire for this worked great - when native support is available, it’s automatically used. When not, it falls back to a web-based OAuth flow.…
I’ve been working this week on Trove ( Github , Docs ). Trove is meant to be a self-hosted system that can store arbitrary (but schema validated) JSON data, files, and relationships between events. By events I mean things like: Bookmarks Photos I like Colours/patterns I’ve seen out and about that I like Storypark updates (from daycare/school) etc. Basically all the digital miscellanery that I want…
I’m trying out OpenNext with Cloudflare. One kind of annoying thing already I’ve run into is that libraries that use cross-fetch end up running into errors, because OpenNext patches the deployment package enough that cross-fetch tries to use Node libraries, when actually it can just use native fetch. In the case I ran into, I could work around this by performing a native fetch, then processing the…
I tried to get to Eastern Hutt Hut on Friday. Didn’t quite make it, just because I’d committed to a half day rather than full day trip, but it seemed doable. I turned around here with 2km to go, it took me 2 hours to get there after biking over from Kaitoke on the 4WD road - 1 hour biking, just over 1 hour walk/running. It felt like another 1-1.5 hours to the hut in the riverbed or game trails.…
Just a quick tip! If you’re like me, you start with just one or two tabs (or tmux panes), and things get more and more disorganised as the day goes on. If you need to restart your Rails server quickly, and don’t feel like hunting out your Rail server terminal, no worries! Just running bin/rails restart will signal the server process, wherever it’s running, to restart itself. This can also be used…
Note: I discovered this API based on a suggestion from an AI editor assistant, just to disclaim that. When creating an RSpec test against an enumerable, rubocop-rspec will enforce that the all matcher be used instead: it "only returns odd numbers" do odd = [ 1 3 5 ] expect ( odd ). all ( be_odd ) end This is fine for a matcher that doesn’t require any args be passed to it, but what do we do in the…
In a before|after_destroy callback, you can use the destroyed_by_association method to access the association (therefore the parent record or parent record type) that is causing this record to be destroyed. This is also a handy way to tell the difference betwee a ‘direct’ destruction ( widget.destroy ), and a ‘dependent’ destruction has_many :widgets, dependent: :destroy`). I had a use case for…
Several years ago, the Rails core team advised that controller tests/specs were being discouraged, in favour of request specs. The basis for this was that controller tests had more visibility into the internals of the controller, and were not purely testing the HTTP interaction as request specs were. This is valid, but one method I end up missing a lot from controller specs is assigns - as in (…
ActionMailer Previews are a great way to provide some living documentation in the form of rendered emails. They’re also great for testing. Often, in order to generate a preview, you might need to create a database record - perhaps using fixtures, or a tool like factory_bot . When you do this, you might be surprised to see these records then show up and remain in your database. This is because…
Minitest has support for stubbing via Minitest::Mock . This is a good fit for providing fake results to a caller, but what about asserting that a method is actually called? To do this, you can leverage the fact that if the stubbed value is callable, it will be called. This means that you can provide a mock, then expect that the mock will receive call with the expected args (and return the return…
I love gdal, particularly ogr2ogr. Like ffmpeg , and convert , it’s just one of those convert anything to anything commands that is so handy to know about. ogr2ogr is part of the GDAL library, and is particularly suited to converting files containing geospatial data from one source to another. I often use this tool to convert shapefiles into GeoJSON for example, load data into Spatialite or…
Normally I would use Bootstrap’s stretched link behaviour to implement functionality where clicking anywhere on a containing element triggers a link element. This is useful for cards, menus, and other cases where a ‘primary’ link can benefit from a larger tap target. Bootstrap’s stretched link works by adding an ::after pseudo element to the link element with the stretched-link class, then using…
Virtualtrails uses Strada to bridge native iOS code to the web application, including notifications, native Apple + Google Signin and Apple Healthkit integration. I added a feature yesterday to replace a Bootstrap dropdown with a native iOS sheet, just because it’s a more intuitive experience for users. Strada has two parts - the web side (which is in the form of a Stimulus controller with some…
I use both RSpec and Minitest, but prefer Minitest. It’s not quite as feature-rich as RSpec, but I find that I prefer the tests I write in Minitest - they are more Ruby-ish, and aren’t as reliant on RSpec’s DSL to work. I don’t use around callbacks in RSpec very often, but it is something I recently found myself wanting to do in Minitest. Since I couldn’t find a built-in way to achieve this, I…
There’s a bit of a mystery about how relevant favicon.ico files are in 2024. Like it not though, they are the lowest common denominator, so they still have some use. Today I learned about a handy option that can be passed to ImageMagick that will automatically resize a source into a range of common dimensions to be packed into an ico file. Given a source file, say favicon.svg , which is, say, a…
I’m a big fan of Bootstrap, I use it a lot. I’ve used it for many years, and with the recent support for theming with CSS variables, and for their utility API, I’ve found I can have the best of both worlds - a rich and capable library of components, along with an extensive class-based approach for making tweaks to elements and components. One of the neat things about the utility API is that it’s…
This is a tip I re-discover every couple of years, then forget until I find my menu bar getting a bit cluttered up again. Holding down the Command key allows menu icons to be dragged backwards and forwards along the menu bar to order them. This is available for all items other than the control center and date/time, including system items like network, bluetooth and sound.
I’m sure this is all over testing-library documentation, but I haven’t had to do this before, so I think it’s worth a quick post about. If I’m writing a testing-library assertion, every now and then the test won’t pass, because the element I expected to be present, was not. Usually, when this happens, a snippet of the DOM will be output, but will be truncated - sometimes the element that has not…
Stretched links have been a feature of Bootstrap for some time, but it’s quite a useful technique to understand, because the functionality it offers - expanding the tap target of an element, works for any element that can be interacted with with a tap or mouse gesture, not just links. Some common examples include: Buttons Inputs Labels The way that stretch links work is that they define a ::after…
Throughout my career, I’ve always had at least one project on the go outside work - call this a personal, learning, or passion project - it all amounts to the same. I’ve got a lot of benefit out of these projects - sometimes, I approach them deliberately to try out a new technique or tool. Sometimes, I just enjoy the process of starting a new app from scratch. Sometimes, I’ve got an idea to…
Just a quick little way to reformat or otherwise transform a value to be used as the label in a select, radio button, or checkbox collection group in a Rails form: Given I have an enum defined like so: class User enum :distance_unit , { mi: "mi" , km: "km" } end And translations defined like so: en : distance_units : km : Kilometer (Metric) mi : Mile (Imperial) Then I can pass a short proc as the…
Bad (but not obviously so): data = { greeting: "Hello, world!" } ERB . new ( " <h1>Show greeting:</h1> <p><%= greeting %></p> " ). result_with_hash ( data ) # => <h1>Show greeting:</h1> <p>Hello, world!</p> The question is - what happens if greeting is something different…like <script>alert('greeting!')</script> ? Perhaps, if you’re used to Rails, not what you might expect: data = { greeting:…
For quite a while, I managed to find something interesting to write up each work day. Unfortunately, life got in the way, and as is easy to do, this habit stopped. I’m hoping to get back into writing, and I’m trying to do this by making it easier to write up a post without leaving my flow as much. To help with this, I set up this shell script: #!/bin/bash blog-post () { # Check if title argument…
I don’t know if this is interesting to anyone, but on Friday and for an extra hour or two in the weekend, I was playing around with calculating an elevation profile for walking tracks in NZ. I started with a dataset from data.govt.nz from the Department of Conservation (which for AU is responsible for walking tracks and facilities at a national level - basically all the national parks, and big…
TIL that aria- attributes get the same treatment in Rails tag helpers as data- . That is, you can do: link_to "X", y_path, aria: { pressed: true } -> aria-pressed="true" OR link_to "X", y_path, "aria-pressed" => true -> aria-pressed="true" I can’t find any multi-dash aria properties on MDN, but I imagine Rails will follow the same formatting rules of turning underscores to dashes, e.g. link_to…
Trix is fairly new to me, but now that it’s closely integrated with Rails, it’s become by go-to for rich text editing. I’ve been working on a project where I wanted a text field with some basic rich text capabilities, but I wanted the input to look like a normal textarea. This would allow standard text entry, but allow users to use keyboard shortcuts to specify bold, italic and underlined text. I…
View specs are great . While I always keep in mind they are a unit test and do not test the full integration of a feature, I find that they are fantastic for asserting around all the edge cases of a feature - things like the expected validation messages showing up, or buttons appearing or disappearing based on different scenarios. I used to use system tests for this kind of thing, and find that…
entr is a tool I’ve been using a bunch recently - mostly for watching source files, and running tests when I change the source file. entr can be piped one or more files, which means it’s really easy to use - just use ls , find , or any other tool that outputs filenames, and then pipe it to entr: ls app/models/widget.rb | entr bundle exec rspec spec/models/widget_spec.rb ls app/models/widget.rb…
I have a React component that is part of a wider Ruby on Rails application. It fetches a record, and if the record does not exist, it redirects to the server-rendered 404 page. Unfortunately, while the user will see the ‘not found’ page, the HTTP status code will be 200 OK by default, because this page is static. Best practise would be to define a location block in your web server (Nginx, Apache,…
This trip report is really more a cautionary tale of overconfidence and inexperience leading to a challenging Tararua overnight trip to Mid King Biv . Started: 11am from Holdsworth Carpark Finished: 6:30pm the next day at Holdsworth Carpark Travel Times: Day 1: 2 hours to Atiwhakatu Hut (break) 1 hour from Atiwhakatu Hut to the swingbridge over Atiwhakatu Stream 20 minutes to Baldy turnoff 1 hr 10…
Often, mailer specs will start with a simple assertion like expect(mail.body).to include "Some text in the expected email here" . This works for a while, but quickly breaks down when an email grows more complex, such as having special characters causing it to be encoded as 7bit or QuotedPrintable, having attachments, or supporting different formats (like plaintext & HTML). I’ve always tried to do…
I recently created a Presenter object to wrap around an ActiveStorage::Blob that I wanted to decorate with some presentation methods for things like a human file size and content type. The initialisation signature for my presenter looks like this: class MyPresenter < SimpleDelegator def initialize ( obj , view_context ) super ( obj ) @view_context = view_context end def human_file_size…