Beware: Many browsers define window.event

Some browsers define window.event, which will return a copy of the "current" event. However, this is not defined by the W3C. Most importantly, Firefox does not support it, neither do recent versions of IE.

You should completely avoid accessing the global event and instead pass the event object around that the event handler receives. The easiest protection against accidentally accessing window.event is just never naming the event event, but e or evnt or so.

function brokenInFirefox() {
  event.preventDefault(); // Break...

How to split up a git commit

Quick steps

  1. git rebase -i -> mark your commit with edit
  2. git reset HEAD~ (remove the marked commit, but keep its changes)
  3. Make several commits (optionally setting the previous author manually)
  4. git rebase --continue

Detailed instructions

Basically, you will review the last n commits and stop at the splittable commit. Then you'll undo that commit and put its changes into new commits at your liking.

  1. Review commits (rebase)

    git rebase -i HEAD~3
    # or
    git rebase -i origin/master
    

    ...

How to explain SQL statements via ActiveRecord

ActiveRecord offers an explain method similar to using EXPLAIN SQL statements on the database.

However, this approach will explain all queries for the given scope which may include joins or includes.

Output will resemble your database's EXPLAIN style. For example, it looks like this on MySQL:

User.where(id: 1).includes(:articles).explain
EXPLAIN for: SELECT `users`.* FROM `users`  WHERE `users`.`id` = 1
+----+-------------+-------+-------+---------------+
| id | select_type | table | type  | possible_keys |
+----+-----...

Always disable autocomplete for date pickers

When we write a form with date fields, we often use graphical data picker like Rome to get a consistent calendar popup on all browsers.

When you integrate a date picker popup, remember to also set autocomplete="off" on the text input that opens the calendar on click. Otherwise the autocomplete suggestions will cover the calendar box and make it unusable:

Image

If you are using a tool like Unpoly you might want to set autocomplete="off" i...

Cancelling event propagation

Within an event handler, there are multiple methods to cancel event propagation, each with different semantics.

  • event.preventDefault()

    Only prevents the default browser behavior for the click, i.e. going to a different url or submitting a form.

    When invoked on a touchstart event, this also prevents mouse events like click to be triggered.

  • event.stopPropagation()

    Prevents the event from bubbling up the DOM.

  • `event.st...

Controlling how your website appears on social media feeds

When a user shares your content, a snippet with title, image, link and description appears in her timeline. By default social networks will use the window title, the first image, the current URL and some random text snippet for this purpose. This is often not what you want.

Luckily Facebook, Twitter, etc. lets you control how your content appears in the activity streams. They even have agreed on a common format to do this: OpenGraph <meta> tags that go into your HTML's <head>:

<meta property="og:url" content="http://start.m...

How to bulk-unwatch many repositories on Github

To easily opt out of notifications for a large number of Github repositories, go to https://github.com/watching.

Defining custom RSpec matchers

There are three ways to define your own RSpec matchers, with increasing complexibility and options:

1) Use RSpec::Matchers.define

RSpec::Matchers.define :be_a_multiple_of do |expected|
  match do |actual|
    actual % expected == 0
  end
  
  # optional
  failure_message do |actual|
    "expected that #{actual} would be a multiple of #{expected}"
  end
  
  # optional
  failure_message_when_negated do |actual|
    "expected that #{actual} would not be a multiple of #{expected}"
  end
end
  • This is automatically available i...

Your database tables should always have timestamps

Whenever you create a table from a database migration, remember to add updated_at and created_at timestamps to that table. Without those timestamps, investigating future bug reports will be hell. Always have timestamps.

Adding timestamps to new tables

When you create a table using create_table, you can add timestamps by using the timestamps shortcut:

class CreateEpisode < ActiveRecord::Migration
  def change
    create_table :episodes do |t|
      t.string :name
      t.timestam...

Rubocop fails with undefined StringIO

If your rubocop run fails with a stack like

rubocop-1.61.0/lib/rubocop/server/socket_reader.rb:36:in `ensure in read!': undefined method `string' for nil:NilClass (NoMethodError)
        Cache.stderr_path.write(stderr.string)
                                      ^^^^^^^
...
rubocop-1.61.0/lib/rubocop/server/socket_reader.rb:27:in `read!': uninitialized constant RuboCop::Server::SocketReader::StringIO (NameError)

        stderr = StringIO.new
                 ^^^^^^^^

this card might help you.

In old versions of rubocop stringio w...

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.

Exploiting the capturing phase

Before an event bubbles up from an element to the document, it...

How to accept more than 4k query parameters in Rails

I have a form with a dynamic number of fields. Submitting it worked fine until I tried out a very large version of it. The development log was not very helpful:

Invalid or incomplete POST params

As it turned out, the following exception did not reach the log output

Rack::QueryParser::QueryLimitError
Error Message: total number of query parameters (6313) exceeds limit (4096)

If you ever happen to be in the same position, this is how to increase the limit of allowed query parameters:

# config/initializers/rack_query_parser.rb
...

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 }, duration: 10.seconds)
end

The problem

# This doesn't actually test concurrency control
test('enqueues both jobs') do
  MembershipJob.perform_later(membership)
  MembershipJob.perform_later(membership)
  
  assert_enqueued_jobs(2, only: MembershipJob)
  # Both...

Generating test images on the fly via JavaScript or Ruby

When you need test images, instead of using services like lorempixel or placehold.it you may generate test images yourself.

Here we build a simple SVG image and wrap it into a data: URI. All browsers support SVG, and you can easily adjust it yourself.
Simply set it as an image's src attribute.

JavaScript

Simple solution in modern JavaScript, e.g. for use in the client's browser:

function svgUri(text) {
  let svg = `
    <svg wid...

Use a global .gitignore file to ignore stuff from your machine

Sometimes you want git to ignore certain files that appear on your machine. You can do this in 3 ways:

  • Per project, in the project's .gitignore file
  • Per project, in a local exclude file
  • Globally on your machine

Downsides of per-project .gitignore entries

While it might be tempting to set it per project (other devs might benefit from it), you

  • need to do it each time for every project
  • "pollute" a project's .gitignore file with stuff...

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: none still allows keyboard input, and does not indicate disabledness visually, or to screen readers.

Ye...

Updated: Learn how to use ruby/debug

Added a section about remote debugging.

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

Project management best practices: Standup

If the project team consists of at least 2 members, do a daily standup. It should not take much longer than 15 minutes.

Format

Tell everyone else

  • what you did yesterday
  • what you intend to do today
  • where you might need help or other input
  • if there are new developments everyone needs to know about

A "still working on X, will probably be done today" is totally fine. No need to tell a long story.

If you are out of work, find a new story with the others.

If there are new stories in the backlog, look at them and

  • make sure ev...

Rails disables CSRF protection in tests

The default configuration of Rails disables CSRF protection in tests. If you accidentally forget to send the CSRF token for POST requests, your tests will be green even though your application is broken.

You probably want to enable CSRF protection in tests that can speak JavaScript.

For RSpec...

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