What's New in Rails 7.1
- By: Shriffle

Rails 7 was a welcome release that brought a lot of significant features and changes. On the backend, Rails 7 introduced asynchronous query loading and Zeitwerk for autoloading. The frontend saw Hotwire becoming the default solution for new Rails apps.
Rails 7.1 will add to these notable features. In this post, we'll discuss some noteworthy additions that are likely to be shipped.
A New API for Async Queries in Rails
Rails 7.1 introduces a powerful feature called the "Async Queries API," which builds upon the asynchronous query loading feature introduced in Rails 7. This new API enables running a wider range of queries asynchronously, resulting in improved performance and responsiveness in your Rails applications.
In Rails 7, the `ActiveRecord::Relation#load_async` method was introduced, allowing you to schedule a query to be executed in a background thread pool. For example, you could use `Post.where(published: true).load_async` to asynchronously load published posts.
In Rails 7.1, the capability to run queries asynchronously is expanded, particularly for aggregate methods. Now, certain aggregate methods can be executed concurrently in the background. This means that if you have multiple independent queries running in a job or controller, the query results may return faster if your application is properly configured.
To make the most of this feature, there are two configuration options you should be aware of:
1. `config.active_record.async_query_executor`: This option allows you to configure the executor used for asynchronous queries. You can choose the executor based on your application's requirements and the concurrency model you want to adopt.
2. `config.active_record.global_executor_concurrency`: This option controls the maximum concurrency level for running asynchronous queries. By adjusting this setting, you can control how many queries can be executed concurrently in the background.
In addition to the existing `load_async` method, Rails 7.1 introduces new asynchronous versions of common aggregate methods such as `async_sum`, `async_pluck`, and `async_count_by_sql`. These methods can be utilized to perform calculations, extract specific values, or count records asynchronously, further enhancing the performance of your application.
By leveraging the Async Queries API in Rails 7.1, you can optimize your application's responsiveness and improve the overall user experience by efficiently running queries in the background, freeing up the main thread for other tasks.
Resetting Singular Associations
In Rails 7.1, a convenient new feature is introduced that allows you to reset the cache on singular associations. Previously, you could only clear the cache for `has_many` associations using the `reset` method. However, with Rails 7.1, this capability is extended to `has_one` associations as well.
Let's consider an example class to illustrate this feature:
class Teacher < ActiveRecord::Base has_many :students has_one :classroom end teacher = Teacher.first
In previous versions of Rails, if you wanted to clear the cache for the `students` association, you could use the `reset` method like this:
teacher.students.reset
This would invalidate the cache for the `students` association, and subsequent requests would fetch a fresh result set from the database.
With Rails 7.1, you can now use the `reset` method on `has_one` associations as well. Using the example class above, you can clear the cache for the association between `teacher` and `classroom` like this:
teacher.classroom.reset_teacher
By invoking `reset_teacher` on the `classroom` association, you can invalidate the cache and ensure that any subsequent access to the `classroom` will fetch updated data from the database. This is particularly useful when the associated data might have changed and you want to ensure you have the most up-to-date information.
This enhancement in Rails 7.1 allows for more flexible cache management, enabling you to reset the cache not only on `has_many` associations but also on `has_one` associations, providing greater control over data freshness and consistency.
Disabling Methods Generated By ActiveRecord#enum
In Rails 7.1, a new option is introduced that allows you to opt out of generating auxiliary methods when using enums in ActiveRecord models. This provides more control over which methods are generated by the enum declaration.
Let's consider a simple example to demonstrate this feature:
class Payment < ActiveRecord::Base enum :status, %i[succeeded failed], instance_methods: false end
By specifying `instance_methods: false` in the enum declaration, Rails 7.1 will not generate the auxiliary methods typically associated with enums. In the current version of Rails, when using enums, you would expect to have methods like:
payment = Payment.first payment.succeeded? payment.failed? payment.succeeded! payment.failed!
However, with the introduction of `instance_methods: false` in Rails 7.1, these methods will not be automatically generated for the `status` enum in the `Payment` model. This means that you won't have direct access to methods like `succeeded?`, `failed?`, `succeeded!`, and `failed!` on instances of the `Payment` model.
This feature can be useful in scenarios where you want more fine-grained control over the methods available for an enum. By opting out of the generated methods, you can define your own custom methods or handle the enum values in a different manner that suits your application's specific requirements.
Overall, this enhancement in Rails 7.1 provides flexibility and allows you to tailor the behavior of enums to better align with your application's needs, giving you more control over the generated methods.
Support for Common Table Expressions
In Rails 7.1, Common Table Expressions (CTEs) are natively supported, simplifying the process of writing complex queries without the need to use `Arel::Nodes`. This enhancement improves code readability and eliminates the extra complexity involved in constructing complex queries.
With the introduction of CTE support in Rails 7.1, a new `.with` method is provided to write queries that utilize CTEs. Here's an example:
Post.with( posts_with_comments: Post.where("comments_count > ?", 0), posts_with_tags: Post.where("tags_count > ?", 0) )
In the above example, we use the `.with` method to define two CTEs: `posts_with_comments` and `posts_with_tags`. Each CTE is associated with a specific condition using `Post.where`, specifying the criteria for inclusion in the CTE.
By using the `.with` method, you can conveniently define multiple CTEs within a single query, making your code more concise and readable. This approach eliminates the need to resort to `Arel::Nodes` or other complex constructs when dealing with advanced queries involving CTEs.
The introduction of native CTE support in Rails 7.1 streamlines the process of writing complex queries, enhances code maintainability, and allows developers to express their intent more clearly.
Support for Async Bulk Record Destruction
Rails 7.1 brings enhanced support for asynchronous code execution, including a new configuration called `destroy_association_async_batch_size`. This configuration allows developers to specify the maximum number of records to be destroyed in a single background job when using the `dependent: :destroy_async` association.
By default, Rails performs the destruction of all dependent records in a single background job when the parent record is destroyed. This behavior remains unchanged. However, with the introduction of `destroy_association_async_batch_size`, if the number of dependent records exceeds the specified batch size, they will be destroyed in multiple background jobs.
This new configuration provides flexibility in handling the destruction of dependent records asynchronously. Developers can adjust the batch size according to their specific needs, optimizing resource utilization and ensuring efficient execution of the deletion process.
In summary, Rails 7.1 allows setting the `destroy_association_async_batch_size` configuration to control the maximum number of records destroyed per background job when using `dependent: :destroy_async`. This empowers developers to manage the destruction of dependent records asynchronously with improved granularity and performance.
Other Rails 7.1 Updates
In Rails 7.1, there are several updates and additions that bring new functionality and improvements to the framework:
1. `ActiveRecord::Relation#explain` Accepts Options: With Rails 7.1, you can pass database-specific `EXPLAIN` options to `ActiveRecord::Relation#explain`. This allows you to customize the explanation of a query, as demonstrated by the example using `Customer.where(id: 1).joins(:orders).explain(:analyze, :verbose)`.
2. `ActiveRecord` Regroup Method: Rails 7.1 introduces the `regroup` method for Active Record queries. It enables "regrouping" queries based on specific attributes. For example, `Post.group(:title).regroup(:author)` generates SQL equivalent to `SELECT posts.* FROM posts GROUP BY posts.author`.
3. New `stub_const` Method for Testing: Rails 7.1 adds a `stub_const` method for `ActiveSupport::TestCase`. It allows you to stub a constant for the duration of a block, simplifying test setup and assertions involving constants.
4. Enhanced `has_secure_password` with `password_challenge`: Rails 7.1 improves the functionality of `has_secure_password` by adding a `password_challenge` accessor and validation. This enables easy implementation of a password challenge, similar to password confirmation.
5. Attachments Returning the Blob: In Rails 7.1, when saving attachments to a record using `attach`, the method will return the attached blob or blobs. This allows direct use of blob methods on the attachment object, facilitating operations such as downloading, generating URLs, and applying variants.
6. Storage of CSRF Tokens Outside of Sessions: Rails introduces a new configuration option to store CSRF tokens outside of sessions. By using a lambda function or custom strategy class, you can define the storage and retrieval of CSRF tokens, reducing the overhead of session creation and eviction.
7. Validity Checking for PostgreSQL Indexes: Rails 7.1 provides a way to verify the validity of PostgreSQL indexes. You can check whether an index is valid using `connection.index_exists?(:users, :email, valid: true)` or by examining the validity flag of indexes using `connection.indexes(:users).select(&:valid?)`.
8. `ActiveRecord::QueryMethods#select` Accepts a Hash: In Rails 7.1, `ActiveRecord::QueryMethods#select` now accepts a hash of options. This simplifies the selection of specific attributes from joined tables, allowing you to write more concise and readable queries.
9. Default Dockerfiles for New Rails Applications: Rails 7.1 includes Docker files as default options for new applications. These files provide a starting point for deploying Rails applications in a production environment.
10. Default Health Controller: Rails 7.1 introduces a new health endpoint (`Rails::HealthController#show`) mapped to the "/up" path in newly generated applications. This endpoint allows load balancers and uptime monitors to easily track the availability of the application.
11. New `Rails.env.local?` for Environment Checks: Rails 7.1 introduces the `local?` method to simplify environment checks. It provides a more expressive and readable way to check if the environment is development or test.
12. `ActiveRecord::Persistence#update_attribute!` Method: Rails 7.1 adds a new method, `ActiveRecord::Persistence#update_attribute!`, which behaves similarly to `update_attribute` but raises an exception (`save!`) if the record fails to save.
13. Templates Capable of Defining Accepted Locals: Rails 7.1 enhances templates by allowing required arguments with default values. Templates can define specific accepted locals through a magic comment, providing better control and customization options for template behavior.
Overall, Rails 7.1 brings significant improvements and new capabilities to streamline development, enhance performance, and improve the developer experience.
1 Comments