Implementing authentication and authorization for ActiveStorage blobs/files

ActiveStorage does not provide any built-in way of implementing authentication for the available DirectUpload endpoint in Rails. When using DirectUpload as JS wrapper in the frontend, be aware that its Rails endpoint is public by default, effectively allowing anyone to upload an unlimited amount of files to your storage.

The DirectUploadController from @rails/activestorage bypasses your form controller because it uploads the file using an AJAX request that runs directly, before any form roundtrip happens. This is a comfortable solutio...

Overview of method delegation in Rails

Method delegation in Rails can help you to keep your code organized and avoid deep call chains (law of demeter) by forwarding calls from one object to another. Rails provides several ways to accomplish this. Below is a concise overview of the most common approaches:

Single-Method delegation with delegate

Use the built-in delegate method from ActiveSupport to forward specific methods:

class User < ApplicationRecord
  has_one :profile

  delegate :full_name, :age, to: :profile, prefix: true
end
  • `delegate: full_name, :age...

Haml Whitespace Preservation (or: Fixing Textarea Indentation in Haml)

Haml renders HTML with indentation reflecting the nesting level of elements. When it comes to white-space preserving content, this may become a problem: the content will be rendered including the HTML indentation.

Problematic: Preserved Indentation

.nest
  %span Reference
  %pre
    = content
<div class="nest">
    <span>Reference</span>
    <pre>
        Hello
        World
    </pre>
</div>

Image

Better: Without Extra Indentation

Render with tilde ~ instead of equal...

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

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

How to disable telemetry for various open source tools and libraries

Hint

If you are using our opscomplete.com hosting we can set all environment variables mentioned below for your deployment on request.

If you're lucky DO_NOT_TRACK=1 opts you out of CLI telemetry - it's not widely adopted. When you're using any of the libraries below, I'd rather opt out explicitly:

Yarn

https://yarnpkg.com/advanced/telemetry (Since: Version 2.2)

Disable for a project:

#...

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

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

Rails 7 adds #caching? and #uncacheable!

Rails' fragment caching caches subtrees of an HTML document tree. While constructing that tree though, it can be really hard to keep track of whether some code is run in a caching context. Fortunately, Rails 7 brings two helpers that simplify this.

Note that these helpers are all about Rails' fragment caching and not about downstream caching (i.e. Cache-Control).

uncacheable!

Invoke this helper in a partial or another helper that should never be cached. Used outside of fragment caches, the helper does just nothing. But should it ...

Tom Select: How to adjust the delay for options list to update

When users type into a Tom Select control, the options list is updated with a short delay which can feel too long. Here is how to adjust it.

Background

Your users can filter options by typing into the Tom Select control. However, after typing there is a 300ms delay until the options list is filtered.
This can be especially annoying when users type something and quickly press the Return key, as they'd select the option that was just rendered.

There is a loadThrottle option, but it cannot help us here, as...

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

RSpec: Increase readability with super_diff

When handling nested hashes the RSpec output is often hard to read. Here the gem super_diff could help.

Add super_diff to your project

  1. Add super_diff to your Gemfile:
gem 'super_diff'
  1. Require it in your spec_helper.rb
require 'super_diff/rspec' # For Rails applications you can replace this with 'super_diff/rspec-rails'
  1. Customize colors in spec/support/super_diff.rb
SuperDiff.configure do |config|
  config.ac...

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

Jasmine: Dealing with Randomness

Whenever you have to deal with randomness in a jasmine test there are some spy strategies to help you out!

Let's say we have a method Random.shuffle(array) to shuffle an array randomly and a class that uses shuffle within the constructor.

returnValue & returnValues

it('shuffles the array', () => {
  spyOn(Random, 'shuffle').and.returnValue([3, 2, 1])
  array = [1, 2, 3]
  
  testedClass = new testedClass(array)
  
  expect(Random.shuffle).toHaveBeenCalled()
  expect(testedClass.array).toEqual([3, 2, 1])
})

If you have...

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

Ruby: Different ways of assigning multiple attributes

This card is a short summary on different ways of assigning multiple attributes to an instance of a class.

Using positional parameters

Using parameters is the default when assigning attributes. It works good for a small number of attributes, but becomes more difficult to read when using multiple attributes.

Example:

class User
  def initialize(salutation, first_name, last_name, street_and_number, zip_code, city, phone_number, email, newsletter)
    @salutation = salutation
    @first_name = first_name
    @last_name = last_nam...

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

Caveat when using Rails' new "strict locals" feature

In Rails 7.1 it has become possible to annotate partials with the locals they expect:

# partial _user_name.erb
<%# locals: (user:) %>
<%= user.name %>

# view
<%= render 'user_name' %> <%# this raises an ArgumentError %>

Unfortunately, when some other code in that template raises an ArgumentError (for example an error in the User#name method) you will end up with a confusing stacktrace that looks like you have an error in your render call.

If th...

RSpec: Using helpers in view specs

If an view spec crashes due to undefined helper methods, you can enable this option:

# config/application.rb
config.action_controller.include_all_helpers = true

If you cannot use this setting, your spec can include individual helper modules like this:

describe 'some view', type: :view do
  helper SomeHelper
  helper OtherHelper

  it 'renders' do
    render 'view_that_uses_helpers'
  end
end

Alternatively you can also explicitly include *all help...