Rails 7.0 Through My Lens: The Five Features That Captivated Me
I recently had the opportunity to present a Lightning Talk (LT) on the Rails 7.0 release. In this article, I aim to dissect and delve into…
Rails 7.0 Through My Lens: The Five Features That Captivated Me
I recently had the opportunity to present a Lightning Talk (LT) on the Rails 7.0 release. In this article, I aim to dissect and delve into a selection of features from this release.
Rails 7.0 includes hundreds of new features and improvements. Given the time constraint of a 15-minute LT, my focus was narrowed down to five specific items that particularly caught my interest. These features showcase the robust capabilities of Rails 7.0 but also highlight the evolving landscape of web development
Replacing Byebug with ruby/debug
After seven years of dependency on Byebug, Rails 7.0 marks a shift by replacing it with ruby/debug, which is planned to ship with Ruby 3.1.
For instance, to use ruby/debug in Rails application:
class Book < ApplicationRecord
...
def title
binding.break # The new way to invoke the debugger
"#{name} (#{isbn})"
end
end
When this method is called, the debugging console appears as follows:
[1, 8] in /myapp/app/models/book.rb
1| class Book < ApplicationRecord
2| encrypts :name, :isbn
3|
4| def title
=> 5| binding.break
6| "#{name} (#{isbn})"
7| end
8| end
=>#0 Book#title at /myapp/app/models/book.rb:5
#1 <main> at (irb):2
# and 27 frames (use `bt' command for all frames)
(rdbg)
This interface allows developers to inspect and navigate through the code efficiently.
refs
Depend on ruby/debug, replacing Byebug
update_only option for #upsert_all
Rails 7.0 introduces an important enhancement to the #upsert_all method. Previously, when using #upsert_all, there was no straightforward way to specify which columns should be updated, leading to potential risks of unintentional column updates. The new update_only option addresses this by allowing precise specification of target columns for updates.
For example, consider the following scenario:
# Initial upsert, creates a record with the given attributes
Book.upsert_all([{ id: 1, name: 'Alice', isbn: '11111', published_at: Time.zone.now }], record_timestamps: true)
# Subsequent upsert, updating only the 'name' attribute
Book.upsert_all([{ id: 1, name: 'Alice 2', isbn: '22222' }], update_only: :name)
# When we retrieve the book, we see that only the 'name' has changed
book = Book.find(1)
book.name #=> 'Alice 2'
book.isbn #=> '11111' (remain unchanged)
This feature enhances data integrity by ensuring only specified columns are updated, thereby reducing risks of accidental data alteration.
refs
Automatic timestamps on #insert_all/#upsert_all
Rails 7.0 introduces an enhancement to the #insert_all and #upsert_all methods, focusing on the handling of timestamp columns. In previous versions, setting values for timestamp columns during these operations required explicitly providing the timestamps as attributes. This update simplifies the process by enabling automatic timestamp setting.
This feature operates based on the model’s record_timestamps attribute, which can be overridden by using the record_timestamps option.
For example:
# Using upsert_all with automatic timestamp setting
Book.upsert_all([{ id: 2, name: 'Bob', isbn: '22222', published_at: Time.zone.now }], record_timestamps: true)
# Retrieving the record to see the automatically set timestamps
book = Book.find(2)
book.created_at #=> Tue, 02 Jan 2024 08:51:02.275590000 UTC +00:00 (Timestamp when the record was created)
book.updated_at #=> Tue, 02 Jan 2024 08:51:02.275590000 UTC +00:00 (Timestamp when the record was created)
refs
Set timestamps on insert_all/upsert_all record creation
Introduction of a new method #load_async
Rails 7.0 introduces the #load_async method, a valuable addition for executing independent queries more efficiently. This method allows queries to be executed in the background, thereby reducing the total response time of an application.
The #load_async method is particularly useful in scenarios where multiple complex queries are independent of each other and can be run concurrently.
For instance:
def index
# Both queries are scheduled to run asynchronously
@categories = Category.some_complex_scope.load_async
@posts = Post.some_complex_scope.load_async
end
In this example, the queries for @categories and @posts are executed in parallel, which can significantly improve the loading speed of a page, especially when dealing with complex and time-consuming queries.
refs
- Allow Adapter#select_all to be performed asynchronously from a background thread pool
- Implement Relation#load_async to schedule the query on the background thread pool
Attribute encryption support
Rails 7.0 introduces attribute encryption support, a feature designed to enhance data security by encrypting sensitive data before it is saved to the database. This feature also decrypts the data when retrieving it, allowing for seamless, transparent operations within the application.
Encrypting sensitive attributes adds an essential layer of security. It can be crucial in scenarios where an attacker gains access to your database or application logs, as the sensitive data remains encrypted and unreadable.
Setting Up Encryption
First, generate a random key set and save it as Rails credentials:
$ bin/rails db:encryption:init
Add this entry to the credentials of the target environment:
active_record_encryption:
primary_key: EGY8WhulUOXixybod7ZWwMIL68R9o5kC
deterministic_key: aPA5XyALhf75NNnMzaspW7akTfZp0lPY
key_derivation_salt: xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
Encrypting Model Attributes
Then, define encryptable attributes in your model:
class Book < ApplicationRecord
encrypts :name
end
With this setup, data retrieval remains transparent:
book = Book.find(2)
book.name #=> 'Bob'
However, in the database, the data appears encrypted:
id | name | published_at | created_at | updated_at
----+----------------------------------------------------------------------------+----------------------------+---------------------------+---------------------------
2 | {"p":"t6LD","h":{"iv":"1rKxNLQ98q/p4Ufv","at":"Y1WtkybX3A72F7kpxiOSYQ=="}} | 2024-01-02 08:51:02.272408 | 2024-01-02 08:51:02.27559 | 2024-01-02 08:51:02.27559
(1 row)
Handling Non-Deterministic Encryption
book = Book.find(2)
book.name #=> 'Bob'
Book.where(name: 'Bob') #=> [] since this is not encoded deterministically
Using Deterministic Encryption for Queries
For cases where you need to query by an encrypted column, use the deterministic option:
class Book < ApplicationRecord
encrypts :name, deterministic: true
end
This allows for encrypted column filtering:
book = Book.find(3)
book.name #=> 'Bob'
Book.where(name: 'Bob') #=> [#<Book:0x00007f8e77259a60...>]
refs
References
- Ruby on Rails 7.0 Release Notes (Last access: 2024/01/02)
- Active Record Encryption (Last access: 2024/01/02)
메타데이터
- post_id
- e9de6ecb9092
- slug
- rails-7-0-through-my-lens-the-five-features-that-captivated-me-e9de6ecb9092
- url
- https://medium.com/@sean0628/rails-7-0-through-my-lens-the-five-features-that-captivated-me-e9de6ecb9092
- canonical_url
- https://medium.com/@sean0628/rails-7-0-through-my-lens-the-five-features-that-captivated-me-e9de6ecb9092
- author_url
- https://medium.com/@sean0628
- status
- ok
- fetched_at
- 2026-06-28 10:39:35