
One thing I really dig about the AI/LLM boom is that it’s the first time since the 90s that otherwise "non-technical" people in my life have had any reason at all to get excited about computer-ass computing.
Seeing friends and family get interested in how ChatGPT works while improving at how they use it is way more gratifying than the last 15 years of the industry merely training the world how to doomscroll.

I wrote this over the weekend. Whether you use Stimulus or just like reading discussions of nuanced code organization and design trade-offs, I hope you’ll enjoy it justin.searls.co/posts/a-decoupled-approach-to-relaying-events-between-stimulus-controllers/
A decoupled approach to relaying events between Stimulus controllers
Part of the allure of Stimulus is that you can attach rich, dynamic behavior to the DOM without building out a long-lived stateful application in the browser.
The pitch is that each controller is an island unto itself, with each adding a particular kind of behavior (e.g. a controller for copying to clipboard, another for displaying upload status, another for drag-and-drop reordering), configured entirely via data attributes. This works really well when user behavior directly initiates all of the behaviors a Stimulus controller needs to implement.
This works markedly less well when a controller's behavior needs to be triggered by another controller.

If I were the judge tasked with breaking up Google, I would simply split it down the middle: with "Goo" getting search results starting with A-M and "gle" getting N-Z nytimes.com/2024/08/13/technology/google-monopoly-antitrust-justice-department.html
Well, would you look at that

1Password has gone from one of my favorite apps to the one I cannot wait to get rid of gist.github.com/searls/7ba37ceb824321e5bd0a89c61bee049f

resets_session_but_its_ still_thursday_i_need _these.rb gist.github.com/searls/d3f5c90bf6310f5eafa5d59a2c61eff0
Boy, have I got a great show for you today! I guess. You be the judge.
If you listen to the podcast, please rate it on a scale from Good Charlotte to My Chemical Romance at podcast@searls.co.
The following links are included with your subscription:
The new Mac mini will be the first major design change to the machine since 2010, making it Apple's smallest ever desktop computer. The new Mac mini will apparently approach the size of an Apple TV, but it may be slightly taller than the current model, which is 1.4 inches high. It will continue to feature an aluminum shell. Individuals working on the new device apparently say that it is "essentially an iPad Pro in a small box."
I can't be the only person thinking "I wonder if I could plug this into a portable USB power bank, throw it in my bag, and then use run Mac Virtual Display on my Vision Pro without needing to carry a laptop… can I?
Spot the N+1
In general, it's great that you can call array and enumerable methods on relations in Active Record, but the way that's implemented (cleverly querying on the relation instead of considering if to_a
and doing the operation in memory would be more performant) is maddening at times.
Something must be done about the skyrocketing cost of college
What one must pass to includes() to include Active Storage attachments
If you're using Active Storage, eager-loading nested associations that contain attachments in order to avoid the "N + 1" query problem can quickly reach the point of absurdity.
Working on the app for Becky's strength-training business, I got curious about how large the array of hashes being sent to the call to includes() is whenever the overall strength-training program is loaded by the server. (This only happens on a few pages, like the program overview page, which genuinely does contain a boatload of information and images).
Each symbol below refers to a reference from one table to another. Every one
that descends from :file_attachment
is a reference to one of the tables
managed by Active Storage for
keeping track of cloud-hosted images and videos. Those hashes were extracted
from the
with_all_variant_records scope that Rails provides.
I mean, look at this:
[{:overview_video=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob}, :preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
{:overview_thumbnail=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob}, :preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
{:warmup_movement=>
{:movement_video=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob}, :preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}},
:movement_preview=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob}, :preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}}},
{:workouts=>
{:blocks=>
{:mobility_movement=>
[{:primary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:secondary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:tertiary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:movement_video=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}},
:movement_preview=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}}],
:exercises=>
{:exercise_options=>
{:movement=>
[{:primary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:secondary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:tertiary_equipment=>
{:equipment_image=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}},
:movement_video=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}},
:movement_preview=>
{:file_attachment=>
{:blob=>
{:variant_records=>{:image_attachment=>:blob},
:preview_image_attachment=>{:blob=>{:variant_records=>{:image_attachment=>:blob}}}}}}}]}}}}}]
By my count, that's 167 relationships! Of course, in practice it's not quite this bad since the vast majority are repeated, and as a result this winds up executing "only" 50 queries or so. But that's… a lot!
Broadcasting real-time database changes on a budget
While building Becky's (yet unreleased) app for her strength training business, I've been taking liberal advantage of the Hotwire combo of Turbo and Stimulus to facilitate dynamic frontend behavior without resorting to writing separate server-side and client-side apps. You can technically use these without Rails, but let's be honest: few people do.
Here are a few capabilities this broader suite of libraries give you, in case you're not familiar:
- Rails Request.js offers a minimal
API for sending conventional HTTP requests from JavaScript with the headers Rails
expects like
X-CSRF-Token
handled for you - Turbo streams can send just a snippet of HTML over the wire (a fetch/XHR or an Action Cable socket) to re-render part of a page, and support was recently added for Custom Actions that sorta let you send anything you want over the wire
- The turbo-rails gem adds some very handy glue code to broadcast model updates in your database and render Turbo streams to subscribers via an Action Cable socket connection
- Stimulus values are synced with the DOM as data attributes on the owning controller's element, and Object serialization is supported (as one might guess) via JSON serialization. Stimulus controllers, by design, don't do much but they do watch the DOM for changes to their values' data attributes
Is your head spinning yet? Good.