Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rails 7の新機能徹底解説:Node.js不要化、Hotwire標準搭載、DB暗号化など

Rails 7のリリースが間近に迫っています。正式なリリース日はまだ確定していませんが、クリスマス前には公開されると予想されており、もうすぐお目見えしそうです。

本記事の執筆時点での最新バージョンは7.0.0.rc1(最初のリリース候補版)です。Basecamp、HEY、GitHub、Shopifyといった大手サービスがすでにRails 7のアルファ版を本番環境で稼働させているため、リリース候補版もかなり安定していることが期待できます。

この記事では、Rails 7がもたらす主な新機能と変更点を紹介します。

Node.jsとWebpackが不要に

そう、聞き間違いではありません! Rails 7ではJavaScriptの扱いにNodeJSやWebpackが不要になります。しかもnpmパッケージは引き続き利用可能です。

従来、BabelによるES6のトランスパイルやWebpackでのバンドルには多くのセットアップが必要でした。Webpacker gemのおかげでRailsはこれをかなりうまくサポートしていましたが、その分多くの複雑さが伴い、理解や変更が難しく、特にアップグレード性を維持しながらの運用は大変でした。

現在では、rails newで作成する新しいアプリのデフォルトは、importmaps-rails gemを通じたimport mapsの使用となりました。package.jsonを書いてnpmyarnで依存関係をインストールする代わりに、./bin/importmap CLIを使って依存関係をpin(固定)・unpin(解除)・update(更新)します。

例えば、date-fnsをインストールするには:

$ ./bin/importmap pin date-fns

これによりconfig/importmap.rbに次のような行が追加されます:

pin "date-fns", to: "https://ga.jspm.io/npm:date-fns@2.27.0/esm/index.js"

JavaScriptコード内では、従来通りすべてをそのまま使えます:

import { formatDistance, subDays } from "date-fns";

formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true });
//=> "3 days ago"

この仕組みで注意すべき点は、書いたコードとブラウザが受け取るコードの間にトランスパイルが行われないことです。とはいえ、主要なモダンブラウザはES6をネイティブサポートしているため、ほとんどの場合問題ありません。

ただし、TypeScriptやJSXは使用前にJSへのトランスパイルが必要なため、このままでは使えません。

つまり、JSXを使ってReactを利用したい場合は、別のセットアップ(webpack/rollup/esbuildなど)に切り替える必要があります。

Rails 7ならそれも簡単。選択した戦略を指定して、コマンド1つ実行するだけです:

$ ./bin/rails javascript:install:[esbuild|rollup|webpack]

TurbolinksとUJSはTurboとStimulusへ置き換え

Rails 7で生成されるアプリケーションは、TurbolinksとUJSの代わりに、HotwireのTurboとStimulusがデフォルトで組み込まれます。Hotwireは、HTMLをそのまま送信することでDOMへの高速な更新を実現する新しいアプローチです。

データベース層での暗号化

Rails 7では、ActiveRecord::Baseencryptsメソッドを使って、特定のデータベースフィールドを暗号化対象としてマークできます。初期設定を行えば、次のようなコードが書けます:

class Message < ApplicationRecord
encrypts :text
end

暗号化された属性は、他の属性と同じように扱えます。Rails 7がデータベースとアプリケーションの間で自動的に暗号化・復号化を行ってくれるのです。

ただし少し癖があります。そのフィールドでデータベースを検索したい場合は、encryptsメソッドにdeterministic: trueオプションを渡す必要があります。deterministicモードはデフォルトの非決定的モードよりセキュリティが低いため、どうしても検索が必要な属性にのみ使うようにしましょう。

非同期クエリ

データ取得時にバックグラウンドで結果をフェッチできるload_asyncメソッドが追加されました。これは、コントローラアクションから互いに関連のない複数のクエリを読み込む必要がある場合に特に有効です。次のように書けます:

def PostsController
def index
@posts = Post.load_async
@categories = Category.load_async
end
end

こうすると、両方のクエリが同時にバックグラウンドで実行されます。各クエリに200msかかる場合、逐次実行では400msかかるところ、合計のデータ取得時間は約200msで済みます。

Rails 7におけるZeitwerkモード

これは、依然としてクラシックローダーを使用している古いアプリケーションにとって破壊的変更となります。Rails 7のすべてのアプリケーションはZeitwerkモードを使用する必要がありますが、切り替え自体は比較的簡単です。詳細はZeitwerk移行ガイドを参照してください。

その他のRails 7の更新点

ジョブの無制限リトライ

ActiveJobでは、retry_onattemptsパラメータに:unlimitedを渡せるようになりました。Railsは最大試行回数なしでジョブの再試行を続けます。

class MyJob < ActiveJob::Base
retry_on(AlwaysRetryException, attempts: :unlimited)

