RSSAmplifier

Blog

Notes: Josh McArthur

I'm Josh McArthur, a web developer specializing in Ruby on Rails and Phoenix. I write posts about technology and hiking.

joshmcarthur.comRSS feed ↗223 posts

Latest posts

Give coding agents a tmux session with your real shell environment

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…

Building resilient personal apps

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.…

How I publish Standard.site records from a Rails Bluesky delivery channel

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…

Re-enable SVG after Rails blocks untrusted libvips loaders

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…

When VPN DNS works in dig but not in curl on macOS

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…

Bind XPath variables in Nokogiri instead of string interpolation

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…

A generic Stimulus controller for toggling fieldsets

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…

Handle Cloudflare Challenge pages when making Turbo requests

How to handle Cloudflare Challenge pages when making Turbo requests

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…

How to test new Github Actions workflows

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…

Talk on Plain Language Law at the AI Engineers meetup in Wellington, NZ

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…

Handy access to OS environment variables on Debian

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…

How to set up Rails directories for MCP servers

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…

Bikeshed #470: All about Queues

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”,…

Connecting to a locally Running app with hotwire native

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…

A simple redirect to next pattern for Rails

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…

How to run a command via AWS SSM with live output

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…

A Stimulus controller to auto-submit authentication codes

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 =…

Open-sourcing a reference implementation of native iOS Google sign-in flow with Hotwire

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.…

About Trove Project

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…

OpenNext on Cloudflare

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…

Hutt Forks/Eastern Hutt Hut

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.…

Restart rails quickly with bin/rails restart

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…

Check that an enumerable satisfies matchers with args in RSpec

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…

Detecting the origin of an ActiveRecord record's destruction

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…

How to use assigns() in a Rails request spec without a dependency

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 (…

Generating ActionMailer Previews inside a transaction

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…

Asserting method calls with Minitest

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…

Converting geofiles to WKT using ogr2ogr

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…

Tapping targets with Stimulus

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…

iOS Action Sheet with Strada

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…

Implementing an around callback with Minitest using Ruby

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…

Auto-resizing images for .ico files using ImageMagick

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…

Generating responsive utility classes with Bootstrap

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…

How to rearrange menu bar icons in Mac OS

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.

Logging the DOM state of an element with testing-library

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…

Stretching interactive elements

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…

Passion projects

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…

Procs can be passed to Rails' collection form helpers

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…

Avoiding script injection when rendering ERB

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:…

Streamlining daily blog posts

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…

Adding elevation to DOC track dataset

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…

Rails supports transforming aria hashes into dasherized attributes

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…

Create a trix editor with a hidden toolbar

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…

Asserting a partial was rendered in an RSpec view spec

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…

CLI tool entr for running a command when a file changes

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…

Assert window.location properties with Jest and jsdom

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,…

Mid King Biv Trip Report

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…

Quickly access Ruby Mail object parts

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…

Access ActionView context in an RSpec test

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…