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...
How to Work With Time Zones in Rails
When dealing with time zones in Rails, there is one key fact to keep in mind:
Rails has configurable time zones, while
Ruby is always in the server's time zone
Thus, using Ruby's time API will give you wrong results for different time zones.
"Without" time zones
You can not actually disable time zones, because their existence is a fact. You can, however, tell Rails the only single time zone you'll need is the server's.
config.time_zone = "Berlin" # Local time zone
config.active_record.default_timezone = :loca...
Rails: Preloading associations in loaded records
Sometimes you want to fetch associations for an ActiveRecord that you already loaded, e.g. when it has deeply nested associations.
Edge Rider gives your models a static method preload_associations
. The method can be used to preload associations for loaded objects like this:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@user.preload_associations(threads: { posts: :author }, messages: :sender)
end
end
The attached initializers re...
PSA: "index: true" in Rails migrations does not work as you'd expect
Several Rails migration methods accept index: true
as an option to create an index. In some cases (like #add_column
), this option is silently discarded. Know what you are doing, or use #add_index
instead.
Example
Consider the following migration.
class CreateExamples < ActiveRecord::Migration
def change
create_table :examples do |t|
t.references :category, index: true
t.boolean :positive, index: true
t.integer :number_of_participants, index: true
end
add_reference :examples, :user, index: tr...
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...
How to 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...
How to organize large I18n dictionaries in Ruby on Rails
If you're suffering from a huge de.yml
or similiar file, cry no more. Rails lets you freely organize your dictionary files in config/locales
.
My organization works like this:
-
config/locales/rails.de.yml
modified Rails boilerplate -
config/locales/faker.de.yml
modified Faker boilerplate -
config/locales/models.de.yml
model names, attribute names, assignable_value labels - `config/locales/views.de.y...
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...
Rails route namespacing (in different flavors)
TL;DR There are three dimensions you can control when scoping routes: path helpers, URL segments, and controller/view module.
scope module: 'module', path: 'url_prefix', as: 'path_helper_name' do
resources :examples, only: :index
end
as
→ prefixes path helpers: path_helper_name_examples_path
and path_helper_name_examples_url
path
→ prefixes URL segments: /url_prefix/examples
module
→ nests the controller: controller Module::ExamplesController
, found at app/controllers/module/examples_controller.rb with views ...
Rails: Handling actions that need to happen after all transactions
In Rails 7.2. the feature ActiveRecord.after_all_transactions_commit
was added, for code that may run either inside or outside a transaction (we have a special card for nested transactions in general) and needs to perform work after the state changes have been properly persisted. e.g. sending an email.
Example
def publish_article(article)
article.update(published: true)
ActiveRecord.after_all_transactions_commit do
PublishNotification...
Auto-generating plain-text bodies for HTML e-mails in Rails apps
When building an application that sends e-mails to users, you want to avoid those e-mails from being classified as spam. Most obvious scoring issues will not be relevant to you because you are not a spammer.
However, your application must do one thing by itself: When sending HTML e-mails, you should include a plain-text body or tools like SpamAssassin will apply a significant score penalty. Here is how to do that automatically.
- Add
premailer-rails
to yourGemfile
andbundle
. - Done! ...
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...
Rails: Redirecting the Logger output temporary aka showing Rails logs in the console
Most of the time, when you are interested in any log output,
- you see the logs directly on your console
- or you tail / grep some logfile in a separate terminal window
In rare cases it's helpful to redirect the Logger output temporary to e.g. STDOUT
.
Rails.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger = Logger.new(STDOUT)
User.save!
#=> D, [2025-09-08T11:12:26.683106 #1094157] DEBUG -- : User Load (1.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
Many frameworks in Rails ...
Guide to localizing a Rails application
Localizing a non-trivial application can be a huge undertaking. This card will give you an overview over the many components that are affected.
When you are asked to give an estimate for the effort involved, go through the list below and check which points are covered by your requirements. Work with a developer who has done a full-app localization before and assign an hour estimate to each of these points.
Static text
- Static strings and template text in
app
must be translated: Screens, mailer templates, PDF templates, helpe...
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...
Ruby / Rails: clone vs. dup vs. deep_dup
Ruby and Rails have several methods for creating a new object that looks like another: clone
, dup
, deep_dup
. When using them you should be aware of their differences so that you can select the method you really need.
clone
- Shallow copy: references to other objects/values are copied (instead of cloning those objects/values)
- Clones the object and all its "special object attributes" like
frozen
,tainted
and modules that the object has been extended with - [Ruby 2.6 documentation for clone](https://devdocs.io/ruby~2.6/obj...
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 |
... |
Rails: namespacing models with table_name_prefix instead of table_name
When you want to group rails models of a logical context, namespaces are your friend. However, if you have a lot of classes in the same namespace it might be tedious to specify the table name for each class seperately:
class Accounting::Invoice < ApplicationRecord
self.table_name = 'accounting_invoices'
...
end
class Accounting::Payment < ApplicationRecord
self.table_name = 'accounting_payments'
...
end
A replacement for the self.table_name
-assignment is the table_name_prefix
in the module definition:
modu...
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 and Postgres: How to test if your index is used as expected
This is a small example on how you can check if your Postgres index can be used by a specific query in you Rails application. For more complex execution plans it might still be a good idea to use the same path of proof.
1. Identify the query your application produces
query = User.order(:last_name, :created_at).to_sql
puts query
# => SELECT "users".* FROM "users" ORDER BY "users"."last_name" ASC, "users"."created_at" ASC
2. Add an index in your migration and migrate
add_index :users, [:last_name, :created_at]...
Rails: Including HTML in your i18n locales
TL;DR
Append your locale keys with _html to have them marked as
html_safe
and translate them with= t('.text_html')
.
When you're localizing a Rails application, sometimes there is this urge to include a little HTML. Be it some localized link, or a set of <em>
tags, you'd like to have it included in the locale file. Example:
# Locale file
en:
page:
text: 'Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.'
# HAML
= t('.text')
# D...
Rails: When to use :inverse_of in has_many, has_one or belongs_to associations
When you have two models in a has_many
, has_one
or belongs_to
association, the :inverse_of
option in Rails tells ActiveRecord that they're two sides of the same association.
Example with a has_many
/ belongs_to
association:
class Forum < ActiveRecord::Base
has_many :posts, inverse_of: :forum
end
class Post < ActiveRecord::Base
belongs_to :forum, inverse_of: :posts
end
Knowing the other side of the same association Rails can optimize object loading so forum
and forum.posts[0].forum
will reference the same o...
How Rails chooses error pages (404, 500, ...) for exceptions
When your controller action raises an unhandled exception, Rails will look at the exception's class
and choose an appropriate HTTP status code and error page for the response.
For instance, an ActiveRecord::RecordNotFound
will cause Rails to render a red "The page you were looking for doesn't exist" with a status code of "404" (not found).
The mapping from exception classes to error types is a hash in Rails.configuration.action_dispatch.rescue_responses
. The...
Rails 8: The db:migrate task might not run all migrations in db/migrate/ anymore
In Rails 8 the behavior of the rails db:migrate
command has changed for fresh databases (see PR #52830).
- Before Rails 8: The command runs all migrations in the folder
db/migrate/*
- After Rails 8: The command loads the schema file (
db/schema.rb
ordb/structure.sql
) if existing and runs all pending migrations in the folderdb/migrate/*
afterwards
This speeds up the command. But e.g. migrations with data manipulations are missing.
The only way to run all pending mig...