Active Support
-
Add
ActiveSupport::Cache::Store#namespace=
and#namespace
.Can be used as an alternative to
Store#clear
in some situations such as parallel
testing.Nick Schwaderer
-
Create
parallel_worker_id
helper for running parallel tests. This allows users to
know which worker they are currently running in.Nick Schwaderer
-
Make the cache of
ActiveSupport::Cache::Strategy::LocalCache::Middleware
updatable.If the cache client at
Rails.cache
of a booted application changes, the corresponding
mounted middleware needs to update in order for request-local caches to be setup properly.
Otherwise, redundant cache operations will erroneously hit the datastore.Gannon McGibbon
-
Add
assert_events_reported
test helper forActiveSupport::EventReporter
.This new assertion allows testing multiple events in a single block, regardless of order:
assert_events_reported([ { name: "user.created", payload: { id: 123 } }, { name: "email.sent", payload: { to: "user@example.com" } } ]) do create_user_and_send_welcome_email end
George Ma
-
Add
ActiveSupport::TimeZone#standard_name
method.zone = ActiveSupport::TimeZone['Hawaii'] # Old way ActiveSupport::TimeZone::MAPPING[zone.name] # New way zone.standard_name # => 'Pacific/Honolulu'
Bogdan Gusiev
-
Add Structured Event Reporter, accessible via
Rails.event
.The Event Reporter provides a unified interface for producing structured events in Rails
applications:Rails.event.notify("user.signup", user_id: 123, email: "user@example.com")
It supports adding tags to events:
Rails.event.tagged("graphql") do # Event includes tags: { graphql: true } Rails.event.notify("user.signup", user_id: 123, email: "user@example.com") end
As well as context:
# All events will contain context: {request_id: "abc123", shop_id: 456} Rails.event.set_context(request_id: "abc123", shop_id: 456)
Events are emitted to subscribers. Applications register subscribers to
control how events are serialized and emitted. Subscribers must implement
an#emit
method, which receives the event hash:class LogSubscriber def emit(event) payload = event[:payload].map { |key, value| "#{key}=#{value}" }.join(" ") source_location = event[:source_location] log = "[#{event[:name]}] #{payload} at #{source_location[:filepath]}:#{source_location[:lineno]}" Rails.logger.info(log) end end
Adrianna Chang
-
Make
ActiveSupport::Logger
#freeze
-friendly.Joshua Young
-
Make
ActiveSupport::Gzip.compress
deterministic based on input.ActiveSupport::Gzip.compress
used to include a timestamp in the output,
causing consecutive calls with the same input data to have different output
if called during different seconds. It now always sets the timestamp to0
so that the output is identical for any given input.Rob Brackett
-
Given an array of
Thread::Backtrace::Location
objects, the new method
ActiveSupport::BacktraceCleaner#clean_locations
returns an array with the
clean ones:clean_locations = backtrace_cleaner.clean_locations(caller_locations)
Filters and silencers receive strings as usual. However, the
path
attributes of the locations in the returned array are the original,
unfiltered ones, since locations are immutable.Xavier Noria
-
Improve
CurrentAttributes
andExecutionContext
state managment in test cases.Previously these two global state would be entirely cleared out whenever calling
into code that is wrapped by the Rails executor, typically Action Controller or
Active Job helpers:test "#index works" do CurrentUser.id = 42 get :index CurrentUser.id == nil end
Now re-entering the executor properly save and restore that state.
Jean Boussier
-
The new method
ActiveSupport::BacktraceCleaner#first_clean_location
returns the first clean location of the caller's call stack, ornil
.
Locations areThread::Backtrace::Location
objects. Useful when you want to
report the application-level location where something happened as an object.Xavier Noria
-
FileUpdateChecker and EventedFileUpdateChecker ignore changes in Gem.path now.
Ermolaev Andrey, zzak
-
The new method
ActiveSupport::BacktraceCleaner#first_clean_frame
returns
the first clean frame of the caller's backtrace, ornil
. Useful when you
want to report the application-level frame where something happened as a
string.Xavier Noria
-
Always clear
CurrentAttributes
instances.Previously
CurrentAttributes
instance would be reset at the end of requests.
Meaning its attributes would be re-initialized.This is problematic because it assume these objects don't hold any state
other than their declared attribute, which isn't always the case, and
can lead to state leak across request.Now
CurrentAttributes
instances are abandoned at the end of a request,
and a new instance is created at the start of the next request.Jean Boussier, Janko Marohnić
-
Add public API for
before_fork_hook
in parallel testing.Introduces a public API for calling the before fork hooks implemented by parallel testing.
parallelize_before_fork do # perform an action before test processes are forked end
Eileen M. Uchitelle
-
Implement ability to skip creating parallel testing databases.
With parallel testing, Rails will create a database per process. If this isn't
desirable or you would like to implement databases handling on your own, you can
now turn off this default behavior.To skip creating a database per process, you can change it via the
parallelize
method:parallelize(workers: 10, parallelize_databases: false)
or via the application configuration:
config.active_support.parallelize_databases = false
Eileen M. Uchitelle
-
Allow to configure maximum cache key sizes
When the key exceeds the configured limit (250 bytes by default), it will be truncated and
the digest of the rest of the key appended to it.Note that previously
ActiveSupport::Cache::RedisCacheStore
allowed up to 1kb cache keys before
truncation, which is now reduced to 250 bytes.config.cache_store = :redis_cache_store, { max_key_size: 64 }
fatkodima
-
Use
UNLINK
command instead ofDEL
inActiveSupport::Cache::RedisCacheStore
for non-blocking deletion.Aron Roh
-
Add
Cache#read_counter
andCache#write_counter
Rails.cache.write_counter("foo", 1) Rails.cache.read_counter("foo") # => 1 Rails.cache.increment("foo") Rails.cache.read_counter("foo") # => 2
Alex Ghiculescu
-
Introduce ActiveSupport::Testing::ErrorReporterAssertions#capture_error_reports
Captures all reported errors from within the block that match the given
error class.reports = capture_error_reports(IOError) do Rails.error.report(IOError.new("Oops")) Rails.error.report(IOError.new("Oh no")) Rails.error.report(StandardError.new) end assert_equal 2, reports.size assert_equal "Oops", reports.first.error.message assert_equal "Oh no", reports.last.error.message
Andrew Novoselac
-
Introduce ActiveSupport::ErrorReporter#add_middleware
When reporting an error, the error context middleware will be called with the reported error
and base execution context. The stack may mutate the context hash. The mutated context will
then be passed to error subscribers. Middleware receives the same parameters asErrorReporter#report
.Andrew Novoselac, Sam Schmidt
-
Change execution wrapping to report all exceptions, including
Exception
.If a more serious error like
SystemStackError
orNoMemoryError
happens,
the error reporter should be able to report these kinds of exceptions.Gannon McGibbon
-
ActiveSupport::Testing::Parallelization.before_fork_hook
allows declaration of callbacks that
are invoked immediately before forking test workers.Mike Dalessio
-
Allow the
#freeze_time
testing helper to accept a date or time argument.Time.current # => Sun, 09 Jul 2024 15:34:49 EST -05:00 freeze_time Time.current + 1.day sleep 1 Time.current # => Mon, 10 Jul 2024 15:34:49 EST -05:00
Joshua Young
-
ActiveSupport::JSON
now accepts optionsIt is now possible to pass options to
ActiveSupport::JSON
:ActiveSupport::JSON.decode('{"key": "value"}', symbolize_names: true) # => { key: "value" }
matthaigh27
-
ActiveSupport::Testing::NotificationAssertions
'sassert_notification
now matches against payload subsets by default.Previously the following assertion would fail due to excess key vals in the notification payload. Now with payload subset matching, it will pass.
assert_notification("post.submitted", title: "Cool Post") do ActiveSupport::Notifications.instrument("post.submitted", title: "Cool Post", body: "Cool Body") end
Additionally, you can now persist a matched notification for more customized assertions.
notification = assert_notification("post.submitted", title: "Cool Post") do ActiveSupport::Notifications.instrument("post.submitted", title: "Cool Post", body: Body.new("Cool Body")) end assert_instance_of(Body, notification.payload[:body])
Nicholas La Roux
-
Deprecate
String#mb_chars
andActiveSupport::Multibyte::Chars
.These APIs are a relic of the Ruby 1.8 days when Ruby strings weren't encoding
aware. There is no legitimate reasons to need these APIs today.Jean Boussier
-
Deprecate
ActiveSupport::Configurable
Sean Doyle
-
nil.to_query("key")
now returnskey
.Previously it would return
key=
, preventing round tripping withRack::Utils.parse_nested_query
.Erol Fornoles
-
Avoid wrapping redis in a
ConnectionPool
when usingActiveSupport::Cache::RedisCacheStore
if the:redis
option is already aConnectionPool
.Joshua Young
-
Alter
ERB::Util.tokenize
to return :PLAIN token with full input string when string doesn't contain ERB tags.Martin Emde
-
Fix a bug in
ERB::Util.tokenize
that causes incorrect tokenization when ERB tags are preceded by multibyte characters.Martin Emde
-
Add
ActiveSupport::Testing::NotificationAssertions
module to help with testingActiveSupport::Notifications
.Nicholas La Roux, Yishu See, Sean Doyle
-
ActiveSupport::CurrentAttributes#attributes
now will return a new hash object on each call.Previously, the same hash object was returned each time that method was called.
fatkodima
-
ActiveSupport::JSON.encode
supports CIDR notation.Previously:
ActiveSupport::JSON.encode(IPAddr.new("172.16.0.0/24")) # => "\"172.16.0.0\""
After this change:
ActiveSupport::JSON.encode(IPAddr.new("172.16.0.0/24")) # => "\"172.16.0.0/24\""
Taketo Takashima
-
Make
ActiveSupport::FileUpdateChecker
faster when checking many file-extensions.Jonathan del Strother
Active Model
-
Add
except_on:
option for validation callbacks.Ben Sheldon
-
Backport
ActiveRecord::Normalization
toActiveModel::Attributes::Normalization
class User include ActiveModel::Attributes include ActiveModel::Attributes::Normalization attribute :email, :string normalizes :email, with: -> email { email.strip.downcase } end user = User.new user.email = " CRUISE-CONTROL@EXAMPLE.COM\n" user.email # => "cruise-control@example.com"
Sean Doyle
Active Record
-
Remove deprecated
:unsigned_float
and:unsigned_decimal
column methods for MySQL.Rafael Mendonça França
-
Remove deprecated
:retries
option for the SQLite3 adapter.Rafael Mendonça França
-
Introduce new database configuration options
keepalive
,max_age
, and
min_connections
-- and renamepool
tomax_connections
to match.There are no changes to default behavior, but these allow for more specific
control over pool behavior.Matthew Draper, Chris AtLee, Rachael Wright-Munn
-
Move
LIMIT
validation from query generation to whenlimit()
is called.Hartley McGuire, Shuyang
-
Add
ActiveRecord::CheckViolation
error class for check constraint violations.Ryuta Kamizono
-
Add
ActiveRecord::ExclusionViolation
error class for exclusion constraint violations.When an exclusion constraint is violated in PostgreSQL, the error will now be raised
asActiveRecord::ExclusionViolation
instead of the genericActiveRecord::StatementInvalid
,
making it easier to handle these specific constraint violations in application code.This follows the same pattern as other constraint violation error classes like
RecordNotUnique
for unique constraint violations andInvalidForeignKey
for
foreign key constraint violations.Ryuta Kamizono
-
Attributes filtered by
filter_attributes
will now also be filtered byfilter_parameters
so sensitive information is not leaked.Jill Klang
-
Add
connection.current_transaction.isolation
API to check current transaction's isolation level.Returns the isolation level if it was explicitly set via the
isolation:
parameter
or throughActiveRecord.with_transaction_isolation_level
, otherwise returnsnil
.
Nested transactions return the parent transaction's isolation level.# Returns nil when no transaction User.connection.current_transaction.isolation # => nil # Returns explicitly set isolation level User.transaction(isolation: :serializable) do User.connection.current_transaction.isolation # => :serializable end # Returns nil when isolation not explicitly set User.transaction do User.connection.current_transaction.isolation # => nil end # Nested transactions inherit parent's isolation User.transaction(isolation: :read_committed) do User.transaction do User.connection.current_transaction.isolation # => :read_committed end end
Kir Shatrov
-
Emit a warning for pg gem < 1.6.0 when using PostgreSQL 18+
Yasuo Honda
-
Fix
#merge
with#or
or#and
and a mixture of attributes and SQL strings resulting in an incorrect query.base = Comment.joins(:post).where(user_id: 1).where("recent = 1") puts base.merge(base.where(draft: true).or(Post.where(archived: true))).to_sql
Before:
SELECT "comments".* FROM "comments" INNER JOIN "posts" ON "posts"."id" = "comments"."post_id" WHERE (recent = 1) AND ( "comments"."user_id" = 1 AND (recent = 1) AND "comments"."draft" = 1 OR "posts"."archived" = 1 )
After:
SELECT "comments".* FROM "comments" INNER JOIN "posts" ON "posts"."id" = "comments"."post_id" WHERE "comments"."user_id" = 1 AND (recent = 1) AND ( "comments"."user_id" = 1 AND (recent = 1) AND "comments"."draft" = 1 OR "posts"."archived" = 1 )
Joshua Young
-
Make schema dumper to account for
ActiveRecord.dump_schemas
when dumping in:ruby
format.fatkodima
-
Add
:touch
option toupdate_column
/update_columns
methods.# Will update :updated_at/:updated_on alongside :nice column. user.update_column(:nice, true, touch: true) # Will update :updated_at/:updated_on alongside :last_ip column user.update_columns(last_ip: request.remote_ip, touch: true)
Dmitrii Ivliev
-
Optimize Active Record batching further when using ranges.
Tested on a PostgreSQL table with 10M records and batches of 10k records, the generation
of relations for the 1000 batches was4.8x
faster (6.8s
vs.1.4s
), used900x
less bandwidth (180MB
vs.0.2MB
) and allocated45x
less memory (490MB
vs.11MB
).Maxime Réty, fatkodima
-
Include current character length in error messages for index and table name length validations.
Joshua Young
-
Add
rename_schema
method for PostgreSQL.T S Vallender
-
Implement support for deprecating associations:
has_many :posts, deprecated: true
With that, Active Record will report any usage of the
posts
association.Three reporting modes are supported (
:warn
,:raise
, and:notify
), and
backtraces can be enabled or disabled. Defaults are:warn
mode and
disabled backtraces.Please, check the docs for further details.
Xavier Noria
-
PostgreSQL adapter create DB now supports
locale_provider
andlocale
.Bengt-Ove Hollaender
-
Use ntuples to populate row_count instead of count for Postgres
Jonathan Calvert
-
Fix checking whether an unpersisted record is
include?
d in a strictly
loadedhas_and_belongs_to_many
association.Hartley McGuire
-
Add ability to change transaction isolation for all pools within a block.
This functionality is useful if your application needs to change the database
transaction isolation for a request or action.Calling
ActiveRecord.with_transaction_isolation_level(level) {}
in an around filter or
middleware will set the transaction isolation for all pools accessed within the block,
but not for the pools that aren't.This works with explicit and implicit transactions:
ActiveRecord.with_transaction_isolation_level(:read_committed) do Tag.transaction do # opens a transaction explicitly Tag.create! end end
ActiveRecord.with_transaction_isolation_level(:read_committed) do Tag.create! # opens a transaction implicitly end
Eileen M. Uchitelle
-
Raise
ActiveRecord::MissingRequiredOrderError
when order dependent finder methods (e.g.#first
,#last
) are
called withoutorder
values on the relation, and the model does not have any order columns (implicit_order_column
,
query_constraints
, orprimary_key
) to fall back on.This change will be introduced with a new framework default for Rails 8.1, and the current behavior of not raising
an error has been deprecated with the aim of removing the configuration option in Rails 8.2.config.active_record.raise_on_missing_required_finder_order_columns = true
Joshua Young
-
:class_name
is now invalid in polymorphicbelongs_to
associations.Reason is
:class_name
does not make sense in those associations because
the class name of target records is dynamic and stored in the type column.Existing polymorphic associations setting this option can just delete it.
While it did not raise, it had no effect anyway.Xavier Noria
-
Add support for multiple databases to
db:migrate:reset
.Joé Dupuis
-
Add
affected_rows
toActiveRecord::Result
.Jenny Shen
-
Enable passing retryable SqlLiterals to
#where
.Hartley McGuire
-
Set default for primary keys in
insert_all
/upsert_all
.Previously in Postgres, updating and inserting new records in one upsert wasn't possible
due to null primary key values.nil
primary key values passed intoinsert_all
/upsert_all
are now implicitly set to the default insert value specified by adapter.Jenny Shen
-
Add a load hook
active_record_database_configurations
forActiveRecord::DatabaseConfigurations
Mike Dalessio
-
Use
TRUE
andFALSE
for SQLite queries with boolean columns.Hartley McGuire
-
Bump minimum supported SQLite to 3.23.0.
Hartley McGuire
-
Allow allocated Active Records to lookup associations.
Previously, the association cache isn't setup on allocated record objects, so association
lookups will crash. Test frameworks like mocha use allocate to check for stubbable instance
methods, which can trigger an association lookup.Gannon McGibbon
-
Encryption now supports
support_unencrypted_data: true
being set per-attribute.Previously this only worked if
ActiveRecord::Encryption.config.support_unencrypted_data == true
.
Now, if the global config is turned off, you can still opt in for a specific attribute.# ActiveRecord::Encryption.config.support_unencrypted_data = true class User < ActiveRecord::Base encrypts :name, support_unencrypted_data: false # only supports encrypted data encrypts :email # supports encrypted or unencrypted data end
# ActiveRecord::Encryption.config.support_unencrypted_data = false class User < ActiveRecord::Base encrypts :name, support_unencrypted_data: true # supports encrypted or unencrypted data encrypts :email # only supports encrypted data end
Alex Ghiculescu
-
Model generator no longer needs a database connection to validate column types.
Mike Dalessio
-
Allow signed ID verifiers to be configurable via
Rails.application.message_verifiers
Prior to this change, the primary way to configure signed ID verifiers was
to setsigned_id_verifier
on each model class:Post.signed_id_verifier = ActiveSupport::MessageVerifier.new(...) Comment.signed_id_verifier = ActiveSupport::MessageVerifier.new(...)
And if the developer did not set
signed_id_verifier
, a verifier would be
instantiated with a secret derived fromsecret_key_base
and the following
options:{ digest: "SHA256", serializer: JSON, url_safe: true }
Thus it was cumbersome to rotate configuration for all verifiers.
This change defines a new Rails config:
config.active_record.use_legacy_signed_id_verifier
.
The default value is:generate_and_verify
, which preserves the previous
behavior. However, when set to:verify
, signed ID verifiers will use
configuration fromRails.application.message_verifiers
(specifically,
Rails.application.message_verifiers["active_record/signed_id"]
) to
generate and verify signed IDs, but will also verify signed IDs using the
older configuration.To avoid complication, the new behavior only applies when
signed_id_verifier_secret
is not set on a model class or any of its ancestors. Additionally,
signed_id_verifier_secret
is now deprecated. If you are currently setting
signed_id_verifier_secret
on a model class, you can setsigned_id_verifier
instead:# BEFORE Post.signed_id_verifier_secret = "my secret" # AFTER Post.signed_id_verifier = ActiveSupport::MessageVerifier.new("my secret", digest: "SHA256", serializer: JSON, url_safe: true)
To ease migration,
signed_id_verifier
has also been changed to behave as a
class_attribute
(i.e. inheritable), but only whensigned_id_verifier_secret
is not set:# BEFORE ActiveRecord::Base.signed_id_verifier = ActiveSupport::MessageVerifier.new(...) Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => false # AFTER ActiveRecord::Base.signed_id_verifier = ActiveSupport::MessageVerifier.new(...) Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => true Post.signed_id_verifier_secret = "my secret" # => deprecation warning Post.signed_id_verifier == ActiveRecord::Base.signed_id_verifier # => false
Note, however, that it is recommended to eventually migrate from
model-specific verifiers to a unified configuration managed by
Rails.application.message_verifiers
.ActiveSupport::MessageVerifier#rotate
can facilitate that transition. For example:# BEFORE # Generate and verify signed Post IDs using Post-specific configuration Post.signed_id_verifier = ActiveSupport::MessageVerifier.new("post secret", ...) # AFTER # Generate and verify signed Post IDs using the unified configuration Post.signed_id_verifier = Post.signed_id_verifier.dup # Fall back to Post-specific configuration when verifying signed IDs Post.signed_id_verifier.rotate("post secret", ...)
Ali Sepehri, Jonathan Hefner
-
Prepend
extra_flags
in postgres'structure_load
When specifying
structure_load_flags
with a postgres adapter, the flags
were appended to the default flags, instead of prepended.
This caused issues with flags not being taken into account by postgres.Alice Loeser
-
Allow bypassing primary key/constraint addition in
implicit_order_column
When specifying multiple columns in an array for
implicit_order_column
, adding
nil
as the last element will prevent appending the primary key to order
conditions. This allows more precise control of indexes used by
generated queries. It should be noted that this feature does introduce the risk
of API misbehavior if the specified columns are not fully unique.Issy Long
-
Allow setting the
schema_format
via database configuration.primary: schema_format: ruby
Useful for multi-database setups when apps require different formats per-database.
T S Vallender
-
Support disabling indexes for MySQL v8.0.0+ and MariaDB v10.6.0+
MySQL 8.0.0 added an option to disable indexes from being used by the query
optimizer by making them "invisible". This allows the index to still be maintained
and updated but no queries will be permitted to use it. This can be useful for adding
new invisible indexes or making existing indexes invisible before dropping them
to ensure queries are not negatively affected.
See https://dev.mysql.com/blog-archive/mysql-8-0-invisible-indexes/ for more details.MariaDB 10.6.0 also added support for this feature by allowing indexes to be "ignored"
in queries. See https://mariadb.com/kb/en/ignored-indexes/ for more details.Active Record now supports this option for MySQL 8.0.0+ and MariaDB 10.6.0+ for
index creation and alteration where the new index optionenabled: true/false
can be
passed to column and index methods as below:add_index :users, :email, enabled: false enable_index :users, :email add_column :users, :dob, :string, index: { enabled: false } change_table :users do |t| t.index :name, enabled: false t.index :dob t.disable_index :dob t.column :username, :string, index: { enabled: false } t.references :account, index: { enabled: false } end create_table :users do |t| t.string :name, index: { enabled: false } t.string :email t.index :email, enabled: false end
Merve Taner
-
Respect
implicit_order_column
inActiveRecord::Relation#reverse_order
.Joshua Young
-
Add column types to
ActiveRecord::Result
for SQLite3.Andrew Kane
-
Raise
ActiveRecord::ReadOnlyError
when pessimistically locking with a readonly role.Joshua Young
-
Fix using the
SQLite3Adapter
'sdbconsole
method outside of a Rails application.Hartley McGuire
-
Fix migrating multiple databases with
ActiveRecord::PendingMigration
action.Gannon McGibbon
-
Enable automatically retrying idempotent association queries on connection
errors.Hartley McGuire
-
Add
allow_retry
tosql.active_record
instrumentation.This enables identifying queries which queries are automatically retryable on connection errors.
Hartley McGuire
-
Better support UPDATE with JOIN for Postgresql and SQLite3
Previously when generating update queries with one or more JOIN clauses,
Active Record would use a sub query which would prevent to reference the joined
tables in theSET
clause, for instance:Comment.joins(:post).update_all("title = posts.title")
This is now supported as long as the relation doesn't also use a
LIMIT
,ORDER
or
GROUP BY
clause. This was supported by the MySQL adapter for a long time.Jean Boussier
-
Introduce a before-fork hook in
ActiveSupport::Testing::Parallelization
to clear existing
connections, to avoid fork-safety issues with the mysql2 adapter.Fixes #41776
Mike Dalessio, Donal McBreen
-
PoolConfig no longer keeps a reference to the connection class.
Keeping a reference to the class caused subtle issues when combined with reloading in
development. Fixes #54343.Mike Dalessio
-
Fix SQL notifications sometimes not sent when using async queries.
Post.async_count ActiveSupport::Notifications.subscribed(->(*) { "Will never reach here" }) do Post.count end
In rare circumstances and under the right race condition, Active Support notifications
would no longer be dispatched after using an asynchronous query.
This is now fixed.Edouard Chin
-
Eliminate queries loading dumped schema cache on Postgres
Improve resiliency by avoiding needing to open a database connection to load the
type map while defining attribute methods at boot when a schema cache file is
configured on PostgreSQL databases.James Coleman
-
ActiveRecord::Coder::JSON
can be instantiatedOptions can now be passed to
ActiveRecord::Coder::JSON
when instantiating the coder. This allows:serialize :config, coder: ActiveRecord::Coder::JSON.new(symbolize_names: true)
matthaigh27
-
Deprecate using
insert_all
/upsert_all
with unpersisted records in associations.Using these methods on associations containing unpersisted records will now
show a deprecation warning, as the unpersisted records will be lost after
the operation.Nick Schwaderer
-
Make column name optional for
index_exists?
.This aligns well with
remove_index
signature as well, where
index name doesn't need to be derived from the column names.Ali Ismayiliov
-
Change the payload name of
sql.active_record
notification for eager
loading from "SQL" to "#{model.name} Eager Load".zzak
-
Enable automatically retrying idempotent
#exists?
queries on connection
errors.Hartley McGuire, classidied
-
Deprecate usage of unsupported methods in conjunction with
update_all
:update_all
will now print a deprecation message if a query includes eitherWITH
,
WITH RECURSIVE
orDISTINCT
statements. Those were never supported and were ignored
when generating the SQL query.An error will be raised in a future Rails release. This behavior will be consistent
withdelete_all
which currently raises an error for unsupported statements.Edouard Chin
-
The table columns inside
schema.rb
are now sorted alphabetically.Previously they'd be sorted by creation order, which can cause merge conflicts when two
branches modify the same table concurrently.John Duff
-
Introduce versions formatter for the schema dumper.
It is now possible to override how schema dumper formats versions information inside the
structure.sql
file. Currently, the versions are simply sorted in the decreasing order.
Within large teams, this can potentially cause many merge conflicts near the top of the list.Now, the custom formatter can be provided with a custom sorting logic (e.g. by hash values
of the versions), which can greatly reduce the number of conflicts.fatkodima
-
Serialized attributes can now be marked as comparable.
A not rare issue when working with serialized attributes is that the serialized representation of an object
can change over time. Either because you are migrating from one serializer to the other (e.g. YAML to JSON or to msgpack),
or because the serializer used subtly changed its output.One example is libyaml that used to have some extra trailing whitespaces, and recently fixed that.
When this sorts of thing happen, you end up with lots of records that report being changed even though
they aren't, which in the best case leads to a lot more writes to the database and in the worst case lead to nasty bugs.The solution is to instead compare the deserialized representation of the object, however Active Record
can't assume the deserialized object has a working==
method. Hence why this new functionality is opt-in.serialize :config, type: Hash, coder: JSON, comparable: true
Jean Boussier
-
Fix MySQL default functions getting dropped when changing a column's nullability.
Bastian Bartmann
-
SQLite extensions can be configured in
config/database.yml
.The database configuration option
extensions:
allows an application to load SQLite extensions
when usingsqlite3
>= v2.4.0. The array members may be filesystem paths or the names of
modules that respond to.to_path
:development: adapter: sqlite3 extensions: - SQLean::UUID # module name responding to `.to_path` - .sqlpkg/nalgeon/crypto/crypto.so # or a filesystem path - <%= AppExtensions.location %> # or ruby code returning a path
Mike Dalessio
-
ActiveRecord::Middleware::ShardSelector
supports granular database connection switching.A new configuration option,
class_name:
, is introduced to
config.active_record.shard_selector
to allow an application to specify the abstract connection
class to be switched by the shard selection middleware. The default class is
ActiveRecord::Base
.For example, this configuration tells
ShardSelector
to switch shards using
AnimalsRecord.connected_to
:config.active_record.shard_selector = { class_name: "AnimalsRecord" }
Mike Dalessio
-
Reset relations after
insert_all
/upsert_all
.Bulk insert/upsert methods will now call
reset
if used on a relation, matching the behavior ofupdate_all
.Milo Winningham
-
Use
_N
as a parallel tests databases suffixesPeviously,
-N
was used as a suffix. This can cause problems for RDBMSes
which do not support dashes in database names.fatkodima
-
Remember when a database connection has recently been verified (for
two seconds, by default), to avoid repeated reverifications during a
single request.This should recreate a similar rate of verification as in Rails 7.1,
where connections are leased for the duration of a request, and thus
only verified once.Matthew Draper
-
Allow to reset cache counters for multiple records.
Aircraft.reset_counters([1, 2, 3], :wheels_count)
It produces much fewer queries compared to the custom implementation using looping over ids.
Previously:O(ids.size * counters.size)
queries, now:O(ids.size + counters.size)
queries.fatkodima
-
Add
affected_rows
tosql.active_record
Notification.Hartley McGuire
-
Fix
sum
when performing a grouped calculation.User.group(:friendly).sum
no longer worked. This is fixed.Edouard Chin
-
Add support for enabling or disabling transactional tests per database.
A test class can now override the default
use_transactional_tests
setting
for individual databases, which can be useful if some databases need their
current state to be accessible to an external process while tests are running.class MostlyTransactionalTest < ActiveSupport::TestCase self.use_transactional_tests = true skip_transactional_tests_for_database :shared end
Matthew Cheetham, Morgan Mareve
-
Cast
query_cache
value when using URL configuration.zzak
-
NULLS NOT DISTINCT works with UNIQUE CONSTRAINT as well as UNIQUE INDEX.
Ryuta Kamizono
-
PG::UnableToSend: no connection to the server
is now retryable as a connection-related exceptionKazuma Watanabe
Action View
-
Allow
current_page?
to match against specific HTTP method(s) with amethod:
option.Ben Sheldon
-
remove
autocomplete="off"
on hidden inputs generated byform_tag
,token_tag
,method_tag
, and the hidden
parameter fields included inbutton_to
forms will omit theautocomplete="off"
attribute.nkulway
-
Enable configuring the strategy for tracking dependencies between Action
View templates.The existing
:regex
strategy is kept as the default, but with
load_defaults 8.1
the strategy will be:ruby
(using a real Ruby parser).Hartley McGuire
-
Introduce
relative_time_in_words
helperrelative_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"
Matheus Richard
-
Make
nonce: false
remove the nonce attribute fromjavascript_tag
,javascript_include_tag
, andstylesheet_link_tag
.francktrouillez
-
Add
dom_target
helper to createdom_id
-like strings from an unlimited
number of objects.Ben Sheldon
-
Respect
html_options[:form]
whencollection_checkboxes
generates the
hidden<input>
.Riccardo Odone
-
Layouts have access to local variables passed to
render
.This fixes #31680 which was a regression in Rails 5.1.
Mike Dalessio
-
Argument errors related to strict locals in templates now raise an
ActionView::StrictLocalsError
, and all other argument errors are reraised as-is.Previously, any
ArgumentError
raised during template rendering was swallowed during strict
local error handling, so that anArgumentError
unrelated to strict locals (e.g., a helper
method invoked with incorrect arguments) would be replaced by a similarArgumentError
with an
unrelated backtrace, making it difficult to debug templates.Now, any
ArgumentError
unrelated to strict locals is reraised, preserving the original
backtrace for developers.Also note that
ActionView::StrictLocalsError
is a subclass ofArgumentError
, so any existing
code that rescuesArgumentError
will continue to work.Fixes #52227.
Mike Dalessio
-
Improve error highlighting of multi-line methods in ERB templates or
templates where the error occurs within a do-end block.Martin Emde
-
Fix a crash in ERB template error highlighting when the error occurs on a
line in the compiled template that is past the end of the source template.Martin Emde
-
Improve reliability of ERB template error highlighting.
Fix infinite loops and crashes in highlighting and
improve tolerance for alternate ERB handlers.Martin Emde
-
Allow
hidden_field
andhidden_field_tag
to accept a custom autocomplete value.brendon
-
Add a new configuration
content_security_policy_nonce_auto
for automatically adding a nonce to the tags affected by the directives specified by thecontent_security_policy_nonce_directives
configuration option.francktrouillez
Action Pack
-
Remove deprecated support to a route to multiple paths.
Rafael Mendonça França
-
Remove deprecated support for using semicolons as a query string separator.
Before:
ActionDispatch::QueryParser.each_pair("foo=bar;baz=quux").to_a # => [["foo", "bar"], ["baz", "quux"]]
After:
ActionDispatch::QueryParser.each_pair("foo=bar;baz=quux").to_a # => [["foo", "bar;baz=quux"]]
Rafael Mendonça França
-
Remove deprecated support to skipping over leading brackets in parameter names in the parameter parser.
Before:
ActionDispatch::ParamBuilder.from_query_string("[foo]=bar") # => { "foo" => "bar" } ActionDispatch::ParamBuilder.from_query_string("[foo][bar]=baz") # => { "foo" => { "bar" => "baz" } }
After:
ActionDispatch::ParamBuilder.from_query_string("[foo]=bar") # => { "[foo]" => "bar" } ActionDispatch::ParamBuilder.from_query_string("[foo][bar]=baz") # => { "[foo]" => { "bar" => "baz" } }
Rafael Mendonça França
-
Deprecate
Rails.application.config.action_dispatch.ignore_leading_brackets
.Rafael Mendonça França
-
Raise
ActionController::TooManyRequests
error fromActionController::RateLimiting
Requests that exceed the rate limit raise an
ActionController::TooManyRequests
error.
By default, Action Dispatch rescues the error and responds with a429 Too Many Requests
status.Sean Doyle
-
Add .md/.markdown as Markdown extensions and add a default
markdown:
renderer:class Page def to_markdown body end end class PagesController < ActionController::Base def show @page = Page.find(params[:id]) respond_to do |format| format.html format.md { render markdown: @page } end end end
DHH
-
Add headers to engine routes inspection command
Petrik de Heus
-
Add "Copy as text" button to error pages
Mikkel Malmberg
-
Add
scope:
option torate_limit
method.Previously, it was not possible to share a rate limit count between several controllers, since the count was by
default separate for each controller.Now, the
scope:
option solves this problem.class APIController < ActionController::API rate_limit to: 2, within: 2.seconds, scope: "api" end class API::PostsController < APIController # ... end class API::UsersController < APIController # ... end
ArthurPV, Kamil Hanus
-
Add support for
rack.response_finished
callbacks in ActionDispatch::Executor.The executor middleware now supports deferring completion callbacks to later
in the request lifecycle by utilizing Rack'srack.response_finished
mechanism,
when available. This enables applications to definerack.response_finished
callbacks
that may rely on state that would be cleaned up by the executor's completion callbacks.Adrianna Chang, Hartley McGuire
-
Produce a log when
rescue_from
is invoked.Steven Webb, Jean Boussier
-
Allow hosts redirects from
hosts
Rails configurationconfig.action_controller.allowed_redirect_hosts << "example.com"
Kevin Robatel
-
rate_limit.action_controller
notification has additional payloadadditional values: count, to, within, by, name, cache_key
Jonathan Rochkind
-
Add JSON support to the built-in health controller.
The health controller now responds to JSON requests with a structured response
containing status and timestamp information. This makes it easier for monitoring
tools and load balancers to consume health check data programmatically.# /up.json { "status": "up", "timestamp": "2025-09-19T12:00:00Z" }
Francesco Loreti, Juan Vásquez
-
Allow to open source file with a crash from the browser.
Igor Kasyanchuk
-
Always check query string keys for valid encoding just like values are checked.
Casper Smits
-
Always return empty body for HEAD requests in
PublicExceptions
and
DebugExceptions
.This is required by
Rack::Lint
(per RFC9110).Hartley McGuire
-
Add comprehensive support for HTTP Cache-Control request directives according to RFC 9111.
Provides a
request.cache_control_directives
object that gives access to request cache directives:# Boolean directives request.cache_control_directives.only_if_cached? # => true/false request.cache_control_directives.no_cache? # => true/false request.cache_control_directives.no_store? # => true/false request.cache_control_directives.no_transform? # => true/false # Value directives request.cache_control_directives.max_age # => integer or nil request.cache_control_directives.max_stale # => integer or nil (or true for valueless max-stale) request.cache_control_directives.min_fresh # => integer or nil request.cache_control_directives.stale_if_error # => integer or nil # Special helpers for max-stale request.cache_control_directives.max_stale? # => true if max-stale present (with or without value) request.cache_control_directives.max_stale_unlimited? # => true only for valueless max-stale
Example usage:
def show if request.cache_control_directives.only_if_cached? @article = Article.find_cached(params[:id]) return head(:gateway_timeout) if @article.nil? else @article = Article.find(params[:id]) end render :show end
egg528
-
Add assert_in_body/assert_not_in_body as the simplest way to check if a piece of text is in the response body.
DHH
-
Include cookie name when calculating maximum allowed size.
Hartley McGuire
-
Implement
must-understand
directive according to RFC 9111.The
must-understand
directive indicates that a cache must understand the semantics of the response status code, or discard the response. This directive is enforced to be used only withno-store
to ensure proper cache behavior.class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) if @article.special_format? must_understand render status: 203 # Non-Authoritative Information else fresh_when @article end end end
heka1024
-
The JSON renderer doesn't escape HTML entities or Unicode line separators anymore.
Using
render json:
will no longer escape<
,>
,&
,U+2028
andU+2029
characters that can cause errors
when the resulting JSON is embedded in JavaScript, or vulnerabilities when the resulting JSON is embedded in HTML.Since the renderer is used to return a JSON document as
application/json
, it's typically not necessary to escape
those characters, and it improves performance.Escaping will still occur when the
:callback
option is set, since the JSON is used as JavaScript code in this
situation (JSONP).You can use the
:escape
option or setconfig.action_controller.escape_json_responses
totrue
to restore the
escaping behavior.class PostsController < ApplicationController def index render json: Post.last(30), escape: true end end
Étienne Barrié, Jean Boussier
-
Load lazy route sets before inserting test routes
Without loading lazy route sets early, we miss
after_routes_loaded
callbacks, or risk
invoking them with the test routes instead of the real ones if another load is triggered by an engine.Gannon McGibbon
-
Raise
AbstractController::DoubleRenderError
ifhead
is called after rendering.After this change, invoking
head
will lead to an error if response body is already set:class PostController < ApplicationController def index render locals: {} head :ok end end
Iaroslav Kurbatov
-
The Cookie Serializer can now serialize an Active Support SafeBuffer when using message pack.
Such code would previously produce an error if an application was using messagepack as its cookie serializer.
class PostController < ApplicationController def index flash.notice = t(:hello_html) # This would try to serialize a SafeBuffer, which was not possible. end end
Edouard Chin
-
Fix
Rails.application.reload_routes!
from clearing almost all routes.When calling
Rails.application.reload_routes!
inside a middleware of
a Rake task, it was possible under certain conditions that all routes would be cleared.
If ran inside a middleware, this would result in getting a 404 on most page you visit.
This issue was only happening in development.Edouard Chin
-
Add resource name to the
ArgumentError
that's raised when invalid:only
or:except
options are given to#resource
or#resources
This makes it easier to locate the source of the problem, especially for routes drawn by gems.
Before:
:only and :except must include only [:index, :create, :new, :show, :update, :destroy, :edit], but also included [:foo, :bar]
After:
Route `resources :products` - :only and :except must include only [:index, :create, :new, :show, :update, :destroy, :edit], but also included [:foo, :bar]
Jeremy Green
-
A route pointing to a non-existing controller now returns a 500 instead of a 404.
A controller not existing isn't a routing error that should result
in a 404, but a programming error that should result in a 500 and
be reported.Until recently, this was hard to untangle because of the support
for dynamic:controller
segment in routes, but since this is
deprecated and will be removed in Rails 8.1, we can now easily
not consider missing controllers as routing errors.Jean Boussier
-
Add
check_collisions
option toActionDispatch::Session::CacheStore
.Newly generated session ids use 128 bits of randomness, which is more than
enough to ensure collisions can't happen, but if you need to harden sessions
even more, you can enable this option to check in the session store that the id
is indeed free you can enable that option. This however incurs an extra write
on session creation.Shia
-
In ExceptionWrapper, match backtrace lines with built templates more often,
allowing improved highlighting of errors within do-end blocks in templates.
Fix for Ruby 3.4 to match new method labels in backtrace.Martin Emde
-
Allow setting content type with a symbol of the Mime type.
# Before response.content_type = "text/html" # After response.content_type = :html
Petrik de Heus
Active Job
-
Deprecate built-in
sidekiq
adapter.If you're using this adapter, upgrade to
sidekiq
7.3.3 or later to use thesidekiq
gem's adapter.fatkodima
-
Remove deprecated internal
SuckerPunch
adapter in favor of the adapter included with thesucker_punch
gem.Rafael Mendonça França
-
Remove support to set
ActiveJob::Base.enqueue_after_transaction_commit
to:never
,:always
and:default
.Rafael Mendonça França
-
Remove deprecated
Rails.application.config.active_job.enqueue_after_transaction_commit
.Rafael Mendonça França
-
ActiveJob::Serializers::ObjectSerializers#klass
method is now public.Custom Active Job serializers must have a public
#klass
method too.
The returned class will be index allowing for faster serialization.Jean Boussier
-
Allow jobs to the interrupted and resumed with Continuations
A job can use Continuations by including the
ActiveJob::Continuable
concern. Continuations split jobs into steps. When the queuing system
is shutting down jobs can be interrupted and their progress saved.class ProcessImportJob include ActiveJob::Continuable def perform(import_id) @import = Import.find(import_id) # block format step :initialize do @import.initialize end # step with cursor, the cursor is saved when the job is interrupted step :process do |step| @import.records.find_each(start: step.cursor) do |record| record.process step.advance! from: record.id end end # method format step :finalize private def finalize @import.finalize end end end
Donal McBreen
-
Defer invocation of ActiveJob enqueue callbacks until after commit when
enqueue_after_transaction_commit
is enabled.Will Roever
-
Add
report:
option toActiveJob::Base#retry_on
and#discard_on
When the
report:
option is passed, errors will be reported to the error reporter
before being retried / discarded.Andrew Novoselac
-
Accept a block for
ActiveJob::ConfiguredJob#perform_later
.This was inconsistent with a regular
ActiveJob::Base#perform_later
.fatkodima
-
Raise a more specific error during deserialization when a previously serialized job class is now unknown.
ActiveJob::UnknownJobClassError
will be raised instead of a more generic
NameError
to make it easily possible for adapters to tell if theNameError
was raised during job execution or deserialization.Earlopain
Action Mailer
-
Add
deliver_all_later
to enqueue multiple emails at once.user_emails = User.all.map { |user| Notifier.welcome(user) } ActionMailer.deliver_all_later(user_emails) # use a custom queue ActionMailer.deliver_all_later(user_emails, queue: :my_queue)
This can greatly reduce the number of round-trips to the queue datastore.
For queue adapters that do not implement theenqueue_all
method, we
fall back to enqueuing email jobs indvidually.fatkodima
Action Cable
-
Allow passing composite channels to
ActionCable::Channel#stream_for
– e.g.stream_for [ group, group.owner ]
hey-leon
-
Allow setting nil as subscription connection identifier for Redis.
Nguyen Nguyen
Active Storage
-
Remove deprecated
:azure
storage service.Rafael Mendonça França
-
Remove unnecessary calls to the GCP metadata server.
Calling Google::Auth.get_application_default triggers an explicit call to
the metadata server - given it was being called for significant number of
file operations, it can lead to considerable tail latencies and even metadata
server overloads. Instead, it's preferable (and significantly more efficient)
that applications use:Google::Apis::RequestOptions.default.authorization = Google::Auth.get_application_default(...)
In the cases applications do not set that, the GCP libraries automatically determine credentials.
This also enables using credentials other than those of the associated GCP
service account like when using impersonation.Alex Coomans
-
Direct upload progress accounts for server processing time.
Jeremy Daer
-
Delegate
ActiveStorage::Filename#to_str
to#to_s
Supports checking String equality:
filename = ActiveStorage::Filename.new("file.txt") filename == "file.txt" # => true filename in "file.txt" # => true "file.txt" == filename # => true
Sean Doyle
-
Add support for alternative MD5 implementation through
config.active_storage.checksum_implementation
.Also automatically degrade to using the slower
Digest::MD5
implementation ifOpenSSL::Digest::MD5
is found to be disabled because of OpenSSL FIPS mode.Matt Pasquini, Jean Boussier
-
A Blob will no longer autosave associated Attachment.
This fixes an issue where a record with an attachment would have
its dirty attributes reset, preventing yourafter commit
callbacks
on that record to behave as expected.Note that this change doesn't require any changes on your application
and is supposed to be internal. Active Storage Attachment will continue
to be autosaved (through a different relation).Edouard-chin
Action Mailbox
-
Add
reply_to_address
extension method onMail::Message
.Mr0grog
Action Text
-
Forward
fill_in_rich_text_area
options to Capybarafill_in_rich_textarea "Rich text editor", id: "trix_editor_1", with: "Hello world!"
Sean Doyle
-
Attachment upload progress accounts for server processing time.
Jeremy Daer
-
The Trix dependency is now satisfied by a gem,
action_text-trix
, rather than vendored
files. This allows applications to bump Trix versions independently of Rails
releases. Effectively this also upgrades Trix to>= 2.1.15
.Mike Dalessio
-
Change
ActionText::RichText#embeds
assignment frombefore_save
tobefore_validation
Sean Doyle
Railties
-
Add command
rails credentials:fetch PATH
to get the value of a credential from the credentials file.$ bin/rails credentials:fetch kamal_registry.password
Matthew Nguyen, Jean Boussier
-
Generate static BCrypt password digests in fixtures instead of dynamic ERB expressions.
Previously, fixtures with password digest attributes used
<%= BCrypt::Password.create("secret") %>
,
which regenerated the hash on each test run. Now generates a static hash with a comment
showing how to recreate it.Nate Smith, Cassia Scheffer
-
Broaden the
.gitignore
entry when adding a credentials key to ignore all key files.Greg Molnar
-
Remove unnecessary
ruby-version
input fromruby/setup-ruby
TangRufus
-
Add --reset option to bin/setup which will call db:reset as part of the setup.
DHH
-
Add RuboCop cache restoration to RuboCop job in GitHub Actions workflow templates.
Lovro Bikić
-
Skip generating mailer-related files in authentication generator if the application does
not use ActionMailerRami Massoud
-
Introduce
bin/ci
for running your tests, style checks, and security audits locally or in the cloud.The specific steps are defined by a new DSL in
config/ci.rb
.ActiveSupport::ContinuousIntegration.run do step "Setup", "bin/setup --skip-server" step "Style: Ruby", "bin/rubocop" step "Security: Gem audit", "bin/bundler-audit" step "Tests: Rails", "bin/rails test test:system" end
Optionally use gh-signoff to
set a green PR status - ready for merge.Jeremy Daer, DHH
-
Generate session controller tests when running the authentication generator.
Jerome Dalbert
-
Add bin/bundler-audit and config/bundler-audit.yml for discovering and managing known security problems with app gems.
DHH
-
Rails no longer generates a
bin/bundle
binstub when creating new applications.The
bin/bundle
binstub used to help activate the right version of bundler.
This is no longer necessary as this mechanism is now part of Rubygem itself.Edouard Chin
-
Add a
SessionTestHelper
module withsign_in_as(user)
andsign_out
test helpers when
runningrails g authentication
. Simplifies authentication in integration tests.Bijan Rahnema
-
Rate limit password resets in authentication generator
This helps mitigate abuse from attackers spamming the password reset form.
Chris Oliver
-
Update
rails new --minimal
optionExtend the
--minimal
flag to exclude recently added features:
skip_brakeman
,skip_ci
,skip_docker
,skip_kamal
,skip_rubocop
,skip_solid
andskip_thruster
.eelcoj
-
Add
application-name
metadata to application layoutThe following metatag will be added to
app/views/layouts/application.html.erb
<meta name="application-name" content="Name of Rails Application">
Steve Polito
-
Use
secret_key_base
from ENV or credentials when present locally.When ENV["SECRET_KEY_BASE"] or
Rails.application.credentials.secret_key_base
is set for test or
development, it is used for theRails.config.secret_key_base
,
instead of generating atmp/local_secret.txt
file.Petrik de Heus
-
Introduce
RAILS_MASTER_KEY
placeholder in generated ci.yml filesSteve Polito
-
Colorize the Rails console prompt even on non standard environments.
Lorenzo Zabot
-
Don't enable YJIT in development and test environments
Development and test environments tend to reload code and redefine methods (e.g. mocking),
hence YJIT isn't generally faster in these environments.Ali Ismayilov, Jean Boussier
-
Only include PermissionsPolicy::Middleware if policy is configured.
Petrik de Heus
Guides
-
In the Active Job bug report template set the queue adapter to the
test adapter so thatassert_enqueued_with
can pass.Andrew White
-
Ensure all bug report templates set
config.secret_key_base
to avoid
generation oftmp/local_secret.txt
files when running the report template.Andrew White