The numericality validator does not care about your BigDecimal precision

Looking at the source code of the validates_numericality_of validator, it becomes clear that it converts the attribute in question to either an integer or float:

if configuration[:only_integer]
  unless raw_value.to_s =~ /\A[+-]?\d+\Z/
    record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
    next
  end
  raw_value = raw_value.to_i
else
 begin
    raw_value = Kernel.Float(raw_val...

What edge_rider offers you

edge_rider is Power tools for ActiveRecord relations (scopes). Please note that some of the functions edge_rider provides have native implementations in newer rails versions.

Useful in applications

Relation#traverse_association(*names)

Edge Rider gives your relations a method #traverse_association which returns a new relation by "pivoting" around a named association. You can traverse multiple associations in a single call. E.g. to turn a relation of posts into a relation of all posts o...

Git: Advisory for cherry-picks to production branches

We often have a separate production branch that lags a bit behind the more cutting edge main branch. Sometimes you want to move some, but not all commits from main to production. This can be done with a git cherry-pick.

However, this may lead to considerable pain later, since git does not understand the commits are actually "the same". Hazards are unnecessary and hard to resolve conflicts as well as incorrect auto-merges.

In order to avoid this, always merge the production branch back to the main after the cherry-pick. Even t...

Be careful when using buttons without a "type" attribute

Be careful when using buttons without a type attribute, since browsers will consider them the default submit button of a form.

Suppose you have this form:

<form action="/save">
  <input type="text" />
  <button onclick="alert('Alert!')">Alert</button>
  <button type="submit">Save</button>
</form>

If you press the enter key inside in the text input, browsers will trigger the first button and show the alert.

To fix this, add a type="button" attribute to the first button.

We have deprecated Rack::SteadyETag

Rack::SteadyETag was a Rack middleware that generates the same default ETag for responses that only differ in XOR-masked CSRF tokens or CSP nonces.

We have deprecated Rack::SteadyETag. We instead recommend reconfiguring your Rails app so two requests to the same resource produce the same HTML for a given user.

Rails: Fixing ETags that never match

Every Rails response has a default ETag header. In theory this would enable caching for multiple requests to the same resource. Unfortunately the default ETags produced by Rails are effectively random, meaning they can never match a future request.

Understanding ETags

When your Rails app responds with ETag headers, future requests to the same URL can be answered with an empty response if the underlying content ha...

Operators "in" and "of" are very inconsistent between CoffeeScript and JavaScript

CoffeeScript and JavaScript (ECMAScript) both have operators in and of. Each language use them for more than one purpose. There is not a single case where the same operator can be used for the same purpose in both languages.

Check if an object (or its prototype) has a property

CoffeeScript

var hasFoo = 'foo' of object

JavaScript

var hasFoo = 'foo' in object;

Iterate through all properties of an object
================================...

Popular mistakes when using nested forms

Here are some popular mistakes when using nested forms:

  • You are using fields_for instead of form.fields_for.
  • You forgot to use accepts_nested_attributes in the containing model. Rails won't complain, but nothing will work. In particular, nested_form.object will be nil.
  • The :reject_if option lambda in your accepts_nested_attributes call is defined incorrectly. Raise the attributes hash given to your :reject_if lambda to see if it looks like you expect.
  • If you are nesting forms into nested forms, each model involved ne...

Test-Driven Development with integration and unit tests: a pragmatic approach

Test-Driven Development (TDD) in its most dogmatic form (red-green-refactor in micro-iterations) can be tedious. It does not have to be this way! This guide shows a pragmatic approach with integration and unit tests, that works in practice and improves on productivity.

Advantages

  • No added effort: tests need to be written anyway.
  • Test heads serve as todo lists. You'll always know what is finished and what is left to do.
  • Big tasks are broken down into smaller tasks that can be processed one by one.
  • You will not forget a test.
  • You...

Modern CSS supports individual transform properties

tl;dr

Individual transform properties are great because they allow you to write more readable and maintainable CSS, especially when applying multiple transformations and/or when animating transforms.

For ages, CSS transforms had to be defined using the transform property. For a single transformation, this was something like transform: scale(1.5), and multiple transformations could be applied by chaining them.

.example {
  transform: scale(1.5) rotate(45deg) translateY(-50%);
}

All modern browsers (Chrome & Edge ...

ASDF: A Version Manager To Rule Them All

tl;dr

asdf allows you to manage multiple runtime versions with a single CLI tool and is backwards compatible by supporting existing config files, like e.g. .nvmrc or .ruby-version.

Getting Started

  1. Disable rbenv
    1.1 Delete or comment out source /home/$user/.rbenvrc in ~/.profile
    1.2 Delete or comment our eval "$(rbenv init -)" in ~/.bashrc or ~/.zshrc
    1.3 To take effect you may have to restart your shell or log out and log in again from your current linux session
  2. Install asdf by following the official ...

Jasmine: Preventing unhandled promise rejections from failing your test

You have an async function that rejects:

async function failingFunction() {
  throw new Error("Something went wrong")
}

When you call that function in a test, your test will fail:

it('has a test', function() {
  failingFunction() // this will fail your test
})

The failure message will look like this:

Unhandled promise rejection: Error: Something went wrong

You can fix this by expecting the state of the returned promise:

it('has a test', async function() {
  await expectAsync(failingFunction()).toBeRej...

Decide whether cronjobs should run on one or all servers

Understanding your type of cronjob

Some cronjobs must only run on a single server. E.g. when you run nightly batch operations on the database, it should probably run on a single server. Running it on multiple servers would likely result in deadlocks or corrupt data.

Some cronjobs must always run on all servers. E.g. starting a sidekiq process on reboot.

Configuring whenever

If not configured otherwise, cronjobs defined in whenever's `s...

Take care of indentation and blank lines when using .erb for plain text emails

Building plain text emails with an .erb template doesn't allow you to indent code like you normally do in HTML mails.

❌ DON'T

<%= 'foo' if bar %>

"\n" if bar is false

"foo\n" if bar is true


<%= nil %>

"\n"


<% if true %>
  <%= 'foo' %>
<% end %>  

" foo"


<%= 'foo' %>

<%= 'bar' %>

"foo\n\nbar\n"

✔️ DO

Write unindented code to get the expected result.

<% if bar %>
<%= 'bar' %>
<% end %>
<%= 'foo' %>
<%= 'bar' %> 
  • Use [Form Models](https://github.com/makandr...

RSpec: Run a single spec (Example or ExampleGroup)

RSpec allows you to mark a single Example/ExampleGroup so that only this will be run. This is very useful when using a test runner like guard.

Add the following config to spec/spec_helper.rb:

RSpec.configure do |config|
  # These two settings work together to allow you to limit a spec run
  # to individual examples or groups you care about by tagging them with
  # `:focus` metadata. When nothing is tagged with `:focus`, all examples
  # get run.
  config.filter_run_including :focus => true
  config.run_all_when_everything_filtere...

CSP: strict-dynamic

tl;dr

The strict-dynamic source list keyword allows you to simplify your CSP policy by favoring hashes and nonces over domain host lists.

The key super power of strict-dynamic is that it will allow to load additional scripts via non-"parser-inserted" script elements.

For unsupported browsers, your script can be made backwards compatible by doing something like this:

script-src 'nonce-rAnd0m' 'strict-dynamic' https: 'self'
default-s...

Jasmine: Adding custom matchers

Definition

A matcher is a function that returns an object with a compare key. Usually it is registered with beforeEach:

beforeEach(() => {
  jasmine.addMatchers({
  
    // Example matcher
    toBeAnything() {
      return {
        compare(actualValue, ...matcherArguments) {
           // Do some computations here ...
         
           // Return whether the actualValue matches the expectation
           return {pass: true}
         }
      }
    }
  })
})

Usage

expect(actualValue).toBeAnything(...matcherArg...

Use find_in_batches or find_each to deal with many records efficiently

Occasionally you need to do something directly on the server -- like having all records recalculate something that cannot be done in a migration because it takes a long time.

Let's say you do something like this:

Project.all.each(&:recalculate_statistics!)

Even though you may have been successful with this on your development machine or the staging server, keep in mind that production machines often hold a lot more records. Using all may just work, even with lots of records, but when you iterate over such records and fetch a...

CSS: Combining different length units with calc()

calc() lets you mix CSS units. Ever wanted to give an element "the container's width minus 20px on each side"? Here you go:

.foo {
  width: calc(100% - (20px * 2));
}

When using Sass, you need to interpolate Sass expressions:

$margin: 20px * 2

.foo
  width: calc(100% - #{$margin})

Supported by all modern browsers and IE9+.

How to fix: irb / rails console randomly crashing

If your irb or rails console keeps randomly crashing and you can't figure out why then you can try to disable multi-line autocomplete.

Sidekiq 7: Rate limiting with capsules

Sidekiq 7 adds a new feature called capsules.

Use cases:

  • a chrome queue limited to 1 for e.g. PDF processing to not overload the application server
  • an api queue, that limits a queue to 2 to protect the API server from too many requests in parallel

Example:

Sidekiq.configure_server do |config|
  # Edits the default capsule
  config.queues = %w[critical default low]
  config.concurrency = 5

  # Define a new capsule which ...

ActiveRecord::Relation#merge overwrites existing conditions on the same column

In Ruby on Rails ActiveRecord::Relation#merge overwrites existing conditions on the same column. This may cause the relation to select more records than expected:

authorized_users = User.where(id: [1, 2])
filtered_users   = User.where(id: [2, 3])
authorized_users.merge(filtered_users).to_sql
# => SELECT * FROM users WHERE id IN (2, 3)

The merged relation select the users (2, 3), although we are only allowed to see (1, 2). The merged result should be (2).

This card explores various workarounds to combine two scopes so t...

Using the Oklch color space to generate an accessible color palette

The linked content describes:

  • The different color space of Oklch and RGB/HSL (HDR colors)
  • The advantage of Oklch when you change a base color and your derived colors will keep the same assertions on contrast level

Warning

This feature landed in browsers at the end of 2022. According to our support policy this will become generally usable starting Dec 2024.

The oklch() functional notation expresses a given color in the Oklch color space. It has the same L axis as oklab(), but uses polar coordinates C (Chroma) and H (Hue).

Sour...

Understanding database Indexes in PostgreSQL

I found the linked article very helpful to refresh my understanding of database indexes. As a small bonus, it includes a few helpful SQL oneliners like these two:

Warning

Do not run random code snippets unless you understand them in detail - especially in production.