Friday, August 14, 2026

Agents on Rails, query method and more

Posted by Wojtek

Hi, Wojtek here. Let’s explore this week’s news in the Rails.

Agents on Rails
Read the announcement and the first benchmark report.

Add support for the HTTP QUERY method
QUERY is a safe and idempotent HTTP method that conveys the query in the request content, making it suitable for queries too large or structured for a URL query string:

# config/routes.rb
query "search", to: "search#index"
match "filter", to: "search#filter", via: :query

# request handling
request.query?                # => true
request.request_method_symbol # => :query

# integration tests
query "/search", params: { filters: { status: "active" } }, as: :json

Show bin/console startup banner based on IRB
Show a Rails-flavored startup banner (a small logo, Rails/Ruby version, a rotating tip about console helpers like reload!, and Rails.root) when starting bin/rails console.

Set RAILS_TIPS=false to hide the rotating tip.

Allow ffmpeg and ffprobe input arguments to be configured
Two new configuration parameters are introduced. config.active_storage.video_preview_input_arguments is passed to ffmpeg before -i, and config.active_storage.ffprobe_arguments is passed to ffprobe before the file path. Both default to empty string.

ffmpeg’s flags are position dependent, and these parameters carry the ones that apply to the input, such as -codec_whitelist, -f, and -protocol_whitelist. For example, an application that accepts only H.264 video with AAC audio:

config.active_storage.video_preview_input_arguments = "-codec_whitelist h264,aac"
config.active_storage.ffprobe_arguments = "-codec_whitelist h264,aac"

Allow config.active_storage.variant_processor to be set to a class
The class must implement the interface defined by ActiveStorage::Transformers::Transformer. Active Storage then uses it for variant processing:

config.active_storage.variant_processor = CustomTransformer

The schema readers answer for many tables at once
indexes / primary_keys / foreign_keys / check_constraints / exclusion_constraints / unique_constraints now accept a list of tables and answer for all of them at once:

connection.indexes(:users)            # => [IndexDefinition, ...]
connection.indexes([:users, :posts])  # => { "users" => [...], "posts" => [...] }

Fix LengthValidator crash with proc minimum and nil value
The proc was resolved only when the value was present, so for a nil value it leaked unresolved into the error message and was invoked with the message options hash instead of the record. It is now resolved before the error is built, producing the expected “is too short” message.

validates_length_of :title, minimum: ->(record) { record.min_length }
# title = nil now yields "is too short (minimum is N characters)"
# instead of raising NoMethodError

Use bind parameters for array-form arguments in find_by_sql / count_by_sql
The API doc claims parity with where, but array-form values were eagerly interpolated via sanitize_sql.

Deprecate create alias of insert in connection adapters
insert / update / delete correspond to their SQL DML verbs, but create reads like DDL (create_table / create_database) rather than the INSERT it actually performs.

Split keyword arguments off #args on Rails.application.middleware entries
Middleware entries now expose keyword arguments through a new #kwargs accessor. #args previously bundled kwargs as a trailing hash inside the positional array (via Hash.ruby2_keywords_hash). It now returns positional arguments only.

Allow to configure ERB options through ActionView::Base
The ActionView::Template::Handlers::ERB class is now private API. Applications that used to configure ERB options such as escape_ignore_list now need to do this on the ActionView::Base class or on the railtie config.action_view configuration.

Append TRADITIONAL instead of STRICT_ALL_TABLES to MySQL’s sql_mode by default
On environments whose global sql_mode is empty — most notably Amazon RDS and Aurora MySQL default parameter groups — appending only STRICT_ALL_TABLES leaves out NO_ZERO_IN_DATE, NO_ZERO_DATE, and ERROR_FOR_DIVISION_BY_ZERO, which MySQL 5.7+ has otherwise made part of its own default. Appending TRADITIONAL closes the gap.

Allow ActiveSupport::ProxyLogger to ignore messages by pattern
Useful to silence noisy logs from gems that your application may not care about, without needing to change the log level and losing other useful logs.

Add Pacific Time (Canada) and Alberta Time zone mappings
Adds two friendly zone mappings:

  • Pacific Time (Canada)America/Vancouver
  • AlbertaAmerica/Edmonton

Change the shape of ActiveRecord::Migration::CommandRecorder#commands
Each recorded migration command is now stored as [cmd, args, kwargs, block] (4-element) instead of [cmd, args, block] (3-element) with kwargs bundled into a trailing hash inside args. Code that inspects recorder.commands directly needs to adapt to the new tuple shape.

Fix normalizes to run before the underlying type validates an assigned value
When normalizes was combined with another type that rejects invalid input (such as an Active Record enum), the underlying type’s assert_valid_value ran against the raw, un-normalized value and raised before normalization had a chance to run. The normalization is now applied first, so a value like “ Pending “ is normalized to “pending” and accepted by the enum.

You can view the whole list of changes here.
We had 28 contributors to the Rails codebase this past week!

Until next time!

Subscribe to get these updates mailed to you.