Rails: Encrypting your database information using Active Record Encryption

Since Rails 7 you are able to encrypt database information with Active Record. Using Active Record Encryption will store an attribute as string in the database. And uses JSON for serializing the encrypted attribute.

Example:

  • p: Payload
  • h: Headers
  • iv: Initialization Vector
  • at: Authentication Tag
{ "p": "n7J0/ol+a7DRMeaE", "h": { "iv": "DXZMDWUKfp3bg/Yu", "at": "X1/YjMHbHD4talgF9dt61A=="} }

Note this before encrypting attributes with Active Record:
...

git: find the version of a gem that releases a certain commit

Sometimes I ran across a GitHub merge request of a gem where it was not completely obvious in which version the change was released. This might be the case for a bugfix PR that you want to add to your project.

Git can help you to find the next git tag that was set in the branch. This usually has the name of the version in it (as the rake release task automatically creates a git tag during release).

git name-rev --tags <commit ref>

Note

The more commonly used git describe command will return the last tag before a c...

Postgres: DISTINCT ON lets you select only one record per ordered attribute(s) for each group

  • To retrieve only unique combinations of the selected attributes: You can omit rows, where all selected columns are equal with the DISTINCT statement.
  • To retrieve the group wise maximum of certain columns: You can keep only one record for each group with the DISTINCT ON statement, to omit equal rows within each specified group.

Use case

You have a query where you want only one record for a set of specifically ordered attributes.

How to use?

Let's say we look at the example how to query only the latest post for each user:
...

RSpec: How to compare ISO 8601 time strings with milliseconds

Rails includes milliseconds in Time / DateTime objects when rendering them as JSON:

JSON.parse(User.last.to_json)['created_at']
#=> "2001-01-01T00:00:00.000+00:00"

In RSpec you might want to use .to_json instead of .iso8601 to use the build-in eq matcher:

it 'returns the created at attribute of a user' do
  get '/users/1'
  
  expect(JSON.parse(response.body)['created_at']).to eq(Time.parse('2001-01-01').to_json)
end

Otherwise the strings do not match:

DateTime.parse('2001-01-01').to_s (will defa...

How to kill a Rails development server by force

Sometimes, the rails dev server doesn't terminate properly. This can for example happen when the dev server runs in a RubyMine terminal.

When this happens, the old dev server blocks port 3000, so when you try to start a new server, you get the error:

Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)

You can terminate such a dev server with this command:

lsof -t -i :3000 -s TCP:LISTEN | xargs kill -9

It might be worth it to add this to your bash aliases.

Heads up: expect(object).to receive(:method_name) does not execute the original implementation of the method

Let's assume that we have a model Movie that registers a callback function when a new instance of Movie is created (Note: For the purpose of this card it is not important what that callback does or which type of callback it is).

This is how we test whether the callback function (here it is named :my_method) is called when a new movie is created:

expect_any_instance_of(Movie).to receive(:my_method)
create(:movie)  # <-- this is where the method :my_method should be called

You might expect that when calling `create(:mo...

Spreewald: patiently blocks must not change variables from the surrounding scope

I recently enjoyed debugging a Cucumber step that tried to be retryable using a patiently block:

Then /^"([^"]*)" should( not)? be selected for "([^"]*)"$/ do |value, negate, field|
  patiently do
    field = find(:label, text: field)['for'].delete_suffix('-ts-control')
    ...
  end
end

Unfortunately this block is not retryable:

  • The first attempt changes the value of field.
  • All subsequent attempts will using the changed value of field, instead of the o...

Why Sidekiq Jobs should never be enqueued in an `after_create` or `after_save` callback

When an object is created / updated, various callbacks are executed in this order:

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback

Thus, each of these callbacks is executed at a specific time in the life cycle of the object. This is important because this point in time determ...

Ruby object equality

TLDR

if you define a equality method for a class you must also implement def hash.

Ruby has a lot of methods that have to do something with equality, like ==, ===, eql?, equal?. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.

Differences between the methods

for everyday use: ==

When you compare two objects in ruby, you most often see the use of foo == bar. By default the == operator inherits from Object and is impl...

Timecop: reset after each test

Timecop is a great gem to set the current time in tests. However, it is easy to introduce flakyness to your test suite when you forget to reset the time after the test.
This might be the case if:

  • a test freezes time and a later test does not work for frozen time
  • a later test needs the real current date to work correctly

Often you only notice these kinds of errors in rare cases when tests are executed in a particular order.

A way to avoid this is by using block notation (`Timecop.travel(...) ...

Jasmine: Mocking ESM imports

In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.

Example

We are going to use the same example to demonstrate the different approaches of mocking an imported function.

We have a module 'lib' that exports a function hello():

// lib.js

function hello() {
  console.log("hi world")
}

export hello

We have a second module 'client' that exports a function helloTwice(). All this does is call hello() ...

Heads up: network requests `Kernel#open` are not mocked with VCR

We usually rely on VCR and WebMock to prevent any real network connection when running our unit tests.

