An Overview Of Upcoming Ruby on Rails 7.1 Features Part 1
[ comments ]
Table of contents
- Intro
- 01. Rails 7.0.1 released
- 02. Adding autoload paths to
$LOAD_PATH
disabled - 03. A new
#update_attribute!
method was added - 04. Dart Sass for Rails released
- 05. A new
#stub_const
method was added - 06. Improved the error message with a missing association
- 07.
ActiveRecord::ConnectionPool
is now Fiber-safe - 08. Rails 7.0.2 released
- 09.
#to_fs(:format)
replaced#to_s(:format)
- 10. Password challenge via
has_secure_password
added - 11. Saving attachments to a record now returns the blob
- 12.
audio_tag
&video_tag
now accept attachments - 13.
#destroy_association_async_batch_size
added - 14. Active Record added a new API for general async queries
- 15. CSRF tokens can now be stored outside of sessions
- 16. Keyword arguments for system tests screenshot helper
- 17. Encrypted attributes on columns with default values
- 18. Pattern matching for ActiveModel is now available đźš©
- 19.
db_runtime
added to Active Job instrumentation - 20. Validity check for PostgreSQL indexes added
- 21. Exclusion constraints (PostgreSQL-only) are now supported
- 22.
Messageverifier
gets aurlsafe:
initialization option - 23. Order
DESC
forin_batches
- 24.
db:prepare
can load the schema of an empty database - 25. Specify the parent class of a job with a job generator
- 26.
datetime_field
gets a newinclude_seconds
option - 27. Rails added support for Common Table Expressions
- 28. Pre-defined variants for previews allowed now
- 29.
routes --unused
option to detect dormant routes - 30. Templates can now define which locals they accept
- End of Part I
Intro
The Ruby on Rails repository has seen a tremendous amount of activity this year, introducing a wealth of new framework features, fixing numerous bugs and implementing countless performance improvements.
With so much happening in the Ruby on Rails repository, it can be challenging to keep up. However, in this series of blog posts, I will zero in on some of the exciting new features and improvements in the upcoming minor release of Rails, version 7.1.
In this series of blog posts, I will be sharing my favourite new features and improvements from the Rails repository throughout 2022. This is the first of a three-part series, so be sure to stay tuned for the next two instalments of a comprehensive overview of the exciting developments in Rails this year. You can follow me on Twitter where I’ll send updates; if you’re not on Twitter you can subscribe to my newsletter if you scroll to the bottom.
If you want to stay up to date on the latest developments in the Ruby on Rails framework, I recommend subscribing to This Week In Rails for weekly issues.
Please be aware that the code samples were taken out of the various pull requests and docs verbatim, I haven’t tested them to guarantee they work as they are. That said, all the features described have been merged.
Let’s get started.
01. Rails 7.0.1 released.
Rails kicked off the year with the release of Rails 7.0.1, which introduced support for Ruby 3.1 along with various bug fixes and documentation improvements.
Later in the year, Rails retired Webpacker, a tool that had served as a bridge to compiled and bundled JavaScript for over five years. In its place, Rails introduced import maps, Turbo and Stimulus as the default options, replacing Webpacker, Turbolinks and UJS.
I vividly remember the excitement that surrounded the news of Webpacker’s retirement. Webpacker had been a source of frustration for many developers. The introduction of ./bin/importmap
to pin and unpin NPM packages brought back some joy of working with Rails I reckon.
02. Adding autoload paths to $LOAD_PATH
disabled.
Beginning with Rails 7.1, paths managed by the autoloader will no longer be added to $LOAD_PATH
. This means that they can no longer be loaded using a manual require
call; instead, you can reference the class or module directly. To accomplish this change, the config.add_autoload_paths_to_load_path
configuration option will be set to false
in Rails 7.1 and later versions.
It is recommended to set config.add_autoload_paths_to_load_path
to false
in the config/application.rb
file when running in :zeitwerk
mode. This is because Zeitwerk internally uses absolute paths, and therefore models, controllers, jobs and other files do not need to be in the LOAD_PATH
when require_dependency
is not used.
By setting config.add_autoload_paths_to_load_path
to false
, you can prevent Ruby from checking these directories when resolving require
calls with relative paths. This can also improve the performance of your application by saving on Bootsnap work and RAM usage as Bootsnap does not need to build an index for these directories.
03. A new #update_attribute!
method was added.
Rails added a new ActiveRecord::Persistence#update_attribute!
method. This method is similar to update_attribute
, but calls save!
instead of save
.
ActiveRecord::Persistence#update_attribute!
raises ActiveRecord::ActiveRecordError
if an attribute is marked as readonly
.
04. Dart Sass for Rails released.
Rails released a new dartsass-rails gem that wraps the standalone executable version of the Dart-based Sass for use with Rails 7.
dartsass-rails
makes it easy to use Sass stylesheets with Rails and replaces the previously used Ruby Sass, which has been deprecated. With this gem, Rails developers can take advantage of the latest features and improvements in Sass while working with Rails.
In a new Rails app, you can install Dart Sass for Rails by doing:
The installer will create a default Sass input file, app/assets/stylesheets/application.scss
, where you should import all of your style files to be compiled. When you run rails dartsass:build
, this input file will be used to generate the output CSS file, app/assets/builds/application.css
which you can then include in your app.
05. A new #stub_const
method was added.
A new #stub_const
 method was added to easily change the value of a constant for the duration of a block, silencing warnings. The implementation is not thread-safe if you have parallel testing enabled though.
