Hey everyone — Sajjad Umar here with the August edition of Ruby on Rails Monthly!
A quick personal note before we dive in: I had to skip the July 2025 issue. Between a packed schedule and my 3.5-year-old daughter taking a fall down the stairs and breaking her arm, things got a little overwhelming. She’s doing much better now, thankfully. Thanks for bearing with me — this edition makes up for the gap by digging deeper into the biggest changes since mid-June.
Our Proud Partners
rubyonrails.pk — for the love of rails in Pakistan
adex.world — Free cross-promotion platform
Let's dive in!
Rails Foundation
New written & video tutorials for beginners
The Rails Foundation, in collaboration with Chris Oliver and the Typecraft team, has rolled out several learning resources for newcomers — both written and video.
They’ve launched a new written tutorial, “Sign Up & Settings,” which builds on the updated Getting Started guide. It teaches you how to set up admin accounts, allow user registrations, manage nested layouts, add rate limiting, and write tests for different user roles. This continues the e-commerce app series started last year.
For visual learners, there’s now a video series called “Rails New” by Typecraft. Across 10 beginner-friendly episodes, you’ll go from installation through rails new
, scaffolding, MVC fundamentals, styling with TailwindCSS, and even deploying with Kamal.
These additions reflect the Education mission of the Rails Foundation — and are a great boost for anyone learning Rails from scratch.
Check the full playlist here.
Structured Event Reporting lands in Rails!
Rails now offers a dedicated Event Reporter, available through Rails.event
, designed for producing richly structured events—spanning structured logs, telemetry, and business events—all via a unified interface.
When emitting events, Rails now automatically augments them with consistent metadata:
Name of the event
Payload — with arbitrary data or schematized object
Tags — domain-specific, stackable metadata
Context — request/job-scoped information, e.g.,
request_id
,user_agent
Timestamp at nanosecond precision
Source location — file path, line number, and label
Rails adds tools to streamline event enrichment:
Rails.event.tagged(...) { ... }
— wraps events in nested tagsRails.event.set_context(...)
— sets contextual metadata for the entire request/job scope
Through Rails.event.with_debug
, developers can conditionally emit debug-level events:
Rails.event.with_debug do
Rails.event.debug("sql_query", { sql: "SELECT * ..." })
end
These events are emitted only if debug mode is active.
Emission is decoupled from consumption:
Developers define subscribers implementing an
emit(event)
method.These subscribers receive the complete structured event and decide how to handle it — e.g., serialize, forward, or log.
The framework doesn’t bake in any default consumers, giving flexibility to applications to define processing pipelines
Rails now supports test assertions to verify event emissions:
assert_event_reported("user.created", payload: { id: 123 }) do
UserService.create_user(name: "John")
end
assert_no_event_reported("user.deleted") do
UserService.update_user(...)
end
These make it easier to validate expected event behaviors in your app’s test suite.
What This Means for Rails Developers
Observability and analytics: Rails now ships with a first-class event reporting system, enabling structured data capture for logs, metrics, or business signals.
Greater flexibility: With subscriber-driven architecture, apps can define how events are processed — whether that means JSON serialization, external services, or custom pipelines.
Improved testing: Event assertions allow reliable testing of telemetry and business event behaviors.
Optional layering: Rails doesn’t force this system onto all apps; developers can adopt it gradually, integrating as needed.
Learn more about it here.
assert_events_reported
Test Helper
Rails PR #55497 adds the assert_events_reported
helper for testing event-driven behavior. It lets you assert that certain events were emitted during a block, without caring about order or extra events.
Example:
assert_events_reported("event-x", "event-z") do
CheckoutService.run_checkout
endru
You can even check payloads:
assert_events_reported(["user.created", { id: 123 }]) do
UserService.create_user(id: 123, name: "John")
end
This makes it much easier to test logging, telemetry, background jobs, and business events — focusing on intent rather than sequence.
deliver_all_later
for Bulk Enqueuing Emails
In PR #55448, Rails introduces a handy new method — ActionMailer.deliver_all_later
—that lets you enqueue multiple emails in one go, drastically cutting down on queue round-trips.
A quick code peek:
user_emails = User.all.map { |user| Notifier.welcome(user) }
ActionMailer.deliver_all_later(user_emails)
# Or enqueue with a custom queue:
ActionMailer.deliver_all_later(user_emails, queue: :my_queue)
I’ve been needing this exact feature — imagine sending thousands of welcome emails efficiently without looping one by one. This method batches them all into a single call, making your mailer pipeline leaner and faster.
What’s cool is how naturally it fits into Rails. It builds on an existing concept — similar to what we’ve seen with bulk actions elsewhere — and now it applies it to email delivery specifically.
Add “Copy as Text” Button to Error Pages
In PR #55431, Rails added a neat little UX feature: a “Copy as Text” button on error pages. As a developer, I love this. Often when debugging, I find myself copying error messages manually — whether to Google them, share with teammates, or even paste into an AI assistant. This button does that in one click.
The button gets added to the error page header and grabs a plain-text version of the error (reusing the already formatted log output, which is concise and helpful) onto your clipboard — no manual selection needed.
Removing autocomplete="off"
from Hidden Inputs in button_to
In PR #55336, Rails cleaned up HTML by removing the unnecessary autocomplete="off"
attribute from hidden inputs generated by methods like button_to
, token_tag
, and method_tag
. This attribute was redundant and resulted in invalid markup. GitHub
The change simplifies the generated form helpers without altering behavior — other than cleaner HTML. Tests were updated accordingly to reflect this removal.
Composite Model Channels for ActionCable stream_for
In this PR, Rails enhances ActionCable::Channel#stream_for
to accept multiple model objects—just like Turbo::Broadcastable#broadcasting_to
. Clauses like the following are now supported:
class BroadcastSignalChannel < ApplicationCable::Channel
def subscribed
return reject unless set_account_broadcast
stream_for [@account_broadcast, current_user]
end
# ...
end
This adjustment means you can effortlessly stream to a combination of targets — think model and its owner — without manually wiring up multiple streams. It’s especially useful when you’re doing more than just rendering HTML, such as powering WebRTC signaling over ActionCable.
Shared Rate Limits Across Controllers
Rails now lets you define shared rate limits across multiple controllers using a new scope
option—great when you want to rate-limit groups of endpoints together (like all API routes), instead of per-controller by default.
Here’s how it works: instead of this:
class API::PostsController < ApplicationController
rate_limit to: 100, within: 1.minute
end
class API::UsersController < ApplicationController
rate_limit to: 100, within: 1.minute
end
…where each controller gets its own counter, you can now do this:
class APIController < ActionController::API
rate_limit to: 100, within: 1.minute, scope: :api_global
end
class API::PostsController < APIController
# inherits same scoped limit
end
class API::UsersController < APIController
# shares limit with others under :api_global
end
This means API::PostsController
and API::UsersController
share the same rate limit counter, enforcing a global cap across both. The implementation adjusts the cache key generation to use scope
instead of the default controller_path
when provided or falling back to it when not.
Support for rack.response_finished
Callbacks in ActionDispatch::Executor
This PR includes support for Rack’s rack.response_finished
callbacks in ActionDispatch::Executor
, allowing Rails apps to defer certain “completion” operations until after the full response lifecycle—ideal for cleanup or logging logic that runs too early otherwise.
In simpler terms: Previously, Rails’ executor middleware ran “ensure”-style callbacks immediately when the controller action finished. Now, if your server implements rack.response_finished
Those callbacks can wait until the response is fully sent—so they run at just the right moment, not before your app’s cleanup logic has had a chance to wrap up.
Configurable Render Tracker in Action View
Rails now makes its render tracker configurable, giving developers the option to choose between different parser-based implementations. Previously, Rails experimented with a Ripper-based tracker but rolled it back. With the Prism parser now becoming the default in Rails 7.2, this change introduces a cleaner, more reliable approach for detecting render
calls.
New Rails 8.1 apps will default to the Ruby-parser-based tracker, while existing apps can switch rendering strategies through a simple configuration setting. These new trackers not only improve accuracy but also simplify maintenance, especially in tricky cases like trailing interpolation.
You can check out the full discussion and implementation details in the PR here.
credentials:fetch
Command Added to Rails Credentials API
Rails now provides a new command, rails credentials:fetch
, making it easier to extract specific secrets directly from your encrypted credentials. Previously, accessing a credential meant using rails credentials:edit
or show
, which required manual extraction or parsing. Now, you can retrieve a credential's raw value—ideal for scripting or populating environment variables.
For example:
RAILS_MASTER_KEY=... so my script can decrypt...
KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal/registry_password)
This handy addition is particularly useful in automation and deployment scenarios — for instance, injecting secrets into tools like Kamal or CI pipelines.
You can explore the full discussion and implementation in the pull request itself.
Making ActiveSupport::Logger
#freeze
-Friendly
Rails introduced a subtle but practical change in PR 55465 that smooths out behavior when loggers are frozen.
Previously, calling methods like logger.level
or logger.debug
on a frozen ActiveSupport::Logger
could raise a FrozenError
. This PR fixes that by ensuring the internal @local_level_key
instance variable is initialized eagerly—meaning the logger can operate normally even after being frozen.
A quick code example from the change:
def local_level_key
@local_level_key ||= :"logger_thread_safe_level_#{object_id}"
end
attr_reader :local_level_key
And the added test that confirms the fix works without crashing:
logger.freeze
assert_nothing_raised do
assert_equal Logger::DEBUG, logger.level
end
To sum it up: this is a neat quality-of-life improvement that avoids unnecessary errors in frozen-logger scenarios — keeping things stable, predictable, and developer-friendly.
ERB::Util.html_escape
: Stop “Tidying” Bytes for Better Performance
PR 55467 improves the ERB::Util.html_escape
method by removing the now-unnecessary call to Unicode.tidy_bytes
. Introduced about 10 years ago, that tidying step was intended to clean up invalid strings—but it added significant performance overhead. As the PR author noted: “Semantically, it makes no sense to deal with invalid strings at that layer, and performance-wise it imposes a massive overhead.”
The benchmarks speak for themselves:
current (with tidy_bytes): ~5.6 million ops/sec
no_tidy (without tidy_bytes): ~21.8 million ops/sec — nearly 4× faster!
This is the kind of optimization that silently boosts performance without changing behavior.
Improved Exception Logging for rescue_from
This PR enhances Rails’ exception monitoring by adding logging when a rescue_from
handler intercepts an exception. Traditionally, rescue_from
silently swallowed exceptions in controllers, which could make debugging harder—especially when behaviors weren’t occurring as expected. By logging these incidents, Rails surfaces helpful context whenever an exception is caught and handled, without breaking existing flows.
The change introduces a log entry within the rescue_from
flow (typically in controllers), enabling developers to see in the logs exactly which handler ran and for which exception:
rescue_from SomeError, with: :handle_some_error
def handle_some_error(exception)
# Now the log will include that this exception was caught
render ...
end
Check the full discussion in the PR.
Allow Redirects from Configured hosts
PR #55420: apps can now safely allow redirects to domains specified in your configuration via a new allowed_redirect_hosts
setting.
Before this change, the framework either raised errors or blocked redirects to unauthorized hosts, offering little flexibility for trusted external redirects. The update introduces a configuration point that makes redirect behavior both secure and adaptable.
You can configure it like this:
# config/application.rb or an environment file
config.action_controller.allowed_redirect_hosts = ["example.com", /.*\.trusted\.org/]
With this setup, Rails now permits redirects to "example.com"
or any host that matches *.trusted.org
, while still guarding against unsafe or open redirect vulnerabilities.
More Rate Limit Notification Data for Instrumentation
Rails now enriches rate limit notifications with deeper context to support better observability and migration from tools like rack-attack
. The change adds several useful payload attributes to the rate_limit.action_controller
event—specifically: count
, to
, within
, by
, name
, and cache_key.
Previously, the payload only included the request
, which made it tricky to introspect or analyze throttling behavior. Now, with fields like:
count
: how many requests have been made so far,to
: the allowed maximum,within
: the time window,by
: how the request is being scoped (like IP or domain),name
: the identifier for the rate limit rule,cache_key
: the key used in the cache store—
…you get a richer snapshot of what’s happening when a rate limit is hit.
Expose current_transaction.isolation
to Inspect Transaction Isolation
PR #55407 — adds the connection.current_transaction.isolation
API. This lets you programmatically find out the current database transaction's isolation level.
Before this change, once you entered a transaction, there wasn’t a clean, supported way in Rails to inspect what isolation level was in use. Now you can do:
ActiveRecord::Base.connection.current_transaction.isolation
# => :read_committed, :serializable, etc., if explicitly set
# => nil if no custom isolation level is set
It even works well with nested transactions — reporting the parent transaction’s isolation level if none was set explicitly in the inner one. So, it behaves intuitively in real-world use.
relative_time_in_words
Helper Added to Action View
Action View now includes a handy relative_time_in_words
helper. Here’s how the new helper works in practice:
relative_time_in_words(3.minutes.from_now) # => "in 3 minutes"
relative_time_in_words(3.minutes.ago) # => "3 minutes ago"
relative_time_in_words(10.seconds.ago, include_seconds: true)
# => "less than 10 seconds ago"
This method complements existing helpers like time_ago_in_words
but reads more naturally by adapting language based on whether the time is in the future or past.
Check the full PR here.
Avoid Dynamic Encryption in Generated Fixtures
In Rails PR #55387, the fixture generator got an upgrade: instead of dynamically encrypting password digests on every test run, Rails now precomputes the BCrypt hash once during fixture generation. This shift improves test consistency and performance.
Key Improvements:
The
password_digest
in fixtures is now a fixed hash, not regenerated each run.A comment is included in the fixture file, e.g.
password_digest: <computed_hash> # Generated with BCrypt::Password.create("secret")
The corresponding test was updated to verify the hash is a valid BCrypt string.
Dropping JRuby Default Platforms from Gemfile.lock
Rails is now instructing Bundler not to include the default JRuby platforms unless explicitly required in your Gemfile
.
Previously, Bundler often auto-included JRuby (and other default platforms) in the Gemfile.lock
, which could cause confusion, mismatched dependencies, or deployment issues on non-JRuby environments—even when those gems were never used.
With this PR:
Bundler is prevented from automatically adding JRuby-related platforms (and possibly others) when generating the lock file.
Your
Gemfile.lock
now stays cleaner and more aligned with the platforms you actually need.This helps avoid cross-platform discrepancies, improves clarity in team environments (especially when targeting MRI Ruby), and reduces unintended dependency mismatches during deployment or CI.
Check the PR here.
Making ActiveSupport::Gzip.compress
Deterministic (Rails PR #55382)
Behind-the-scenes upgrade in PR #55382: ActiveSupport::Gzip.compress
now produces deterministic outputs. Previously, each call generated a slightly different compressed string because a timestamp was embedded in the gzip header—making tests brittle when comparing compressed data. Now, Rails always sets the timestamp to zero during compression, ensuring that the output remains exactly the same across runs.
This practically eliminates flaky tests related to gzip comparisons. =
Fix for HashWithIndifferentAccess#transform_keys!
to Prevent Key Collisions
A helpful bug fix in PR #55376 that addresses an edge case in HashWithIndifferentAccess
. Previously, calling the destructive method transform_keys!
could inadvertently drop keys when transformations caused duplicates—because the hash would delete a key before its new transformed key was added, leading to unexpected data loss.
With this update, Rails ensures that transformations no longer collide in a way that drops values unintentionally. If two original keys map to the same new key, their values will now be preserved correctly instead of one vanishing silently.
Why it matters:
Better reliability: You can confidently use
transform_keys!
in-place without worrying about dropped entries.Safer transformations: Ensures data integrity when converting key formats (e.g., string → symbol) or case changes.
Warning for PostgreSQL 18 with Outdated pg
Gem
Rails now adds a helpful compatibility check: if your application uses PostgreSQL 18 or newer but is running an older version of the pg
gem (below 1.6.0), Rails will emit a warning. This addresses known compatibility issues with PG::Connection#cancel
, which can break when the gem and Postgres versions don't align.
Check the full PR here.
Action Cable Redis 5.4 Compatibility Fix
Rails PR #55359 updates the Redis subscription adapter so Action Cable now works seamlessly with Redis 5.4.1, ensuring real-time features remain stable on newer Redis versions.
Remove Unnecessary Calls to the GCP Metadata Server
A thoughtful performance improvement in PR #55353. By default, calling Google::Auth.get_application_default
could trigger a metadata server request on GCP—a costly operation when done frequently, such as during numerous file operations. This PR switches to a more efficient approach, recommending:
Google::Apis::RequestOptions.default.authorization = Google::Auth.get_application_default(...)
This change avoids redundant calls to the metadata server, reducing latency and the risk of overwhelming the metadata endpoint.
Add load-hooks for Active Model Autoloaded Constants
PR #55347 adds load-hooks for ActiveModel::Error
and ActiveModel::SecurePassword
, and updates related configuration to utilize these hooks during class loading. This ensures that any behaviors or setups tied to those modules are applied consistently, even when they're autoloaded.
has_secure_password
Validation Fix
PR #55232 improves the behavior of the has_secure_password
validation. Previously, setting the password attribute to nil
could cause unexpected validation failure, even when a password wasn’t required. This update ensures that password validation now handles nil
values correctly, making authentication-related tests and logic more reliable.
Health Check Endpoint Now Supports JSON Responses
PR #55092 enhances the default health check endpoint (typically accessible at /up
) by allowing it to respond in JSON format in addition to plain text. This enables smoother integration with monitoring tools and services that parse structured responses.
Before, hitting /up
would return a simple 200 OK
response with no body or plain text; now, you can request JSON output for more informative and machine-readable health checks.
Add ActiveRecord::Tasks::AbstractTasks
for Adapter Customization
A useful enhancement in PR #54879: the introduction of ActiveRecord::Tasks::AbstractTasks
. This change enables developers to subclass and customize task behaviors (e.g., db:drop
, db:setup
) on a per-database-adapter basis—without patching the core tasks directly.
It was motivated by scenarios like SQLite3 running in read-only mode on Linux, where default behavior fails (check_current_protected_environment!
). With this abstract base, adapters can override these behaviors cleanly and explicitly.
nonce: false
Now Truly Omits the nonce
Attribute
Rails has refined how nonce: false
behaves in view helper tags with this enhancement, now merged in PR #54724. Previously, passing nonce: false
to javascript_tag
, javascript_include_tag
, or stylesheet_link_tag
would generate nonce="false"
in the HTML—effectively a misleading attribute. Now, setting nonce: false
completely omits the nonce
attribute, aligning behavior with developer expectations.
Key change summary:
nonce: true
→ addsnonce="..."
as expected.nonce: false
→ now does not add anynonce
attribute at all, instead ofnonce="false"
.A changelog entry was also included to document this behavior fix.github.com
Customizable Domain Extraction in ActionDispatch::Http::URL
Rails now lets you plug in your own domain extraction logic when parsing URLs, thanks to PR #50763. Previously, ActionDispatch::Http::URL
used a fixed strategy to extract the domain from request URLs. With this update, you can swap in a custom extractor class—perfect for handling specialized domain patterns, IPs, or nonstandard hosts.
# Example: Rails.config set to use your custom domain logic
Rails.application.config.action_dispatch.domain_extractor = MyCustomDomainExtractor
Read ActionText::Attachment.tag_name
in Action Text Fixtures
Utilizing the ActionText::FixtureSet.attachment method in consumer application’s test suites relies on a hard-codedaction-text-attachment
element tag name. While it's uncommon for that value to change, it is possible to configure it with the config.action_text.attachment_tag_name
configuration.
This commit replaces the hard-coded name by interpolating ActionText::Attachment.tag_name
.
Internal to the Rails test suite, Action Text’s fixtures use HTML with <p>
elements to wrap new blocks of text. Trix uses <div>
elements by default for line breaking (and for other historical reasons). This change is internal to this codebase, and will not affect users.
PR here.
Streamlining the Public API of the tag
Helper
Rails has recently cleaned up its internal API by reducing the number of publicly exposed methods in the ActionView::Helpers::TagHelper
module, specifically around the tag
helper. With this change, more lower-level methods—such as build_tag_values
and others—have been moved out of the public interface scope, making the API surface slimmer and clearer for developers.
This keeps the public API focused on what’s actually meant for use, reducing confusion and improving long-term maintainability. You can check out the full implementation and discussion in PR #49369.
Add touch
Option to update_columns
and update_column
A neat improvement in PR #51455: you can now pass touch: true
when using update_columns
or update_column
to automatically update the model's timestamp—just like the regular touch
method does.
Before this change, if you used these methods, you’d have to manually call touch
to update timestamps—especially when working with ETL flows or bypassing model callbacks. Now, it's as simple as:
user.update_columns(name: "Alice", touch: true)
Open Files from the Crash Page in Your Editor
Quality-of-life feature in PR #55295: now, when you’re looking at a crash page in development, you’ll see links that open the exact file and line in your local code editor.
Developers can configure their editor via the EDITOR
environment variable—popular ones like VSCode, Emacs, IntelliJ, MacVim, etc., are supported out of the box. The feature is implemented via TraceToFileExtractor
an editor registration logic, now neatly organized within ActiveSupport.
Fix for Invalid Query String Key Errors
Fix in PR #55319 that prevents 500 errors when the keys in a query string have invalid encoding. The patch adds a validation step in the query string parser to ensure keys are properly encoded, defusing errors caused by malformed requests — which were commonly triggered by bots.
Importantly, the change doesn’t affect performance, with benchmarks showing nearly identical speed before and after the fix.
require "benchmark/ips"
require "action_dispatch"
require "active_support/all"
require_relative "param_builder"
require_relative "param_builder_original"
QUERY_STRING = "foo=bar"
QUERY_STRING_INVALID = "foo=%81E"
def check_param_encoding
request_params = ActionDispatch::ParamBuilder.from_query_string(QUERY_STRING, encoding_template: nil)
request_params.inspect
end
def check_param_encoding_original
request_params = ActionDispatch::ParamBuilderOriginal.from_query_string(QUERY_STRING, encoding_template: nil)
request_params.inspect
end
Benchmark.ips do |x|
x.report("new") { check_param_encoding }
x.report("original") { check_param_encoding_original }
x.compare!
end
$ bundle exec ruby benchmark.rb
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin24]
Warming up --------------------------------------
new 40.767k i/100ms
original 41.113k i/100ms
Calculating -------------------------------------
new 408.540k (± 1.5%) i/s (2.45 μs/i) - 2.079M in 5.090267s
original 408.498k (± 3.2%) i/s (2.45 μs/i) - 2.056M in 5.037672s
Comparison:
new: 408540.0 i/s
original: 408498.2 i/s - same-ish: difference falls within error
Fix Migration Log Message for down
Operations
Simple yet valuable update in PR #52749: it fixes the migration log messaging for down
(rollback) operations. Before, rolling back migrations could result in misleading or ambiguous log entries, making it harder to follow what was happening. This change ensures that logs accurately reflect rollback actions, helping developers—and anyone reading the history—understand the exact migration flow.
Raise Error for Order-Dependent Finder Methods Without an ORDER Clause
A new error handling improvement via this pull request: methods like last
and find_nth
now raise a clear, descriptive error when used on models without an ORDER clause defined. This replaces silent or undefined behavior with a precise exception—likely ActiveRecord::MissingRequiredOrderError
—making it much more obvious when you're trying to perform order-dependent operations without specifying how records should be ordered.
PR here.
Return a 500 on MissingController Errors
Rails now treats a missing controller as a programming error rather than a typical routing error. When a controller doesn’t exist, Rails will respond with a 500 Internal Server Error instead of a 404. This ensures that such issues — typically bugs, typos, or routing misconfigurations — are surfaced during development and properly logged, rather than silently hidden behind a “not found” response.
Optimize Active Record Batching with in_batches(use_ranges: true)
Rails has streamlined its batch-loading performance with a clever update in PR #51243. Now, when using in_batches(use_ranges: true)
, Rails avoids transferring every ID across the network—choosing instead to use OFFSET
to efficiently fetch just the last ID needed for each batch.
Before this change, Rails ran a query like:
SELECT id FROM users WHERE id > 10000 ORDER BY id ASC LIMIT 10000;
Even though only the last ID was used, the rest were still retrieved and discarded.
Now it runs:
SELECT id FROM users WHERE id > ... ORDER BY id ASC LIMIT 1 OFFSET 9999;
This grabs just the last ID in each batch, reducing network traffic and resource usage significantly — benchmarks show ~2.4× faster performance and 900× less bandwidth usage. For tables with billions of records, these improvements are game-changing.
SafeBuffer Optimization for Action Text
PR #55352: SafeBuffer objects in Action Text are now optimized for better performance.
The normalize_action_path
Nodoc Fix
ActionController::RequestForgeryProtection
. Thanks to PR #55317, the method normalize_action_path
, which was previously appearing in API docs despite being internal-only, has now been marked with nodoc
. This means it will no longer be visible in the public documentation—keeping things cleaner and more focused for developers.
And that is it for this month's edition of Ruby on Rails Monthly.
As we wrap up this month’s updates, it’s clear that Rails continues to refine not only its features but also its developer experience, from subtle documentation cleanups to impactful improvements under the hood. Each of these changes, big or small, helps shape a more stable, reliable, and enjoyable framework for all of us. I’ll be keeping an eye on the next wave of enhancements and will share the highlights with you in the upcoming edition. Until then, happy coding and keep building great things with Rails!