This is not entirely true: They are both limited to a set of HTTP libraries listed below (as of 2022). Direct calls to Kernel#open or OpenURI#open_uri are not mocked and will trigger real network requests even in tests. This might bite you e.g. in [older versions of CarrierWave](https://github.com/carrierwaveuploader/carrierwave/blob/0.11-stable/lib/carrierwave/upl...

Destructors for async Unpoly compilers

Usually, Unpoly compiler destructors are returned from the compiler function.
However, when using async compiler functions, you can not register destructors via return.

This will not work:

up.compiler('my-example', async (element) => {
  await something
  
  return function onDestroy() {
    // ...
  }
})

Instead, use up.destructor:

up.compiler('my-example', async (element) => {
  await something
  
  u...

Using path aliases in esbuild

In esbuild, you usually import other files using relative paths:

import './some-related-module'
import `../../utils/some-utility-module`
import `../../../css/some-css.sass`

This is totally fine if you import closely related files, but a bit clunky when you're trying to import some "global" module, like a utility module. When moving a file, your imports also need to change.

To get around this, esbuild support a mechanism first introduced in TypeScript called "path aliases". It works like this:

First, you create a file called `js...

Rails: Use STI in Migration

tl;dr

You should decouple migrations from models by embedding models into the migration. To use STI in this scenario you have to overwrite find_sti_class and sti_name.

Tip

When possible, try to avoid STI in migrations by disabling it.

Example

Warning

This is more for the sake of I want to do it but I kno...

How to see how many inotify instances are used by each process

As a developer you may have many tools watching your project for changes: Your IDE, Webpack, Guard, etc. This is often done with an inotify watcher. If you have too many inotify instances you may run into limits of your operating system.

To find out which process is using them all up you can run:
sudo find /proc/*/fd/ -type l -lname "anon_inode:inotify" -printf "%hinfo/%f\n" | xargs grep -cE "^inotify" | column -t -s:

You will get a list like:

/proc/3753/fdinfo/7      1
/proc/3774/fdinfo/7      1
/proc/4034/fdinfo/12     14
/pr...

Jasmine: Creating DOM elements efficiently

Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.

Let's say I need this HTML structure:

<ul type="square">
  <li>item 1</li>
  <li>item 2</li>
</ul>

This card compares various approaches to fabricating DOM elements for testing.

Constructing individual elements

While you can use standard DOM functions to individually create and append elements, this is extremely verbose:

let list = document.createElement('...

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

Inspect and Debug CSS Flexbox and Grid Layouts by using the Layouts Tab in Dev Tools

tl;dr

In Chrome DevTools in the Layouts tab you have handy options to debug CSS Flexbox and Grid. Including:

  • Display size and lines along with labels
  • Changing their attributes
  • Change how overlay is colored and fastly switch nested elements in the Elements panel

This guide will only cover some example gif recordings on how to use with Grid, since it's basically straight forward to apply this for Flexbox by yourself afterwards.

For this purpose a the link to documentation and a simple code pen have been added...

Do not use unitless zeros in CSS calc functions

While in CSS zero is usually referenced without specifying a unit (e.g. padding: 0), you must not use a unitless zero in calc functions.

You would probably not write something like calc(1rem + 0) yourself, but it might be the result of a CSS preprocessor (like Sass) or when using custom properties.

The following is invalid:

.example {
  --extra-padding: 0;
  padding: calc(1rem + var(--extra-padding));
}

That is simply because it is unsupported, as per docum...

Organizing custom Date(Time) formats

Large Rails projects tend to define multiple custom ways to format Dates or DateTimes. This often leads to duplicated format strings, wrong formats and unnecessary introduction of new formats.

to_fs also supports to refer to those formats by name e.g. to_formatted_s(:iso_8601) or to_formatted_s(:short).
to_fs is an alias for to_formatted_s.

Those names are defined in Time::DATE_FORMATS and it's possible to add your own formats. There is a how to in the official [docs](https://api.rubyonrails.org/classes/Date.html#method-i-t...

Sass: How to get rid of deprecation warnings in dependencies

sass >= 1.35.0 has the option quietDeps and silenceDeprecations to silence deprecation warnings from dependencies.

Below there are a few examples for different build tools how to set the Sass options.

Webpacker

const sassLoaderConfig = environment.loaders.get('sass')
const...

Chrome DevTools: Event Listener Breakpoints

tl;dr

In Chrome DevTools in the Sources tab you can activate Event Listener Breakpoints for debugging events.

Example

The Event Listener Breakpoints in the Chrome DevTools can be quiet useful for debugging why and where code is handling specific events.

Here you can see a very simple example that shows what lines of code handle a click:

Image

You can use this Code Pen if you want to try it yourself.

Limitation

...

Chrome DevTools: DOM Breakpoints - Breakpoints on HTML Elements

tl;dr

In Chrome DevTools in the Elements tab or in Firefox in the Inspector tab you can right click on an element and choose Break on to debug changes related to this element.

Example

DOM Breakpoints can be quite useful to quickly find the JavaScript that is responsible for some (unexpected) behavior. You can use DOM Breakpoints for debugging subtree modifications, attribute modifications or node removal.

Here you can see a very simple example that shows what JavaScript lines are responsible for ...