Rails 7 sắp ra mắt. Chưa có release date chính thức, nhưng dự kiến trước Giáng sinh. Phiên bản mới nhất lúc viết: 7.0.0.rc1 (release candidate đầu tiên). Basecamp, HEY, Github, Shopify đã chạy Rails 7 alpha production, nên RC cũng ổn định.
Bài này xem xét features và changes chính của Rails 7.
Không Cần Node Và Webpack
Đúng như bạn đọc! JavaScript trong Rails 7 không còn yêu cầu NodeJS hay Webpack. Vẫn dùng được npm packages.
Transpile ES6 với Babel + bundle Webpack setup phức tạp. Rails support tốt với gem Webpacker nhưng mang theo nhiều baggage, khó hiểu, khó maintain upgradability.
Mặc định cho rails new giờ dùng import maps qua gem importmaps-rails. Thay vì package.json + npm/yarn, dùng ./bin/importmap CLI để pin/unpin/update dependencies.
Ví dụ cài date-fns:
$ ./bin/importmap pin date-fns
Thêm line vào config/importmap.rb:
pin "date-fns", to: "https://ga.jspm.io/npm:date-fns@2.27.0/esm/index.js"
JS code dùng như bình thường:
import { formatDistance, subDays } from "date-fns";
formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true }); //=> "3 days ago"
Lưu ý: Không transpile giữa code viết và browser nhận. OK vì browser hiện đại support ES6. Nhưng KHÔNG dùng được TypeScript hay JSX (cần transpile).
Muốn React với JSX? Fallback setup khác (webpack/rollup/esbuild). Rails 7 support:
$ ./bin/rails javascript:install:[esbuild|rollup|webpack]
Turbolinks & UJS → Turbo & Stimulus
Rails 7 mới sinh app mặc định dùng Turbo và Stimulus (từ Hotwire) thay Turbolinks và UJS. Hotwire: approach mới gửi HTML qua wire để update DOM nhanh.
Mã Hóa Ở Database Layer
Rails 7 cho phép mark fields encrypted dùng encrypts trên ActiveRecord::Base. Sau setup, code như sau:
class Message < ApplicationRecord encrypts :text end
Dùng encrypted attributes như attributes bình thường. Rails 7 tự encrypt/decrypt giữa DB và app.
Quirk: Không query được field đó trừ khi pass deterministic: true cho encrypts. Deterministic mode kém an toàn hơn non-deterministic default, chỉ dùng cho attributes bắt buộc query.
Async Querying (Truy Vấn Bất Đồng Bộ)
Method load_async fetch results background. Quan trọng khi load nhiều queries unrelated từ controller action:
def PostsController
def index
@posts = Post.load_async
@categories = Category.load_async
end
end
Cả 2 query chạy background song song. Mỗi query 200ms → total ~200ms thay vì 400ms serial.
Zeitwerk Mode Cho Rails 7
Breaking change cho app cũ dùng classic loader. Tất cả Rails 7 apps phải dùng Zeitwerk mode. Switch dễ dàng. Xem Zeitwerk upgrade guide đầy đủ.
Các Cập Nhật Khác Của Rails 7
Retry Jobs Unlimited Times
ActiveJob cho phép :unlimited làm attempts param trên retry_on. Rails retry job không giới hạn lần.
class MyJob < ActiveJob::Base
retry_on(AlwaysRetryException, attempts: :unlimited)
def perform
raise "KABOOM"
end
end
Named Variants
Đặt tên variants trên ActiveStorage thay vì chỉ định size mỗi lần truy cập.
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize: "100x100"
end
end
# Gọi avatar.variant(:thumb): <%= image_tag user.avatar.variant(:thumb) %>
Hash → HTML Attributes
Method mới tag.attributes cho views chuyển hash thành HTML attributes:
<input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
Sinh ra:
<input type="text" aria-label="Search" />
Ruby debug
Default debugger thay byebug bằng gem debug. Gọi debugger thay vì byebug để vào debug session.
Assert Single Record Với sole
Query records, dùng sole hoặc find_sole_by (thay first/find_by) khi assert query chỉ match 1 record.
Product.where(["price = %?", price]).sole # => ActiveRecord::RecordNotFound (nếu không có) # => #<Product ...> (nếu có 1) # => ActiveRecord::SoleRecordExceeded (nếu >1) user.api_keys.find_sole_by(key: key) # tương tự
Check Presence/Absence Association
Dùng where.associated(:association) check association present thay vì join + check id existence.
# Before: account.users.joins(:contact).where.not(contact_id: nil) # After: account.users.where.associated(:contact)
Stream Generated Files Từ Controller Actions
Dùng send_stream trong controller action stream file đang generate on-the-fly:
send_stream(filename: "subscribers.csv") do |stream|
stream.write "email_address,updated_at\n"
@subscribers.find_each do |subscriber|
stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
end
end
Response tức thì (partial) → user biết đang xử lý. Lợi ích deploy Heroku: không terminate connection (30s timeout), không cần background jobs cho file one-off >30s.
Nâng Cấp Lên Rails 7
Giống các version trước, upgrade đơn giản. Chưa có official guide nhưng steps giống nhau:
- Đổi Rails version trong Gemfile (
7.0.0.rc1lúc viết) →bundle update - Chạy
bundle exec rails app:update. Follow interactive CLI, add/replace/modify files. - Chạy tests, verify mọi thứ works.
Kết Luận
Full list bug fixes, features, changes: Rails 7 release notes. Chưa comprehensive, sẽ update sớm.
Vẫn chạy Rails 6 hoặc thấp hơn? Lưu ý: Rails 7 final release → Rails 6.1 vào mode "security issues only", không còn bug fixes. Rails 5.2 EOL, không còn fixes.
Happy coding!
P.S. Đăng ký Ruby Magic newsletter để đọc sớm nhất!