Double loading issue with Ruby default gems

Ruby includes many standard gems that are bundled into the Ruby installation. Here is an example for the gem strscan that will be displayed as default:

gem list strscan     

*** LOCAL GEMS ***

strscan (default: 3.0.1)

It is still possible to have newer version of a gem installed beside the default version:

gem install strscan  
Fetching strscan-3.0.3.gem
Building native extensions. This could take a while...
Successfully installed strscan-3.0.3
1 gem installed
gem list strscan   

*** LOC...

Do not forget mailer previews

When changing code in mailers, updating the corresponding mailer preview is easily forgotten.

Mailer previews can be tested like other code as well and I sometimes add the following test to test suites:

# Make sure to require the previews
Dir[Rails.root.join('test/mailers/previews/*.rb')].sort.each { |file| require(file) }


ActionMailer::Preview.all.index_with(&:emails).each do |preview, mails|
  mails.each do |mail|
    it "#{preview}##{mail} works" do
      expect { preview.call(mail) }.not_to raise_error
   ...

Show a JS fiddle in fullscreen

If you have a JS fiddle, you can open it in fullscreen by appending /show to the URL.

Example: https://jsfiddle.net/b275g910/3 => https://jsfiddle.net/b275g910/3/show

Debug file system access in a Rails application

It might sometimes be useful to check whether your Rails application accesses the file system unnecessarily, for example if your file system access is slow because it goes over the network.

The culprit might be a library like carrierwave that checks file existence or modification times, whereas your application could determine all this from your database.

Introducing strace

One option it to use strace for this, which logs all system calls performed by a process.

To do this, start your rails server using something like

DISA...

Capybara: Most okayest helper to download and inspect files

Testing file download links in an end-to-end test can be painful, especially with Selenium.

The attached download_helpers.rb provides a download_link method for your Capybara tests. It returns a hash describing the download's response:

details = download_link('Download report')
details[:disposition]  # => 'attachment' or 'inline'
details[:filename]     # => 'report.txt'
details[:text]         # => file content as string
details[:content_type] # => 'text/plain'

Features

