Controlling smooth scrolling in browsers

It can be hard to understand what causes a browser scroll smoothly or instantly. CSS, JavaScript and the browser settings all have options to influence the scroll behavior. They all interact with each other and sometimes use the same words for different behavior.

CSS

Scrolling elements can use the scroll-behavior CSS property to express a preference between smooth and instant scrolling.

Preferring instant scrolling

CSS can prefer instant scrolling behavior:

html {
  scroll-behavior: auto; /* the default */
}

An `aut...

Testing Accessibility using Orca

Orca is a Linux screen reader. Since it is part of the GNOME project it should come preinstalled with Ubuntu installations.
To turn on the screen reader you can either go to Settings > Accessibility and then activate Screen Reader in the "Seeing" section or you can simply type orca in your terminal.

Note

It may feel quite strange in the beginning to use a screen reader. It is constantly commenting on everything you do and every application you visit will be read aloud.

Once you started your screen reader you can simply navigate to w...

Rails: Configuring the default sorting behaviour

In Rails, the implicit_order_column (added in Rails 6) is a configuration option that helps you define the default sorting behavior of ActiveRecord queries when no explicit ORDER BY clause is provided. This option allows you to specify a column that Rails will use to automatically sort records in a particular order when no specific ordering is given.

Since the id is typically the primary key and automatically indexed, Rails will default t...

Implementing upload progress and remove button with ActiveStorage DirectUpload

DirectUpload allows you to upload files to your file storage without having to wait for the form to submit. It creates AJAX requests to persist the file within your form and wraps them in a little API. This card will show you how to use it in order to create in-place file uploads with progress and a remove button.

This is basic functionality, you may add additional elements, styles and logic to make this look fancy, but the core functionality is the same. I created a file upload that looks like this:

![Image](/makandra/625023/attachments/3...

Implementing authentication and authorization for ActiveStorage blobs/files

ActiveStorage does not provide any built-in way of implementing authentication for the available DirectUpload endpoint in Rails. When using DirectUpload as JS wrapper in the frontend, be aware that its Rails endpoint is public by default, effectively allowing anyone to upload an unlimited amount of files to your storage.

The DirectUploadController from @rails/activestorage bypasses your form controller because it uploads the file using an AJAX request that runs directly, before any form roundtrip happens. This is a comfortable solutio...

How to automatically optimize SVG files with esbuild

SVG files often contain redundant information, like editor metadata or hidden elements. When esbuild handles your static assets, you can easily optimize SVG files using svgo as a plugin. Here is how to do it.

Adding svgo as an esbuild plugin

  1. Add the svgo package to your project's package.json
  2. Adjust your esbuild.config.js like so:
const esbuild = require('esbuild')
const svgo = require('svgo')

const options = {
  // ...
  loader: {
    // ...
    '.svg': 'copy', // this may be different...

Overview of method delegation in Rails

Method delegation in Rails can help you to keep your code organized and avoid deep call chains (law of demeter) by forwarding calls from one object to another. Rails provides several ways to accomplish this. Below is a concise overview of the most common approaches:

Single-Method delegation with delegate

Use the built-in delegate method from ActiveSupport to forward specific methods:

class User < ApplicationRecord
  has_one :profile

  delegate :full_name, :age, to: :profile, prefix: true
end
  • `delegate: full_name, :age...

Debug your Postgres SQL query plan

When debugging slow SQL queries, it’s helpful to understand the database engine's query plan. Whenever you execute a declarative SQL query, the database generates a "query plan" that outlines the exact steps the engine will take to execute the query. Most of the time, we don’t need to worry about this plan because SQL engines are highly optimized and can generate efficient execution strategies automatically. However, if a query is slow, inspecting the generated plan can help identify bottlenecks and optimization opportunities.

If you're usi...

How the Date Header Affects Cookie Expiration and Caching

tl;dr

When a cookie includes an Expires attribute or an HTTP response includes caching headers like Expires or Cache-Control, their validity depends on the server's Date header if present. Otherwise, the browser uses its local time. This can lead to issues in tests with mocked time or inconsistent cache behavior.

Cookie Expires depends on the Date header or browser time

When a cookie includes an Expires attribute, the browser evaluates the expiration date relative to a reference time:

  1. If the HTTP response ...

How to enable pretty IRB inspection for your Ruby class

When Ruby objects are inspected in any modern IRB, some objects (like ActiveRecord instances) are rendered with neat colors and line breaks.
You will not get that for custom classes by default -- which can be annoying if your inspection contains lots of meaningful information.

Here is what you need to do if you want your objects to be inspected nicely.

Implement a pretty_print method

As an example, consider the following class.

class MyClass

  # ...

  def inspect
    "#<#{self.class} attr1: #{attr1.inspect}, attr2: #{attr2...

DB enums are ordered

A lesser known fact about PG enums is that they are ordered. This can be really handy when values have an implicit ordering.

Let's imagine a record Issue(criticality: string). criticality should have the following three possible values: critical, medium, low.

Sorting with Issue.all.order(criticality: :desc) will return the following order:

  1. The 'medium' issue
  2. The 'low' issue
  3. The 'critical' issue

