Rails I18n fallback locales

When you need to create a locale for a language variant (like Austrian for German), you probably don't want to duplicate your entire de.yml file only to change a few minor exceptions for our Austrian friends.

Luckily, the I18n gem used by Rails has a fallback feature where you can make one locale file fall back to another if no translation is available.

In the example above you would have a config/locales/de_DE.yml:

de_DE:
  # hundreds of translations here

... and another...

Unpoly: Showing the better_errors page when Rails raises an error

When an AJAX request raises an exception on the server, Rails will show a minimal error page with only basic information. Because all Unpoly updates work using AJAX requests, you won't get the more detailled better_errors page with the interactive REPL.

Below is an event listener that automatically repeats the request as a full-page load if your development error shows an error page. This means you get...

Sidekiq: Problems and Troubleshooting

When using Sidekiq in your application, you must write thread-safe code.

This wiki page also lists gems that are known to be unsafe on threaded applications.
When adding a gem that will also be used by a Sidekiq worker, make sure to confirm it's thread-safe.

Caution when using the || operator to set defaults

I often see the use of || to set a default value for a variable that might be nil, null or undefined.

x = x || 'default-value'

This pattern should be avoided in all languages.

While using || works as intended when x is null or an actual object, it also sets the default value for other falsy values, such as false. false is a non-blank value that you never want to override with a default.

