CSP: Nonces are propagated to imports
When you load a <script> with a nonce, that script can await import() additional sources from any hostname. The nonce is propagated automatically for the one purpose of importing more scripts.
This is not related to strict-dynamic, which propagates nonces for any propose not limited to imports (e.g. inserting <script> elements).
Example
We have a restrictive CSP that only allows nonces:
Content-Security-Policy: default-src 'none'; script-src 'nonce-secret123'
Our HTML loads script.js using that nonce:
Caching file properties with ActiveStorage Analyzers
When working with file uploads, we sometimes need to process intrinsic properties like the page count or page dimensions of PDF files. Retrieving those properties requires us to download (from S3 or GlusterFS) and parse the file, which is slow and resource-intensive.
Active Storage provides the metadata column on ActiveStorage::Blob to cache these values. You can either populate this column with ad-hoc metadata caching or with custom Analyzers.
Attachment...
Sending newsletters via rapidmail with SMTP and one-click unsubscribe
If you need to implement newsletter sending, rapidmail is a solid option.
Support is very fast, friendly and helpful, and the initial setup is simple. Since rapidmail works via SMTP, you can simply ask the Ops team to configure SMTP credentials for your application.
You also do not need to use rapidmail’s built-in newsletter feature. Instead, you can send emails as transactional mails, which allows you to keep the entire newsletter logic inside your application.
One thing to keep an ey...
Postgres Ranges
Postgres supports multiple built-in range datatypes:
int4rangeint8rangenumrange-
tsrange(range with timestamp without timezone) -
tstzrange(range with timestamp with timezone) daterange
They represent a start and endpoint of something in a single column. Image you're building a vacation booking feature:
create_table :events do |t|
t.date :starts_on
t.date :ends_on
end
This is how you would use a range type in a migration:
create_table :vacations do |t|
t.daterange :period
end
See [t...
Locally testing a website on its real domain
In rare circumstances, you want to use a websites full domain (say https://mywebsite.com) while testing in dev mode. This can be useful if you need to test third party integrations like embeds, cookie banners, Google Tag Manger or other integrations that allowlist your actual domain.
To achieve this, we will have to
- make our system resolve
mywebsite.comtolocalhost, - run a reverse proxy on
localhost:443with a (locally signed and accepted) SSL certificate formywebsite.com, - forward the traffic to our actual dev server.
On l...
TypeScript: Get in Shape with Structural Typing
TypeScript basically uses structural typing, which is conceptually quite similar to duck typing, but with static compile-time type checking. We'll explore what this means in practice.
TypeScript is a superset of JavaScript, meaning TypeScript compiles down to native JavaScript syntax and checks type consistency only at compile time.
Idea of Structural Typing
TypeScript only wants to know whether the shapes of two objects are identical:
interface Point2D {
x: number;
y: number;
}
function printPoint(p: Point2D) {
c...
RSpec: How to retry examples
In some projects we have issues with flaky tests. The best default is to fix them all. But in some cases it might be a pragmatic way to retry them for a limit number of times.
Notes:
- This setup was tested with a CI Pipeline that uses knapsack for parallel test balacing
- The official docs recommend to use rspec-retry, but we preferred the maintained fork rspec-rebound
**...
ActiveRecord: Cleaning up your database with ignored_colums
Leaving old unused DB columns around after a migration is confusing for other developers. However, dropping columns too eagerly might also cause problems and extra work. If you want to mark columns for future deletion, but you are unsure, whether you can simply drop them right now, use these tools:
Add a comment to your DB schema
With schema comments you can add a comment like LEGACY as of yyyy-mm-dd to your DB schema.
Ignore the column
With [...
Bash functions to provide repository context for LLM chats
I use the Gemini web chat interface quite extensively. One thing that is tedious is giving it all the context it needs to do a proper job. Context engineering is not an easy task, but on the other hand we now have context limits of ~1 million token, which allows us to just dump in everything we have in many cases. And when we do that in the web interface, we can avoid extra costs that would be charged when using the API!
The functions below pack your current work (diffs, full repos, or specific commits) into XML/Diff files, which are then ...
Knapsack: Rerun a flaky test locally
Knapsack allows you to rerun a specific job locally. This is helpful to run specs in the exactly same order like in the CI.
Example for running rspec 3/8 with a seed output of 20689:
CI_NODE_INDEX=2 CI_NODE_TOTAL=8 bundle exec rake "knapsack:rspec[--seed=20689]"
*Note: the environment variable ...
Controlling the order of DOM event listeners
Event listeners are called in the order of their registration:
button.addEventListener('click', () => console.log("I run first"))
button.addEventListener('click', () => console.log("I run second"))
Sometimes you want a listener to always run first (or last), but have no control over the order in which other listeners are registered.
There is no clean mechanism in the DOM API for this. This card shows some hacks to do it anyway.
Find conflicting listeners
You can see the order of registered DOM event listeners usi...
Testing ActiveJob `limits_concurrency` with Solid Queue
The :test adapter doesn't respect limits_concurrency configuration. Switch to :solid_queue adapter in your test to verify blocking behavior.
Job Configuration
class MembershipJob < ApplicationJob
limits_concurrency(key: ->(membership) { membership })
end
The problem
When using the default test mode for enqueuing jobs, both will be enqueued immediately. However, we actually we want to test that only one of both will be enqueued as the other should have been blocked.
# This doesn't actually test concurren...
Opt out of selenium manager's telemetry
If you use the selenium-webdriver gem, it will sneakily phone home once every hour whenever you run a browser based feature spec.
Check if you're affected
Check if ~/.cache/selenium/se-metadata.json exists. (It contains a "ttl" timestamp of its last/next anaytics call. You can parse it with Ruby's Time.at.)
Opt out
You can opt out either globally:
# .bashrc
export SE_AVOID_STATS=true
or project based
# spec_helper...
Cucumber CI job quirks
Most of our CI pipelines don't use the --retry flag for Cucumber and instead build their own retry via the tmp/failing_features.txt file.
Benefits:
- It's possible to only use
-f prettyfor the rerun.
Drawbacks:
- MAJOR: With our current setup, when the main run fails without writing a
tmp/failing_features.txt(e.g. due to a syntax error), the CI job will pass - MINOR: With our current setup, we lose the test coverage of the main run
A fix for the passing CI despite syntax error could look like this:
cucumber:
# ...
sc...
Debugging Rails Active Jobs with the Vanilla Adapters
Short reference on how to quickly debug the vanilla Rails job adapters.
Queue Adapters by Environment
| Environment | Adapter | Jobs Run In | Worker Needed? |
|---|---|---|---|
| development | :async |
Rails server process | No |
| test | :test |
Not executed (stored) | No |
| production | :solid_queue |
Separate worker | Yes (bin/jobs) |
Development (:async)
Jobs run in background threads ([Concurrent Ruby ThreadPoolExecutor](https://ruby-concurrency.github.io/concurrent-ruby/maste...
pnpm: How to update a single package conservatively
I recently had to update a few selective npm libraries in a project that uses pnpm to apply a CVE mitigation. My first instinct was to modify the package.json file and hope that pnpm install only makes the most minimal changes. But that's not always the case, and there is a much better way.
Use pnpm up (aliased to update and upgrade) with a set of exact library names and versions. The resulting changes (both to the package.json and pnpm lockfile) will be minimal. Example for my CVE-2025-66478 fix:
pn...
How to iterate over all ActiveRecord models
Sometimes you have a maintenance script where you want to iterate over all ActiveRecord models. Rails provides this out of the box:
# script/maintenance_task.rb
# Load all models eagerly, otherwise you might only get a subset
Rails.application.eager_load!
ApplicationRecord.descendants.select(&:table_exists?).each do |model|
# ...
end
Caution
If you iterate over individual records, please provide a progress indicator: See [https://makandracards.com/makandra/625369-upload-run-scripts-production](https://makandracards.com/...
Working with lists of DOM elements in JavaScript
When you query the browser for DOM elements, there are some footguns you should know about.
Some lists are synchronized with the DOM
Some DOM APIs return live lists that automagically update their contents as the underlying DOM is manipulated.
Example
Let's assume we have two <div> elements:
<div id="one"></div>
<div id="two"></div>
We have multiple ways to retrieve a list of these elements. At first glance, they all look the same:
let liveList = element.getElementsByTagName('div')
let nonLiveList = docum...
Git: Finding changes in ALL commits
Finding changes
When you're looking for a specific change in Git, there are multiple axes you can choose:
-
git log -- path/to/filelists all commits that touch a file -
git log -G some_stringlists all commits where the diff contains "some_string"
Note that you can do most of these things with Git tools as well, e.g. tig path/to/file.
Considering ALL commits
By default, only the current branch (HEAD) is searched. To search across the entire local repository, add these options:
-
--all: Search all known refs (branches...
Making minimal updates to DOM trees using morphdom / idiomorph
When you replace parts of the DOM with new HTML, using .innerHTML = newHtml is usually the simplest and fastest option. It comes at the price of your DOM elements losing state, like input values, scroll position, progress in a video player, or even more complex state for custom elements.
One option to avoid this are libraries like morphdom (as used by Phoenix Liveviews) or idiomorph (as used by Rails' Turbo).
It lets you write
morphdo...
Custom Angular Test Bootstrap
Compatibility: Angular 20+ with Jasmine 5.x and Karma 6.x
As a default Angular CLI auto-generates test bootstrap via angular:test-bed-init via injecting it as a dynamic virtual module.
Custom Bootstrap
Override the main test option in angular.json → projects.{app}.architect.test.options.main: <test entry file> and [then initialize th...
Faux-disabled fields in HTML forms
You want to prevent input to a form field, but all the solutions have side effects:
- The
[readonly]attribute is only available for text fields, but not for checkboxes, selects, buttons, etc. - The
[disabled]attribute is available for all kinds of fields, but will no longer include the field value in the submitted form data. -
pointer-events: nonestill allows keyboard input, and does not indicate disabledness visually, or to screen readers.
Ye...
Using a virtual column for trigram indexes in PostgreSQL
Full-text search can reach its limits in terms of flexibility and performance. In such cases, trigram indexes (pg_trgm) offer a lightweight alternative.
You can base the index on a virtual column that combines multiple text attributes. A stored virtual column stores the result of an expression as if it were a real column. It is automatically updated when the source columns change and can be indexed like normal data. This keeps your query logic consistent and avoids repeating string concatenation in every search.
def searc...
Unpoly: Passing Data to Compilers
Quick reference for passing data from Rails to JavaScript via Unpoly compilers.
Haml Attribute Syntax
# Ising hash rockets and string symbols (method calls)
= form.text_field :name, 'date-picker': true
# Curly braces or brackets (elements)
%div.container{ id: 'main', 'data-value': '123' }
Simple Values: data-* Attributes
Use for: Scalar values (IDs, strings, booleans)
%span.user{ 'data-age': '18', 'data-first-name': 'Bob' }
up.compiler('.user', (element, data) => {
console.log(...