In the example above, by using this method instead of setting World::List::Import::LARGE_IMPORT_THRESHOLD to 5000
, we can prevent warnings from being thrown and ensure that the original value is restored after the test has finished.
Take note, however, that stubbing a constant can have unintended consequences when used in a multithreaded environment. If multiple threads depend on the same constant and each thread attempts to stub the constant, it can lead to conflicting stubs and unpredictable behaviour. To avoid this issue, it is important to carefully consider the impact of stubbing constants in concurrent threads, such as when running separate test suites in parallel.
06. Improved the error message with a missing association.
Using where.associated
with a missing association used to raise a cryptic error message, this has now been improved with a much clearer message. It’s better to show you an example with this one.
For a Post
that doesn’t have an association named cars
, we’d get something like:
Now, we’ll be getting
Much better.
07. ActiveRecord::ConnectionPool
is now Fiber-safe.
Rails made ActiveRecord::ConnectionPool
Fiber-safe. Rails has a lot of thread-centric code and does I/O with databases with threads inherently, this pull request makes it possible to switch how the connection pool is interacted with. For instance, if you use a fiber-based job processor or server like falcon
, you should set config.active_support.isolation_level
to :fiber
, in which case multiple fibers in the same thread will be used to manage connections.
08. Rails 7.0.2 released!
On February 8, 2022, Rails 7.0.2 was released with a patch that included the reversal of a problematic feature, as well as the introduction of the ability to version the database schema based on the Rails version in use. This new feature allows existing applications to continue using their database schemas generated in Rails 6.1, maintaining the same behaviour and ensuring that the production database schema remains consistent.
09. #to_fs(:format)
replaced #to_s(:format)
.
The #to_s(:format)
method was deprecated not long ago in favor of #to_formatted_s(:format)
. #to_formatted_s(:format)
’s alias was #to_fs(:format)
but this pull request swapped so that #to_formatted_s(:format)
became an alias to #to_fs(:format)
. Why? Because #to_formatted_s(:format)
is too long a name for a method that’s used so frequently according to DHH. I concur.
10. Password challenge via has_secure_password
added.
Rails now allows a password challenge to be implemented with the same ease as a password confirmation, re-using the same error handling logic in the view, as well as the controller.
This enhances has_secure_password
to define a password_challenge
accessor and the appropriate validation. When password_challenge
is set, the validation checks that it matches the currently persisted password_digest
(i.e. password_digest_was
).
For example, in the controller, instead of:
One could write:
I couldn’t describe this pull request in a better way than the author, I grabbed this verbatim from the pull request description. Thanks Jonathan.
11. Saving attachments to a record now returns the blob.
Saving attachments to a record with the #attach
 method now returns the blob or array of blobs that were attached to the record. This means we can use blob methods directly on the attachment! If the record fails to save, #attach