def perform
raise "KABOOM"
end
end

名前付きバリアント

ActiveStorageのバリアントに名前を付けられるようになり、アクセスのたびにサイズを指定する必要がなくなりました。

class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize: "100x100"
end
end

# avatar.variant(:thumb) を呼び出してサムネイルバリアントを取得:
<%= image_tag user.avatar.variant(:thumb) %>

ハッシュからHTML属性へ

ビューで使える新しいtag.attributesメソッドは、ハッシュをHTML属性に変換します:

<input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>

出力結果:

<input type="text" aria-label="Search" />

Ruby debug gem

デバッグのデフォルトがbyebugからdebug gemに変更されました。

デバッグセッションに入るには、コード内でbyebugの代わりにdebuggerを呼び出します。

soleで1件だけのレコードをアサート

レコードを検索する際、クエリが必ず1件のレコードのみにマッチすべきだと表明したい場合は、firstfind_byの代わりにsolefind_sole_byを呼び出せます。

Product.where(["price = ?", price]).sole
# => ActiveRecord::RecordNotFound (該当価格の商品がない場合)
# => #<Product ...> (該当価格の商品が1件の場合)
# => ActiveRecord::SoleRecordExceeded (該当価格の商品が複数ある場合)

user.api_keys.find_sole_by(key: key)
# 上記と同様

アソシエーションの存在チェック

where.associated(:association)を使えば、JOINしてidの存在を確認する代わりに、レコード上のアソシエーションの有無を直接チェックできます。

# Before:
account.users.joins(:contact).where.not(contact_id: nil)

# After:
account.users.where.associated(:contact)

コントローラアクションからのファイルストリーミング

コントローラアクション内でsend_streamを使うと、その場で生成中のファイルをリアルタイムにストリーミング開始できます。

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

これによりユーザーは即座に(部分的な)レスポンスを受け取れるので、処理が進んでいることが分かり、Herokuにデプロイしている場合にはさらなるメリットもあります。

ファイルが即座にストリーミングを開始するため、Herokuは接続を切断しません。つまり、30秒以上かかる使い捨てファイルの生成に、わざわざバックグラウンドジョブを使う必要がなくなります。

Rails 7へのアップグレード

これまでのRailsと同様、アップグレードはシンプルです。現時点で公式のアップグレードガイドはありませんが、手順は以下の通り変わりません:

  1. GemfileのRailsバージョン番号を変更し(執筆時点では7.0.0.rc1)、bundle updateを実行する。
  2. bundle exec rails app:updateを実行する。対話型CLIに従い、必要に応じてファイルを追加・置換・修正する。
  3. テストを実行し、すべてが期待どおりに動作することを確認する。

まとめ

バグ修正、機能、変更点の完全なリストはRails 7のリリースノートで確認できます。現時点では網羅的ではありませんが、近いうちに更新されるでしょう。

まだRails 6以前を利用している場合は、Rails 7の正式リリース後、Rails 6.1は「セキュリティ修正のみ」モードに入り、バグフィックスを受けなくなることにご注意ください。また、Rails 5.2はこれをもってEOL(サポート終了)となり、一切の修正を受けなくなります。

それでは、楽しいコーディングを!

P.S. Ruby Magicの記事を公開と同時にお読みになりたい方は、Ruby Magicニュースレターを購読すれば、記事を見逃すことはありません!

  1. iOS 13.3の新機能を徹底解説!注目の15の変更点とアップデート方法

    2019年9月にiOS 13が登場して以来、新機能の追加やバグ修正、各種改善を目的としたアップデートが続々とリリースされてきました。そして2019年12月10日、iOS 13.3が正式リリース。多数の新機能に加え、バグ修正やパフォーマンス改善も盛り込まれています。対応するiOSデバイスをお使いの方は、ぜひ最新アップデートをダウンロードしましょう。iPhoneを最新のiOSバージョンに更新するには、「設定」アプリ→「一般」→「ソフトウェア・アップデート」の順に進みます。自動的にアップデートが確認され、最新バージョン「iOS 13.3」がダウンロード可能な状態で表示されます。それでは、iOS 13

  2. Android 12開発者向けプレビュー1の新機能を徹底解説

    セキュリティへの懸念が年々高まる中、各OSは継続的なアップデートによってその進化を続けています。今年のAndroid 12開発者向けプレビューには、これまで以上の期待が寄せられています。すでにリリースされたAndroid Developer Preview 1には、お気に入りのOSに追加されるであろう魅力的な機能が数多く含まれているようです。 Googleによると、世界中のAndroidユーザーにとってよりスマートで使いやすいOSを目指して開発が進められているとのこと。最新バージョンは、ユーザーの利便性とパフォーマンスを高める大きな可能性を秘めています。 Android 12開発者向けプレビュ