This happens because the database column backing the criticality attribute is a string and PG will use a [collation](...

Capistrano task to edit staging / production credentials

When using Rails credentials, you will edit the encrypted credentials for staging or production environments from time to time. To do that you need the secret key which should only live on the servers.

Do not download these key files to your local dev environment. They are sensitive and must not be stored on your machine.

Instead, put the attached capistrano task into lib/capistrano/tasks/ of your application. It expects environment specific keys to live in :shared_path/config/credentials/:stage.key. If you have a single master.key...

Timeouts for long-running SQL queries

While the main goal always is to prevent long-running queries in the first place, automatic timeouts can serve as a safety net to terminate problematic queries automatically if a set time limit is exceeded. This prevents single queries from taking up all of your database’s resources and reduces the need for manual intervention that might destabilize or even crash the application.

As Rails does not set a timeout on database statements by default, the following query will run for an entire day:

ActiveRecord::Base.connection.execute("S...

A simple example with a GIN index in Rails for optimizing a ILIKE query

You can improve your LIKE / ILIKE search queries in PostgreSQL by adding a GIN index with an operate class ("opclass") to split the words into trigrams to the required columns.

Example

class AddSearchTextIndexToUsers < ActiveRecord::Migration[7.1]
  def change
    enable_extension 'pg_trgm'

    add_index :users, :search_tex...

Rails credentials: Always use the bang version

Rails credentials are a way to store secrets in an encrypted YAML file. Usage is simple: each key in the credentials file becomes a method on Rails.application.credentials, returning the corresponding secret.

# Credentials file
file_storage_secret: superstrongsecret
# Somewhere in the application
FileStorage.secret = Rails.application.credentials.file_storage_secret

Since credentials usually are different between environments, you can easily forget to define them for another environment. If it is an API token, you'll...

Implementing a custom RuboCop cop

It's possible to implement simple custom RuboCop cops with very little code. They work exactly the same like existing rubocop cops and fail the pipeline if they find an offense. This is handy for project specific internal rules or conventions.

The following cop looks at every ruby file and searches for TODO or WIP comments and adds an offense.

class NoTodos < RuboCop::Cop::Base
  MSG = "Don't add TODOs & WIPs in the source."

  def on_new_investigation
    processed_source.comments.each { |comment| search_for_forbidden_ann...

Compare library versions as "Gem::Version" instances, not as strings

Sometimes we have to write code that behaves differently based on the version of a specific gem or the Ruby Version itself. The version comparison can often be seen with simple string comparison like so.

# ❌ Not recommended
if Rails.version > '6.1.7.8' || RUBY_VERSION > '3.1.4'
  raise Error, 'please check if the monkey patch below is still needed'
end

If you are lucky, the version comparison above works by coincidence. But chances are that you are not: For example, Rails version 6.1.10.8 would not raise an error in the code ...

Rails npm packages will use an uncommon versioning scheme

When Rails releases a new version of their gems, they also release a number of npm packages
like @rails/activestorage or @rails/actioncable.

Unfortunately Rails uses up to 4 digits for their gem version, while npm only allows 3 digits and a pre-release suffix.

To map gem versions and npm versions, Rails is going to use a naming scheme like this:

Gem version npm version
7.0.0 7.0.0
7.0.1 7.0.100
...

Ruby: Different ways of assigning multiple attributes

This card is a short summary on different ways of assigning multiple attributes to an instance of a class.

Using positional parameters

Using parameters is the default when assigning attributes. It works good for a small number of attributes, but becomes more difficult to read when using multiple attributes.

Example:

class User
  def initialize(salutation, first_name, last_name, street_and_number, zip_code, city, phone_number, email, newsletter)
    @salutation = salutation
    @first_name = first_name
    @last_name = last_nam...

Rails I18n scope for humanized attribute names

ActiveModel classes have a class method .human_attribute_name. This returns a human-readable form of the attribute:

Person.human_attribute_name(:first_name) # => "First name"

By default Rails will use String#humanize to format the attribute name, e.g. by replacing underscores with spaces and capitalizing the first word. You can configure different translation in your I18n locales, e.g. in config/locales/en.yml:

en:
  activerecord:
    attributes...

Unpoly + Nested attributes in Rails: A short overview of different approaches

This card describes four variants, that add a more intuitive workflow when working with nested attributes in Rails + Unpoly:

  1. Without JS
  2. With HTML template and JS
  3. With HTML template and JS using dynamic Unpoly templates
  4. Adding Records via XHR and JS

Example

For the following examples we use a simple data model where a user has zero or more tasks.

Rails: Different flavors of concatting HTML safe strings in helpers

This card describes different flavors for concatting HTML safe strings in a helper method in Rails. You might want to use the tag helper instead of the content_tag helper (the tag helper knows all self closing tags).

Example

We want to generate HTML like this:

<h1>Navigation</h1>
<ul>
  <li>Left</li>
  <li>Right</li>
</ul>

Below you ca...

Using rack-mini-profiler (with Unpoly)

Debugging performance issues in your Rails app can be a tough challenge.

To get more detailed insights consider using the rack-mini-profiler gem.

Setup with Unpoly

Add the following gems:

group :development do
  gem 'memory_profiler'
  gem 'rack-mini-profiler'
  gem 'stackprof'
end

Unpoly will interfere with the rack-mini-profiler widget, but configuring the following works okayish:

// rack-mini-profiler + unpoly
if (process...

Rails 7.1: Take care of the new production log default to standard out

Starting with Rails 7.1 the production logger is set to standard out. For applications running with opscomplete ensure to keep logging to a file as before (e.g. when running bin/rails app:update).

It should be enough to change these lines in the config/environments/production.rb back to the implementation in Rails <7.1:

-  # Log to STDOUT by default
-  config.logger = ActiveSupport::Logger.new(STDOUT)
-    .tap  { |lo...