Compared to [other approaches](...

RSpec: How to turn off partial double verification temporarily

While verifying doubles in RSpec is a good default, it is limited in the amount of methods it actually is able to verify.

The background is that RSpec can't verify dynamically defined methods, which is a known issue for the usage of helper_method and also the reason why [RSpec >= 3.6](http://rspec.info/blog/2017/05/rspec-3-6-has-been-rel...

Project maintenance: four levels of code quality

Code quality can be measured in four levels:

  1. (Working code)
  2. Reliable code (minimum)
  3. Readable code (ok for short-lived code)
  4. Changeable code (standard level)

The code quality of a project directly impacts its maintainability.

Generally you should aim for level 3. If the code will stay for less than a few months, it may stay at level 2. Never go below level 1.

0. Working code

You have implemented that feature and it works. Congrats! You have reached level zero, which means three levels of code quality lie ahead.

First, m...

Use DatabaseCleaner with multiple test databases

There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.

Your database.yml may look like this:

default: &default
  adapter: postgresql
  encoding: unicode
  username: <%= ENV['DATABASE_USER'] %>
  host: <%= ENV['DATABASE...

How to bind an event listener only once with Unpoly

You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }:

up.on('up:fragment:inserted', { once: true }, function () { ... })

In Unpoly 1 you can immediately unregister the listener with up.off:

up.on('up:fragment:inserted', function fragmentInsertedCallback() {
  up.off('up:fragment:inserted', fragmentInsertedCallback)
  // ... code for the callback function, which should run only once
})

Exam...

Chrome DevTools: List Registered Event Listeners

In Chrome DevTools you can use getEventListeners(object) to get a list of registered event listeners on the specified object.

You can also do this without the console, by selecting an element in the DOM inspector. In the element details, select the tab Event Listeners".

Example

const registry = getEventListeners(document)

registry['up-click']
// 0: { useCapture: false, passive: false, once: false, type: 'up:click', listener: ƒ }
// 1: { useCapture: false, passive: false, once: false, type: 'up:click', listener: ƒ }
//...

How to get information about a gem (via CLI or at runtime from Ruby)

When you need information about a gem (like version(s) or install path(s)), you can use the gem binary from the command line, or the Gem API inside a ruby process at runtime.

gem binary (in a terminal)

You can get some information about a gem by running gem info <gem name> in your terminal.

Example:

$ gem info irb

*** LOCAL GEMS ***

irb (1.4.1, 1.3.5)
    Author: Keiju ISHITSUKA
    Homepage: https://github.com/ruby/irb
    Licenses: Ruby, BSD-2-Clause
    Installed at (1.4.1): /home/arne/.rbenv/versions/3.0.3/lib/ruby/g...

JavaScript: Working with Query Parameters

tl;dr: Use the URLSearchParams API to make your live easier if you want to get or manipulate query parameters (URL parameters).

URLSearchParams API

The URLSearchParams API is supported in all major browsers except IE 11.

It offers you a bunch of useful methods:

  • URLSearchParams.append() - appends a query parameter
  • URLSearchParams.delete() - deletes the specified query parameter
  • URLSearchParams.get() - returns the value of the specified query parameter
  • `URLSearchP...

Jasmine: Fixing common errors during initialization

Due to the way we setup Jasmine tests in our projects, you may run into various errors when Jasmine boots.

Setting jasmineRequire on undefined

Jasmine 4 may fail with an error like this:

Uncaught TypeError: Cannot set properties of undefined (setting 'jasmineRequire')

This is due to issues in Jasmine's [environment detection](https://github.com/jasmine/jasmine/blob/502cb24bb89212917a3c943b593fd918ffc481cb/lib/jasmine-core/...

Generating an Entity Relationship Diagram for your Rails application

This card explains how to generate an entity relationship diagram for your Rails application.
We also show how to limit your ERD to a subset of models, e.g. models inside a namespace.

Generating a full ERD

Option A: RubyMine

  1. Right-click anywhere in your project tree
  2. In the context menu, find the "Diagrams" menu item at/near the bottom
  3. Inside, choose "Show diagram" → "Rails Model Dependency Diagram"
  4. A new tab will open with the diagram inside. You can modify it there, and export it as an image.

Option B: Use rails-e...

Version 5 of the Ruby Redis gem removes Redis.current

Redis.current will be removed without replacement in redis-rb 5.0.
Version 4.6.0 adds deprecation warnings for Redis.current and Redis.current=:

`Redis.current=` is deprecated and will be removed in 5.0.

If your application still uses Redis.current, you can only fix it by no longer using it. Here is how.

Redis.new when you need it

You can easily instantiate a Redis client when you need it.

There is probably already a constant like REDIS_URL that you use to configure Sidekiq or similar. So just use that one.

``...

How to access Chrome Devtools when running JavaScript tests via CLI

While we are used to run our JavaScript tests on a test page within our Browser, it's also possible to run them on the command line with NodeJS. I think that's actually the most common way to run JS tests.

Given a Vue project that uses Jest (via vue-cli-service) with the following package.json:

{
  "scripts": {
    "test": "vue-cli-service test:unit --testMatch='**/tests/**/*.test.js' --watch"
  },
}

This allows us to run J...

Jest: How to clear the cache

I recently was in a weird situation where my (Jest/CLI) tests were referencing a function that was no longer part of my code - I had just refactored it.

Apparently Jest has some kind of cache that caused the issue, running npx jest --clearCache solved it for me.

RSpec: You can super into parent "let" definitions

RSpec's let allows you to super into "outside" definitions, in parent contexts.

Example:

describe '#save' do
  subject { described_class.new(attributes) }
  let(:attributes) { title: 'Example', user: create(:user) }

  it 'saves' do
    expect(subject.save).to eq(true)
  end

  context 'when trying to set a disallowed title' do
    let(:attributes) { super().merge(title: 'Hello') } # <==

    it 'will not save' do
      expect(subject.save).to eq(false)
    end
  end
end

I suggest you don't make a habit of using this regula...

Integrating ESLint

Introduction

To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.

1. Adding the gem to an existing code base

You can add the following lines to your package.json under devDependencies:

  "devDependencies": {
    "eslint": "^8.7.0",
    "eslint-config-standard": "^16.0.3",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-node"...

How to add esbuild to the rails asset pipeline

This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.

Preparations

  1. update Sprockets to version 4
  2. add a .nvmrc with your preferred node version (and install it)
  3. add gems jsbundling-rails and foreman to your Gemfile:
    gem 'jsbundling-rails'
    group :development, :test do
      gem 'foreman'
      # ...
    end
    
  4. bundle install
  5. run bin/rails javascript:install:esbuild in a console to prepare esbuild.
  6. run yarn install...

PostgreSQL: "WHERE NOT <column> = '<value>'" statements do not include NULL values

Sometimes we write plain SQL queries in migrations so we don't have to mock ActiveRecord classes. These two migrations do the same:

class Migration1 < ActiveRecord::Migration[5.2]
  class User < ActiveRecord::Base; end

  def up
    add_column :users, :trashed, :boolean
    
    User.update_all(trashed: false)
  end
end

class Migration2 < ActiveRecord::Migration[5.2]
  def up
    add_column :users, :trashed, :boolean

    update("UPDATE users SET trashed = #{quoted_false}")
  end
end

The plain SQL migration is less code, but h...

Carrierwave: How to attach files in tests

Attaching files to a field that is handled by Carrierwave uploaders (or maybe any other attachment solution for Rails) in tests allows different approaches. Here is a short summary of the most common methods.

You might also be interested in this card if you see the following error in your test environment:

CarrierWave::FormNotMultipart:
You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.
If this is a file upload, please check that your upload form is multipart encoded.

Factor...

Deployment: Merge consecutive commits without cherry-picking

You want to deploy new features but the latest commits are not ready for production? Then use git merge master~n to skip the n-last commits.

Tip

A big advantage of merging vs. cherry-picking is that cherry-picking will create copies of all picked commits. When you eventually do merge the branch after cherry-picking, you will have duplicate commit messages in your history.

Example

It's time for a production deployment!

git log --pretty=format:"%h - %s" --reverse origin/production..origin/master

0e6ab39f - Feature A
6396...