To make it worse, languages like JavaScript or Perl have [many more fal...

Caching in Rails < 6.1 may down parts of your application when using public cache control

TL;DR When using Cache-Control on a Rails application, make sure the Vary: Accept header is set.

Proxy caching is a good feature to serve your publicly visible application content faster and reduce load on your servers. It is e.g. available in nginx, but also affects proxies delivered by ISPs.

Unfortunately, there is a little problem in Rails < 6.1 when delivering responses for different MIME-types. Say you have an arbitrary route in your Rails application that is able to respond with regular HTML and JSON. By sending the specific ...

Reverse lookup a fixture name by its id and table name

To reverse lookup a fixture by its table name and id, use the following approach on ActiveRecord::FixtureSet:

table = 'users'       # Specify the fixture table name
id = 123122           # Specify the ID to look for

# Find the fixture that matches the given ID
ActiveRecord::FixtureSet.all_loaded_fixtures[table].fixtures.find { |key, value| value['id'] == id }

Result Example:

[
  "one", # Fixture name
  #<ActiveRecord::Fixture:0x00007e79990234c8>, # ActiveRecord::Fixture object
  @fixture= { ... }, # The raw fixtu...

Updated: How to allow testing beforeunload confirmation dialogs with modern ChromeDrivers

Updated for selenium-webdriver 4.27 which requires options.add_option(:page_load_strategy, 'none').

NPM: How to verify that your package-lock.json fulfills dependencies of package.json

Your package-lock.json should always match and resolve all packages from your package.json.
Coming from Yarn, I was looking for an option like Yarn's --frozen-lockfile which validates that. Here is what seems to be the way to do it.

Using npm clean-install

Running npm clean-install instead of npm install will actually validate that your package-lock.json matches your package.json.
If your package-lock.json cannot be fulfilled, npm install would install other versions while npm clean-install will complain.

You can ...

Rails: Looking up constants by their name string

TL;DR: Rails ships two methods to convert strings to constants, constantize and safe_constantize. Neither is safe for untrusted user input. Before you call either method you must validate the input string against an allowlist. The only difference between the two methods is that unresolvable constants raise an error with constantize, but return nil with safe_constantize. If you validate the input string against an allowlist, an error should never happen.

Preventing Dangerous Lookups

Suppose an application uses eit...

How to disable logging for ActiveStorage's Disk Service routes

In development, we store files using ActiveStorage's disk service. This means that stored files are served by your Rails application, and every request to a file results in (at least!) one non-trivial log entry which can be annoying. Here is how to disable those log entries.

Example

Here is an example of what loading a single <img> in an example application writes to the Rails log.

Started GET "/rails/active_storage/blobs/redirect/..." for ::1 at ...
Processing by ActiveStorage::Blobs::RedirectController#show as SVG
  Parameter...

Ignore Selenium driver deprecations

If you update Selenium regularly, you'll run into deprecation warnings similar to:

WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release

You can ignore this deprecation warning and clean your logs with logfilters:

Selenium::WebDriver.logger.ignore(:clear_local_storage)

Just specify the tag (:clear_local_storage) you want to ignore.

PSA: Umlauts are not always what they seem to be

When you have a string containing umlauts which don't behave as expected (are not matched with a regexp, can't be found with an SQL query, do not print correctly on LaTeX documents, etc), you may be encountering umlauts which are not actually umlaut characters.

They look, depending on the font, like their "real" umlaut counterpart:

  • ä ↔ ä
  • ö ↔ ö
  • ü ↔ ü

However, they are not the same:

'ä' == 'ä' # false
'ä'.size # 1
'ä'.size # 2

Looking at how those strings are constructed reveals what is going on:

'ä'.unpack('U*...

How to combine "change", "up", and "down" in a Rails migration

Rails migrations allow you to use a change method whose calls are automatically inverted for the down path. However, if you need to some path-specific logic (like SQL UPDATE statements) you can not define up and down methods at the same time.

If you were to define define all 3 of them, Rails would only run change and ignore up and down. However, Rails 4+ features a helper method called reversible:

class MyMigration < ActiveRecord::Migration

  def cha...

RubyMine users: you should be using bookmarks

RubyMine allows bookmarking lines of code. This is super-helpful when working on a complex problem.
I've been using this feature for a few years now, and so should you! :)

Here are the default Linux/Windows keystrokes. See the documentation for other keybindings.

Add an anonymous bookmark

F11

A gray checkmark will be shown in the gutter on the left.
If you press F11 again on a bookmarked line, the bookmark will be removed.

Add a named bookmark ("mnemonic")

Ctrl ...

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 Rails' file_fixture helper in FactoryBot

In FactoryBot factories, Rails' file_fixture is not available by default. To enable it, include a support module from rspec-rails:

FactoryBot::SyntaxRunner.include(RSpec::Rails::FileFixtureSupport)

That includes ActiveSupport::Testing::FileFixtures, where file_fixture is defined, but also configures the file_fixture_path so that you can actually use file_fixture.

RSpec: Scoping custom matchers to example groups

When you find yourself in the situation that you would like to define a custom matcher in your specs, but you do not want to define a global matcher since you need it only for your specific test, there are two ways to do it:

Custom matcher for a single group

If you're only going to include a matcher once, you can also use the matcher macro within an example group:

describe "group" do
  
  matcher :be_just_like do |expected|
    match {|ac...

How to tackle complex refactorings in big projects

Sometimes huge refactorings or refactoring of core concepts of your application are necessary for being able to meet new requirements or to keep your application maintainable on the long run. Here are some thoughts about how to approach such challenges.

Break it down

Try to break your refactoring down in different parts. Try to make tests green for each part of your refactoring as soon as possible and only move to the next big part if your tests are fixed. It's not a good idea to work for weeks or months and wait for all puzzle pieces ...

Getting permanent links to files on Github or Gitlab

Please don't simply copy line number links from Github. The URL usually contains a branch name like master which will change over time:

https://github.com/makandra/upjs/blob/master/lib/assets/javascripts/up/link.js.coffee#L76

If someone now posts an insertion or deletion to that file into master your link points to the wrong line!

A better way is to press the Y key after clicking on a line number. This will transform the URL to another URL that points to the particular commit:

https://github.com/makandra/upjs/blob/b3b14...

TestProf II: Factory therapy for your Ruby tests—Martian Chronicles, Evil Martians’ team blog

Some key highlights and points from the linked article TestProf II: Factory therapy for your Ruby tests.

The Problem with Factories in Ruby Tests

  • Factories are used to easily generate test data.
  • However, they can unintentionally slow down test suites by creating unnecessary or excessive associated data (factory cascades).

Understanding Factory-Induced Slowdowns

  • Factories often create additional data (e.g., associated records) th...

Fragment Caching in Rails 7.1+ requires Haml 6

Rails slightly changed the fragment cache implementation from Rails 7.0 to Rails 7.1. Unfortunately, this is incompatible with how Haml 5 patches Action View buffers. I tried turning a String buffer into an ActionView::OutputBuffer, but this brought up...

Text fragments in the browser URI fragment

Text fragments allow linking directly to a specific portion of text in a web document, without requiring the author to annotate it with an ID, using particular syntax in the URL fragment. Supporting browsers are free to choose how to draw attention to the linked text, e.g. with a color highlight and/or scrolling to the content on the page. This is useful because it allows web content authors to deep-link to other content they don't control, without relying on the presence of IDs to make that possible. Building on top of that, it could be u...

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

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