will return false
.
12. audio_tag
& video_tag
now accept attachments.
Rails extended audio_tag and video_tag to accept Active Storage attachments so instead of:
You can now do:
13. #destroy_association_async_batch_size
added.
Rails added a new ActiveRecord.destroy_association_async_batch_size
that allows developers to specify the maximum number of records that will be destroyed in a single background job when using the dependent: :destroy_async
association option. If there are more dependent records than the specified batch size, they will be destroyed in multiple background jobs otherwise they’ll be destroyed in a single background job. This helps to ensure that the destruction of large numbers of records can be handled efficiently without overwhelming the application.
14. Active Record added a new API for general async queries.
In Active Record now, there’s a new API for general async queries. In Rails 7.1 you’ll be able to run aggregate methods as well as all methods returning a single record or anything other than a Relation
and find_by_sql
asynchronously. So you can do stuff like:
15. CSRF tokens can now be stored outside of sessions.
This pull request allows CSRF tokens to be stored outside of sessions. When sessions are not stored in cookies, it can lead to the creation and constant eviction of millions of sessions solely for the purpose of storing a CSRF token. To address this issue, Rails has added a new configuration option that allows a lambda to be provided that can store the CSRF token in a custom location.
You can also implement custom strategy classes for CSRF token storage:
16. Keyword arguments for system tests screenshot helper.
With this pull request, we can now selectively screenshot or dump the HTML of tests with the html:
and screenshot:
keyword arguments, as opposed to collectively dumping the HTML or screenshots when system tests run.
Here are some concrete examples:
17. Encrypted attributes on columns with default values.
Previously, reading encrypted attributes defined on columns with default values would raise an error unless config.active_record.encryption.support_unencrypted_data
was enabled because the contents of these columns were not encrypted. These values will now be encrypted at the time of record creation regardless of the config settings.
18. Pattern matching for ActiveModel [=Reverted=].
Update: This PR was reverted. Thanks to zverok_kha for the heads-up. A new gem replaced this feature for now.
Probably the most loved of 2022, this pull request introduces a pattern matching interface for hash patterns in Ruby versions 2.7 and later, enabling users to perform pattern matching on any object that includes the ActiveModel::AttributeMethods
module, such as ActiveRecord::Base
.
You can do interesting stuff like:
Here’s another example:
19. db_runtime
added to Active Job instrumentation.
This pull request introduces db_runtime
to the notification payload of the perform.active_job
event. db_runtime
tracks the total time (ms) spent on database queries during job execution, allowing for a better understanding of how a job’s time is spent.
20. Validity check for PostgreSQL indexes added.
Creating indexes this way for example add_index :account, :active, algorithm: :concurrently
may result in an invalid index. You can now check if an index is valid:
21. Exclusion constraints are now supported.
Although for PostgreSQL only, this extends Active Record’s migration or schema dumping to support PostgreSQL exclusion constraints.
22. Messageverifier
gets a urlsafe:
initialization option.
The MessageVerifier
and MessageEncryptor
constructors now accept a :urlsafe
option. When enabled, this option ensures that messages use a URL-safe encoding. When you pass urlsafe: true
to the initializers, you’ll get URL-safe strings in response.
23. Order DESC
for in_batches
.
This change adds that ability to call in_batches
with the specified ordering.
It honours the ordering now when it didn’t before. Should I call this a bug fix or a feature?
24. db:prepare
can load the schema of an empty database.
Previously, if a database existed but had not been populated with tables, db:prepare
would run all migrations. Now db:prepare
will load the schema when an uninitialized database exists and dump schema after migrations.
25. Specify the parent class of a job with a job generator.
Rails added a --parent
option to the job generator to specify the parent class of a job. There’s now a superclass option in the job generator.
It’s possible now to do:
to get:
26. datetime_field
gets a new include_seconds
option.
According to input elements of type time browsers render time differently if you format time without the “seconds” bit. This PR adds an option to omit the seconds part of the formatted time with include_seconds: false
.
So something like this:
Will now generate:
27. Rails added support for Common Table Expressions.
You can now build sophisticated queries with Common Table Expressions using the .with
query method on models. The .with
allows the usage of Active Record relations without the need to manually build Arel::Nodes::As
nodes.
28. Pre-defined variants for previews allowed now.
This pull request introduces the capability to use pre-defined variants when calling the preview
or representation
methods on an attachment. This allows for greater flexibility and customization when working with attachments.
For instance, you can now specify which variant of an attachment you want to use when generating a preview
or representation
.
And then use the defined variant like so:
29. routes --unused
option to detect dormant routes.
Over time, a Rails app can become slow to boot simply because of how many routes it has. This new option for the routes
command can be used to detect routes that are drawn but aren’t valid.
30. Templates can now define which locals they accept.
This pull request adds the ability for templates to have required arguments with default values. Before this update, templates would accept any locals as keyword arguments, it’s now possible to define the specific locals that a template will accept with a magic.
This enhancement allows for greater control and customization of template behaviour and functionality.
Previously a partial could look like this:
Now, the same partial dons a simpler look:
How gorgeous.
End of Part I.
And that brings us to the end of part one in the series: This Week In Rails Wrapped: A Summary Of Features Coming To Rails 7.1. This and the other two posts I’m working on are just a scratch on the surface of what’s coming in Rails 7.1. There are countless more features I haven’t covered. It’s important to also mention that I didn’t cover bug fixes and performance improvements.
I believe I can speak for other Rails developers when I say we’re grateful to all the 485 contributors that made this possible.
Special thanks to the This Week In Rails team, namely Greg Molnar, Petrik de Heus, Wojciech Wnętrzak and yours truly for making it easier to track and bring you all of these changes. You can subscribe to This Week In Rails going forward to get weekly updates like these in your email.
Stay tuned for part two.
[ comments ]