Don't use log level :debug in your production environments

Catch phrase

You don't want sensitive user data in your logs.

Background

Rails per default filters sensitive data like passwords and tokens and writes [FILTERED] to the logs. The code which is responsible for enabling that usually lives in filter_parameter_logging.rb (Rails.application.config.filter_parameters). Here is an example of a filtered log entry:

Unfiltered:
`User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."token" = $1 LIMIT $2 [["token", "secret-token"], ["LIMIT", 1]]`

After the filter is appl...

Upgrade Rails: Awareness list

Disclaimer

This card is a collection of guides and things to have in mind when upgrading to a specific version. It is not meant to be complete, so please feel free to contribute!

General workflows

Upgrade to Rails 7

Upgrade to Rails 6

  • [...

Advanced plotting in Ruby with Gnuplot

Besides Plotting graphs in Ruby with Gruff, which comes handy for many uses cases, you sometimes might need configuration for more advanced plots, e.g. for academic concerns. Then using Gnuplot, the first academic open source plotting software, might be a good option.

There are several wrappers for Ruby available and I mainly looked at one of the two most frequently used ones, which are [ruby_gnuplot](https://github.com/rdp/ruby_gnuplot...

Split your parallel tests by execution time and keep execution logs up to date

Both knapsack and parallel_tests have the option to split groups by historic execution time. The required logs for this might be outdated since you manually have to update and push them into your repository.

The following card includes an option how you can keep them consistently up to date with no extra effort locally and/or remotely.

How to always split by execution logs

Parallel Tests

The parallel_tests gem has the option flag `--group...

Git restore vs. reset for reverting previous revisions

The git doc states on the difference of these two commands:

  • git-restore[1] is about restoring files in the working tree from either the index or another commit. This command does not update your branch. The command can also be used to restore files in the index from another commit.
  • git-reset[1] is about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.

git reset can also be used to restore th...

redirect_to and redirect

There are multiple ways to redirect URLs to a different URL in Rails, and they differ in small but important nuances.

Imagine you want to redirect the following url https://www.example.com/old_location?foo=bar to https://www.example.com/new_location?foo=bar.

Variant A

You can use ActionController::Redirecting#redirect_to in a controller action

class SomeController < ActionController::Base
  def old_location
    redirect_to(new_location_url(params.permit(:foo))) 
  end
end

This will:

  • It will redirect with a 302 st...

Do not pass params directly into url_for or URL helpers

Rails' url_for is useful for generating routes from a Hash, but can lead to an open redirect vulnerability.

Your application's generated route methods with a _url suffix are also affected because [they use url_for unter the hood](https://github.com/rails/rails...

Do not use "permit!" for params

Rails' Strong Parameters enable you to allow only specific values from request params to e.g. avoid mass assignment.

Usually, you say something like params.permit(:email, :password) and any extra parameters would be ignored, e.g. when calling to_h.
This is excellent and you should definitely use it.

What is permit! and why is it dangerous?

However, there is also params.permit! whic...

Chromedriver issue #4550 breaks the user agent for device emulation via device name

Newest versions of Chromedriver breaks the user agent for device emulation via device name. In previous versions the user agent of the emulated device was set. In the newest versions the user agent differs from the emulated device.

In Capybara an affected config looks like following:

Capybara.register_driver :mobi...

Minifying object properties in JavaScript files

An introduction to mangling

When you minify ("compress", "optimize") your JavaScript for production, the names of your functions and variables will be renamed for brevity. This process is often called mangling.

E.g. if this is your source code:

function function1() {
  function2()
}

After mangling it would look like this:

function a() {
  b()
}

Object properties are not mangled by default

Minfiers never mangle properties by default, as this can be an unsafe transformation. This leads to larger file sizes if...

Postgres in Alpine docker container: sorting order might differ

In CI test runs I noticed that string sorting order changed after switching from a debian-based PostgreSQL docker image to one that is based on Alpine Linux.

Debian image sorting: bar Bar foo Foo
Alpine image sorting: Bar Foo bar foo

Explanation

Alpine Linux is a very slim linux distribution that results in small docker image sizes (roughly 100MB instead of 150MB), so it's a popular choice. However, it does not have all comman locales installed and does not use all locales that a user installs by default.
Postgres orders string co...

Node: How to run a globally installed package with npx

You can tell npm to install a package globally with npm -g install @puppeteer/browsers. However, it seems that its not possible that npx can run commands from global packages without referencing the global package path.

Example

Installing @puppeteer/browsers globally:

$ npm -g install @puppeteer/browsers

The globally installed package @puppeteer/browsers can not be access via npx:

$ npx --no-install @puppeteer/browsers

npm ERR! canceled # Error message when package is not installed

But it is installed g...

RSpec: How to write isolated specs with cookies

Background

Rails offers several methods to manage three types of different cookies along with a session storage for cookies. These are normal, signed and encrypted cookies.

By following the happy path of testing a web application, that is only the main use-case is tested as a integration test and the rest as isolated (more unit ...

How to transition the height of elements with unknown/auto height

If you want to collapse/expand elements with dynamic content (and thus unknown height), you can not transition between height: 0 and height: auto.

Doing it properly, with modern CSS features

In the past, you might have resorted to bulky JavaScript solutions or CSS hacks like transitioning between max-height: 0 and max-height: 9999px. All of them were awkward and/or have several edge cases.

With modern CSS, there is actually a way to do it properly:
Just use a display: grid container which transitions its grid row height betwe...

Use -webkit-line-clamp to natively truncate long (multi-line) texts with an ellipsis

Note: You won't need this for single lines of text. In this case it is better to just use the text-overflow property: Use CSS "text-overflow" to truncate long texts

You can use -webkit-line-clamp in your CSS/SASS to natively render an ellipsis (...) after a specific amount of lines for a multi-line text in your HTML.
Earlier, it was necessary to implement JavaScript solutions like Superclamp.js to enable this because the browser support has been rather limited...

Using git patchfiles to speed up similar implementation tasks

Sometimes you'll find yourself with a set of tasks that require similar code for different models. For example, if you start working at a new application that allows CRUDing pears and apples, each commit might look similar to this:

commit 41e3adef10950b324ae09e308f632bef0dee3f87 (HEAD -> ml/add-apples-12345)
Author: Michael Leimstaedtner <michael.leimstaedtner@acme.com>
Date:   Fri Aug 11 09:42:34 2023 +0200

    Add Apples as a new fruit

diff --git a/app/models/apple.rb b/app/models/apple.rb
new file mode 100644
index 0000000..a51...

CarrierWave: Processing images with libvips

When you write your next CarrierWave uploader, consider processing your images with libvips instead of ImageMagick.

Reasons for libvips

There are several upsides to using libvips over ImageMagick:

How to find child nodes that match a selector with JavaScript

Using querySelector or querySelectorAll in JavaScript, you can easily find descendants of a node that match a given selector.

But what if you want to find only children (i.e. direct descendants) of an element?
Easy: use :scope. It references the element on which DOM API methods are being called:

element.querySelectorAll(':scope > .your-selector')

Example

Consider this HTML

<body>
  <div id="container1">
    <div id="container1a">foo</div>
    <div id="container1b">bar</div>
    <div id="container1c">baz</...

Balance your texts today with text-wrap: balance

So you have a heading that is just barely wider than the container it should fit into, and it wraps a single word to a new line and it's not really pretty?
Cry no more, for you can use text-wrap: balance today to fix that. At least in some browsers.

When browsers encounter a text-wrapping element with text-wrap: balance style, they will try breaking to a new line sooner, if it balances out the width of lines.

Without text-wrap: balance With text-wrap: balance
Image ![...

Checklist for Implementing Design

We have a long-standing checklist for merge requests. However, it hardly matches the intricate requirements for design. This checklist fills the gap.

Before starting implementing, look at all designs: are there components similar to yours? Have they already been implemented? Can you build on this prior art when implementing yours?

Checklist: I confirm my design implementation

  • has been tested manually by me
  • adheres to the code style of the project (e.g. BEM)
  • avoids "magic numbers" (don't say e.g. ...

Code splitting in esbuild: Caveats and setup

TL;DR Still has caveats.

Code splitting is a feature of JavaScript bundlers that can keep huge libraries out of the main bundle.

How code splitting works

Like Webpack esbuild lets you use the await import() function to load code on demand:

// application.js
const { fun } = await import('library.js')

fun()

However, esbuild's code splitting is disabled by default. The code above would simply [inline](https://en.wiki...

Don't assert exceptions in feature specs

As we are slowly switching from Cucumber scenarios to RSpec feature specs, you might be tempted to write assertions like this one:

feature 'authorization for cards management' do
  let(:guest_user) { create(:user, :guest) }

  scenario "rejects guest users from adding new cards", js: true do
    sign_in guest_user

    expect { visit new_cards_path }.to raise_error(Consul::Powerless)
  end
end

While this might work under certain circumstances¹, there is a good chance you'll see two exceptions when running this single spec:

  • ...