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

Rails: Join model table migration template

When creating a database table for a join model without further importance, you can use Rails' create_join_table:

class CreateSchoolsStudents < ActiveRecord::Migration[7.2]
  def change
    create_join_table :schools, :students, column_options: { foreign_key: true } do |t|
      t.index [:student_id, :school_id], unique: true
    end
  end
end

This will create a table without an id column and without timestamps. It will have school_id and student_id columns with null: false constraints and indexes.

  • null: false pr...

Plain CSS does not support variables in media queries (yet)

It's not possible to use variables in media queries with plain CSS.

@media (max-width: var(--some-pixel-size)) { ... } /* Does not work */

Postcss Plugin

If you're using PostCSS to postprocess your CSS, you can configure postcss-custom-media and add this feature:

@custom-media --small-variable-name(max-width: 968px);

@media (--small-variable-name) { ... } /* works */

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

Fix PNG colors in IE, old Safaris and new Firefoxes

Some browsers render PNG images with color profiles and other shenanigans, some don't.

The cleanest way to have consistent colors across browsers is to convert all your images to a standard color profile, strip the image's original profile and attach the standard profile.

If you can't be bothered to convert color profiles, a quicker (but less effective) method is to remove some PNG chunks from your files.

With Geordi

[Geordi](https://git...

Git: Show commits that have touched specific text in a file

If you want to find the commits that touched a specific text in a file, use

git log -S 'text in the code' -- path/to/file

If you use tig you may run a similar command to get a navigatable list of affected files:

tig -S 'text in the code'

Example

Here is an example, where the move of the convert_number_column_value(value) method in active record is traced (simplified output):

git log -n 1 --pretty=oneline -S 'convert_number_column_value(value)' -- activerecord/lib/active_record/base.rb
ceb33f84933639d3b...

RSpec: How to aggregate failures

RSpec >= 3.3 added aggregate_failures, which allows multiple failures in an example and list them all, rather than aborting on the first failure.

This can be used:

  • In the global configuration
  • With the tag :aggregate_failures (our preferred option in case every expectations should be aggregated)
  • With the method aggregate_failures

[Here](https://web.archive.org/web/20210110131654/https://relishapp.com/rspec/rspec-core/docs/expec...

Automated "git bisect" will make your day

So you're hunting down a regression (or just a bug) and want to use git bisect to find out when it was introduced? Smart kid.
If you have a shell command ready to reveal if your current state is good or bad, you can have git do most of the work for you.

Using git bisect run <your command> you can tell git that your command will reveal the issue; git on the other hand will use the return value of that call to decide if the state is good or bad.
...

Webpack(er): A primer

webpack is a very powerful asset bundler written in node.js to bundle (ES6) JavaScript modules, stylesheets, images, and other assets for consumption in browsers.

Webpacker is a wrapper around webpack that handles integration with Rails.

This is a short introduction.

Installation

If you haven't already, you need to install node.js and Yarn.

Then, put

gem 'webpacker', '~> 4.x' # check if 4.x is still cu...

Ruby: Using named groups in Regex

An alternative of using a multiple assignment for a Regex are named groups. Especially when your Regex becomes more complicates it is easier to understand and to process.

Note:

  • In case a string does not match the pattern, .match will return nil.
  • With Ruby 2.4 the result of .match can be transformed to a Hash with named_captures. This allows you to use methods like slice or fetch on the result.

Example with a mult...

Tint and Shade Generator

A simple web tool for generating lighter (tints) and darker (shades) versions of any HEX color value.

Tip

When using oklch() colors, you can create tints and shades by setting the l (lightness) component to a value between 0% and 100%.

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:

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

RubyMine: Find and Replace with Regex (Capture Groups and Backreferences)

tl;dr

In RubyMine you can use find and replace with capture groups (.*?) and backreferences $1 (if you have several groups: $[Capture-Group ID]).
Named captures (?<text>.*) are also supported.

Examples

Replace double quotes with single quotes

If you want to replace double quotes with single quotes, replacing every " with a ' is prone to errors. Regular expressions can help you out here.

  1. Open find and replace
  2. Activate the regex mode (click on the .* icon next to the "find" field).
  3. Fill in f...

Avoid the FLOAT type for database columns

Like in any language, a FLOAT has complicated semantics for precision. This sometimes causes stored numbers to be slightly off their original value.

Consider using DECIMAL instead. Decimals have well-defined behavior for rounding and range overflows.

Using feature flags to stabilize flaky E2E tests

A flaky test is a test that is often green, but sometimes red. It may only fail on some PCs, or only when the entire test suite is run.

There are many causes for flaky tests. This card focuses on a specific class of feature with heavy side effects, mostly on on the UI. Features like the following can amplify your flakiness issues by unexpectedly changing elements, causing excessive requests or other timing issues:

  • Lazy loading images
  • Autocomplete in search f...

Migrating from rbenv / nvm to mise

Install mise

Follow the installation guidelines at https://mise.jdx.dev/getting-started.html.

Remove rbenv configuration

Search for rbenv config in .bashrc and .profile and remove it:

eval "$(rbenv init - bash)"

Search for rbenv config in .profile and remove it:

source /home/$user/.rbenvrc

Remove nvm configuration

Search for nvm config in .bashrc and remove it:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_...

How to inspect Rails view cache keys (when using Redis)

When your Rails application is using Redis as its cache store, this is how you can list existing keys:

  1. Check: Rails.cache should return an ActiveSupport::Cache::RedisCacheStore.
  2. Rails.cache.redis.with(&:keys) lists existing keys. Cached views start with "views/".
    Caution! The list of keys may be huge in production.

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.jsonprojects.{app}.architect.test.options.main: <test entry file> and [then initialize th...

Checklist: Using Carrierwave in a Rails project

This checklist should help you to check edge cases that are not part of the default Carrierwave configuration.

Ruby: Do not rescue without specifying exception classes

When you are calling a method that may raise an exception that you don't care about, you might think of doing something like the following.

@user = User.something(123) rescue User.new # DON'T

or

@user = begin
  User.something(123)
rescue # DON'T
  User.new
end

This is bad. Do not do that.

You will be rescuing StandardError and all its subclasses, like NameError -- meaning that e.g. a typo in your code won't raise an error...

Jasmine: Cleaning up the DOM after each test

Jasmine specs that work with DOM elements often leave elements in the DOM after they're done. This will leak test-local DOM state to subsequent tests.

For example, this test creates a <spoiler-text> element, runs some expectations, and then forgets to remove it from the DOM:

describe('<spoiler-text>', function() {
  it ('hides the secret until clicked', function() {
    let element = document.createElement('spoiler-text')
    element.secret = 'The butler did it'
    document.body.appendChild(element)
 ...

File System Access API: Recursive Directory Traversal

The File System Access API is a new capability of modern browsers that allows us to iterate over selected folders and files on a user's machine. Browser support is not great yet, but if the feature is only relevant for e.g. a single admin user it could still be worth using it prior to wider adaption instead of building yet another ZIP upload form.

Below is a simple compiler that i used to evaluate this feature.

!...