CarrierWave: Default Configuration and Suggested Changes

CarrierWave comes with a set of default configuration options which make sense in most cases. However, you should review these defaults and adjust for your project wherever necessary.

You will also find suggestions on what to change below.

Understanding the default configuration

Here is the current default config for version 2:

config.permissions = 0644
config.directory_permissions = 0755
config.storage_engines = {
  :f...

Supporting multiple SAML IdPs within a single Rails application

The linked article shows how to configure omniauth-multi-provider to support multiple SAML identity providers for a single Rails app:

To solve this, the omniauth-multi-provider gem acts as a dynamic wrapper around OmniAuth. It enables your application to load the correct IdP configuration at runtime—based on the tenant—allowing for flexible and secure SSO authentication across multiple organisations.

Selenium: Fix Chrome's "Unsafe Password" Warning

tl;dr

Set profile.password_manager_leak_detection to false in your Selenium Chrome options to disable password leak detection and suppress the warning.

Problem

When running Selenium tests with recent versions of Chrome and Chromedriver (e.g., version 136+), entering “unsafe” (weak or reused) passwords in forms triggers a browser warning:

"This password has appeared in a data breach…"

This alert can break automated test runs, especially in CI/CD pipelines.

Solution

You can **disable Chrome’s password leak ...

Using Passenger Standalone for development

For our production servers we use Passenger as a Ruby application server. While it is possible to use Passenger for development as an Apache module, the installation process is not for the faint of heart.

Luckily Passenger also comes as a standalone binary which requires zero configuration.

You can Passenger Standalone as a replacement for Webrick or Thin if you'd like to:

  • Use SSL certificates locally
  • Get performance behavior that is closer to ...

How to make changes to a Ruby gem (as a Rails developer)

At makandra, we've built a few gems over the years. Some of these are quite popular: spreewald (> 1M downloads), active_type (> 1M downloads), and geordi (> 200k downloads)

Developing a Ruby gem is different from developing Rails applications, with the biggest difference: there is no Rails. This means:

  • no defined structure (neither for code nor directories)
  • no autoloading of classes, i.e. you need to require all files yourself
  • no active_support niceties

Also, their scope...

Using the alt attribute and the figcaption element in HTML

While both the alt attribute and the figcaption element provide a way to describe images, the way we write for them is different:

  • alt descriptions should be functional
  • figcaption descriptions should be editorial or illustrative

Using FactoryBot in Development

If you need dummy data to play around with in development, it's often faster to reuse your existing factories instead of using the UI or creating records in the Rails console. This approach saves time and gives you useful defaults and associations right out of the box.

You can use FactoryBot directly in the Rails console like this:

require 'factory_bot_rails' # Not needed if the factory_bot_rails gem is in the :development group
FactoryBot.create(:user)

You can also apply traits or override attributes:

FactoryBot.create...

Web performance snippets: little scripts that return performance metrics

Use these snippets when you want to measure yourself.

Currently available:

Core Web Vitals

Largest Contentful Paint (LCP)
Largest Contentful Paint Sub-Parts (LCP)
Quick BPP (image entropy) check
Cumulative Layout Shift (CLS)

Loading

Time To First Byte
Scripts Loading
Resources hints
Find Above The Fold Lazy Loaded Images
Find non Lazy Loaded Images outside of the viewport
Find render-blocking resources
Image Info
Fonts Preloaded, Loaded, and Used Above The Fold
First And Third Party Script Info
First And Third Party Script Timings
I...

Capybara: Waiting for pending AJAX requests after a test

When ending a Selenium test Capybara resets the browser state by closing the tab, clearing cookies, localStorage, etc.

It may be a good idea to wait for all in-flight AJAX requests to finish before ending a scenario:

  • You may have client-side JavaScript that freaks out when the tab closure kills their pending requests. If that JavaScript opens an error alert or spams errors to the console, your test may fail after the last step.
  • With unlucky timing the server may receive an AJAX request as the browser tab closes, causing a connection ...

Automatically validating dependency licenses with License Finder

"Open-source software (OSS) is great. Anyone can use virtually any open-source code in their projects."

Well, it depends. Licenses can make things difficult, especially when you are developing closed-source software. Since some OSS licenses even require the employing application to be open-sourced as well (looking at you, GPL), you cannot use such software in a closed-source project.

To be sure on this, we have developed a project-level integration of Pivotal's excellent [license_finder](https:/...

Shortcuts for getting ids for an ActiveRecord scope

You can use .ids on an ActiveRecord scope to pluck all the ids of the relation:

# Modern Rails
User.where("users.name LIKE 'Foo Bar'").ids

# Rails 3.2+ equivalent
User.where("users.name LIKE 'Foo Bar'").pluck(:id)

# Edge rider equivalent for Rails 2+
User.where("users.name LIKE 'Foo Bar'").collect_ids

Testing ActiveRecord validations with RSpec

Validations should be covered by a model's spec.

This card shows how to test an individual validation. This is preferrable to save an entire record and see whether it is invalid.

Recipe for testing any validation

In general any validation test for an attribute :attribute_being_tested looks like this:

  1. Make a model instance (named record below)
  2. Run validations by saying record.validate
  3. Check if record.errors[:attribute_being_tested] contains the expected validation error
  4. Put the attribute into a valid state
  5. Run...

CSS variables aka CSS Custom Properties

CSS variables are very different from preprocessor variables. While preprocessors use variables to compile a static piece of CSS, CSS custom properties are a reactive (i.e. live) part of the styles. Think of them like usual CSS properties that cascade, but have:

  • A special syntax: CSS variables always start with a double-dash (--color)
  • No inherent meaning: Defining a CSS variable will not change any styles in itself
  • A special functionality: CSS variables can be used within the values of other properties, including CSS variables...

Carrierwave: Built-in RSpec matchers

CarrierWave comes with some RSpec matchers which will make testing more comfortable. Let's say you have an Uploader like this:

class MyUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  # Create different versions of your uploaded files:
  version :small do
    process resize_to_fill: [100, 100]
  end
  
  version :medium do
    process resize_to_fit: [200, nil]
  end
  
  version :large do
    process resize_to_limit: [400, 400]
  end

end

Imagine you have a class Movie with an attribute poster. In ...

Output of ParallelTests::Cucumber::FailuresLogger was fixed in 4.9.1

Our projects with parallel_tests and cucumber used to have a patched failure logger as the one from parallel_tests was missing spaces which resulted in the output not being a valid rerun argument. With version 4.9.1 output of ParallelTests::Cucumber::FailuresLogger was fixed.

You can remove the patch (e.g. features/support/cucumber_failures_logger.rb) and switch the logger in config/cucumber.yml back to ParallelTests::Cucumber::FailuresLogger.

Thanks to Dominik for [the fix](https://github.com/grosser/parallel_tests/commit/...

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

AppArmor in Linux

This note is a reminder that there is something called AppArmor that could cause weird errors ("File not found", "Can't open file or directory", ...) after configuration changes, e.g. when changing MySQL's data directory.

Remember to have a look at AppArmor's daemon configuration (usually at /etc/apparmor.d/) if you change daemon configuration and run into errors such as the one above.

Rails: How to get the ordered list of used middlewares

Rails middlewares are small code pieces that wrap requests to the application. The first middleware gets passed the request, invokes the next, and so on. Finally, the application is invoked, builds a response and passes it back to the last middleware. Each middleware now returns the response until the request is answered. Think of it like Russian Dolls, where each middleware is a doll and the application is the innermost item.

You can run rake middleware to get the ordered list of used middlewares in a Rails application:

$> rake midd...

PostgreSQL: Difference between text and varchar columns

TL;DR PostgreSQL handles Rails 4+ text and string columns the same. Some libraries may still reflect on the column type and e.g. render differently sized text fields.


PostgreSQL offers three character types for your columns:

  • character varying(n) (also called varchar or just string): Contents are limited to n characters, smaller contents are allowed.
  • character(n): All contents are padded with spaces to allocate exactly n characters.
  • text: There is no upper or lower character limit (except for the absolute...

Extracting parts of forms into Unpoly modals/popups

Say you wrap your index view in a form to apply different filters like pagination or a search query. On submit, your index view changes when the filters are applied (through up-submit and up-target). Now you want to enable more data-specific filters using a separate "Filters" button that opens a popup to not overload your UI.

Filter bar:

Filter popup:

The problem is tha...

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

With the new unpoly client side templates (available since 3.10) there's another way to substitute the ids of inserted nested attribute forms which I added to this card.

Changes to positional and keyword args in Ruby 3.0

Ruby 3.0 introduced a breaking change in how it treats keyword arguments.

There is an excellent blog post on the official Ruby blog going into the details. You should probably read that, but here is a slightly abbreviated version:

What changed

When the last argument of a method call is a Hash, Ruby < 3 automatically converted it to to Keyword Arguments. If you call a method in Ruby >= 3 that accepts keyword arguments, eithe...

CarrierWave: How to remove GIF animation

When accepting GIF images, you will also accept animated GIFs. Resizing them can be a time-consuming task and will block a Rails worker until the image is processed.

Save yourself that trouble, and simply tell ImageMagick to drop any frames but the first one.

Add the following to your uploader class:

process :remove_animation

private

def remove_animation
  if content_type == 'image/gif'
    manipulate! { |image| image.collapse! }
  end
end

You may also define that process for specific versions only (e.g. only for thum...

JavaScript: Comparing objects or arrays for equality (not reference)

JavaScript has no built-in functions to compare two objects or arrays for equality of their contained values.

If your project uses Lodash or Underscore.js, you can use _.isEqual():

_.isEqual([1, 2], [2, 3]) // => false
_.isEqual([1, 2], [1, 2]) // => true

If your project already uses Unpoly you may also use up.util.isEqual() in the same way:

up.util.isEqual([1, 2], [2, 3]) // => false
up.util.isEqual([1, 2], [1, 2]) // => true

If you are wri...