← Back to list

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…

Sho Ito · 2024-01-02 12:27 · 13 claps · 3.5 min read
#ruby-on-rails #web-development #ruby
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🎮 · Gaming

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

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


메타데이터
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