ActiveStorage: How to add a new preprocessed named version
Given there is a user with an attachable avatar:
class User < ApplicationRecord
has_one_attached :avatar
end
If you want to add a preprocessed version follow these steps:
- Add the named version and deploy
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :preview, resize_to_fit: [177, 177 * 9 / 16], preprocessed: true
end
end
- Preprocess this version for all existing records `bundle exec rails runner 'User.find_each { |user| user.avatar.variant(:preview).proc...
ActiveStorage: How to copy / clone an attachment from one record to another
Given there is a user with an attachable avatar:
class User < ApplicationRecord
has_one_attached :avatar
end
You can copy the avatar from one user to another user with the code below:
user_1 = User.first
user_2 = User.create!(
avatar: {
io: StringIO.new(user_1.avatar.download),
filename: user_1.avatar.blob.filename.to_s,
content_type: user_1.avatar.blob.content_type.to_s,
}
)
Note: For large attachments you might need to use a different approach to avoid memory issues.
Using Low-Level Prompts for High-Accuracy AI Coding
The key to unlocking the full potential of LLMs in coding lies in crafting precise prompts. The main challenge is learning how to structure prompts effectively to guide the model toward accurate results. Further evidence supporting this is the fact that Aider already writes ~70% of its own code (as of 02/2025). However, when starting out, your results may fall short of efficiently generating large portions of your code with the...
Shell script to magically configure display setup
Here is a bash script that I use to auto-configure displays on Ubuntu 24.04 with Xorg.
Background
- Ubuntu always sets the primary display to the 1st (i.e. internal) display whenever I connect to a new Dock/Hub.
- I want my primary display to be the large display.
- My notebook is always placed left of external displays, so the 2nd display will be the center (or only) external display and should be primary.
- I also want all my displays to be placed horizontally, but bottom-aligned (the default would be aligned at their top edges)....
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...
Rails: Accessing strong parameters
Rails wraps your parameters into an interface called StrongParameters. In most cases, your form submits your data in a nested structure which goes hand in hand with the strong parameters interface.
Example:
curl -X POST -d "user[name]=bob" https://example.com/users
class UsersController
def create
User.create!(params.expect(user: [:name])) # Or User.create!(params.require(:user).permit(:name))
end
end
This works well most of the time...
Rails 8 introduces `params.expect`
The new params.expect
method in Rails 8 improves parameter filtering, addressing issues with malformed input and enhancing security. It provides a cleaner, more explicit way to enforce the structure and types of incoming parameters.
What changed
-
Replaces
require
andpermit
: Combines both methods for concise parameter validation. - Explicit Array Handling: Requires double array syntax to define arrays of hashes, improving clarity.
- Enhanced Validation: Ensures expected parameter structure, rejecting malformed input wi...
Rails console tricks
Also see the list of IRB commands.
Switching the context
Changes the "default receiver" of expressions. Can be used to simulate a "debugger situation" where you are "inside" an object. This is especially handy when needing to call private methods – just invoke them, no need to use send
.
- Switch to an object:
chws $object
- Reset to
main
:chws
- Show current context:
cwws
(usually shown in IRB prompt)
[Technical details](https://technology.doximity.com/articles/the-hidden-gems-of-r...
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...
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...
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:
- The
'medium'
issue - The
'low'
issue - The
'critical'
issue
This happens because the database column backing the criticality attribute is a string and PG will use a [collation](...
Switching the package manager from yarn to npm
We recently migrated a Rails application from yarn
to npm
. We decided to go this step instead of upgrading to > Yarn 2.0 to reduce the number of dependencies in our project.
Migration
- Remove the
yarn.lock
file - Remove the
node_modules
folder - Run
npm install
- Replace all occurrences of
yarn
withnpm
in your project
Notes
- With
npm
vendored packages with dependencies create their ownnode_modules
folder within the vendor path. We...
A different testing approach with Minitest and Fixtures
Slow test suites are a major pain point in projects, often due to RSpec
and FactoryBot
. Although minitest
and fixtures are sometimes viewed as outdated, they can greatly improve test speed.
We adopted a project using minitest and fixtures, and while it required some initial refactoring and establishing good practices, the faster test suite was well worth it! Stick with me to explore how these tools might actually be a good practice.
So, why is this setup faster? Partially, it's because minitest is more lightweight than RSpec
, which...
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...
Open Terminator from nautilus context menu
On our Ubuntu machines we have nautilus file manager with nautilus-extension-gnome-terminal
installed. This adds an entry to the context menu (right click) to start a gnome-terminal in the current directory. As I'm mostly using Terminator terminal, I wanted to have a similar context menu entry to launch Terminator directly. I came across this python script that does exactly that.
- Install python3-nautilus:
sudo apt install python3-nautilus
- Create `/usr/share/nautilus-...
How to allow testing beforeunload confirmation dialogs with modern ChromeDrivers
Starting with ChromeDriver 127, if your application displays a beforeunload
confirmation dialog, ChromeDriver will immediately close it. In consequence, any automated tests which try to interact with unload prompts will fail.
This is because ChromeDriver now follows the W3C WebDriver spec which states that any unload prompts should be closed automatically.
However, this applies only to "HTTP" test sessions, i.e. what you're using by default. The spec also defines that bi-directional test se...
High-level data types with "composed_of"
I recently stumbled upon the Rails feature composed_of. One of our applications dealt with a lot of addresses and they were implemented as 7 separate columns in the DB and Rails models. This seemed like a perfect use case to try out this feature.
TLDR
The feature is still a VERY leaky abstraction. I ran into a lot of ugly edge cases.
It also doesn't solve the question of UI. We like to use
simple_form
. It's currently not possible to simply write `f...
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...
How to: Benchmark an Active Record query with a Ruby script
Recently I needed to benchmark an Active Record query for performance measurements. I wrote a small script that runs each query to benchmark 100 times and calculates the 95th percentile.
Note: The script requires sudo permissions to drop RAM cache of PostgreSQL. Due to the number of iterations it was impractical to enter my user password that often. And I temporary edited my /etc/sudoers
to not ask for the sudo password with johndoe ALL=(ALL) NOPASSWD: ALL
.
# Run this script with e.g. `rails ru...
Jasmine: Use `throwUnless` for testing-library's `waitFor`
testing-library are widely used testing utilities libraries for javascript dependent frontend testing. The main utilities provided are query methods, user interactions, dom expectations and interacting with components of several frontend frameworks, which allows us to worry less about the details happening in the browser and focus more on user centric tests instead!
Some of the time you will find a necessity to use methods like [waitFor
](https://testing-library.com/docs/dom-testing-library/api-async/...
How to combine unknown CSS selectors
You are given two CSS selectors that you do not control. How can you build a new selector that matches both of them?
item_selector = 'div'
active_selector = '.is-active'
Can't I just concat these selectors?
# Bad
new_selector = "#{item_selector}#{active_selector}"
# => "div.is-active"
Don't! This will break as soon as one of the selectors is actually a selector list.
item_selector = 'div, span, p' # <- Selector list
new_selector # => "div, span, p.is-active" (wrong)
Solution
Wrap both selectors ...
JavaScript: Listening to a class getting added
Reacting on a class getting added can be done with a mutation observer. Example:
const items = document.querySelectorAll('.item')
const expectedClass = 'active'
const activeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.target.classList.contains(expectedClass) {
// Do something
}
})
})
items.forEach(item => activeObserver.observe(item, { attributes: true, attributeFilter: ['class'] }))
Note that this is not a generic solution – it makes a few assumptions to simplif...
How to speed up JSON rendering with Rails
I was recently asked to optimize the response time of a notoriously slow JSON API endpoint that was backed by a Rails application.
While every existing app will have different performance bottlenecks and optimizing them is a rabbit hole of arbitrary depth, I'd like to demonstrate a few techniques which could help reaching actual improvements.
The baseline
The data flow examined in this card are based on an example barebone rails app, which can be used to reproduce the r...
How to query GraphQL APIs with Ruby
While most Rails Apps are tied to at least one external REST API, machine-to-machine communication via GraphQL is less commonly seen. In this card, I'd like to give a quick intro on how to query a given GraphQL API - without adding any additional library to your existing app.
Core aspects of GraphQL
Interacting with GraphQL feels a bit like querying a local database. You are submitting queries to fetch data in a given structure (like SELECT in SQL) or mutations to alter the database (similar to POST/PUT/DELETE in REST). You can ...