はじめに
Ruby on Rails(以下Rails)の最新バージョンである7.0(以下Rails 7)が2021年12月15日にリリースされました。
Rails 7では、JavaScriptフロントエンドの大幅な刷新、CSS BundlingによるTailwind CSSやPostCSSのサポートを含めた、メジャーバージョンアップグレードにふさわしい機能強化が行われています。
本記事では、Railsの公式ブログやRailsガイド、GitHubのRailsプロジェクトのIssuesやPull Requestsの内容をもとに、Rails 7の主要な新機能・機能追加・変更点の紹介を行います。
※ 以前のバージョンのRailsの主要な新機能・機能追加・変更点については以下を参照してください。
注意点
Rubyのバージョン
Rails 7を動かすには、Ruby 2.7.0以上が必要になります。可能であれば、Ruby 3.0系列にアップグレードを行ってください。Ruby 3.1については、Rails 7.0.0では非対応ですが、今後サポートされる予定です。
セキュリティアップデート
Rails 7のリリース後は、Railsのバージョン7.0系列と、6.1系列がセキュリティアップデートの対象となります。それより前の6.0系列、5.2系列についても深刻なセキュリティ問題に対する修正は行われますが、サポート期間が短くなっていますので、注意してください。
参考
新機能
JavaScriptフロントエンドの刷新(ESM importmap / Hotwire / Stimulus / Turbo)
ES6(ECMAScript 6)/ ESM(ECMAScript modules) 、HTTP/2、import mapsを使用することで、Node.jsをインストールせずにモダンなJavaScriptをRailsアプリケーションで利用することが可能になりました。
以前のRailsのバージョンでは、Webpacker経由でWebpackを使用してJavaScriptファイルを依存するライブラリと共に結合・処理してその結果のファイルを使用していました。
最近では、ES6 / ESMのブラウザサポートによってJavaScriptのコンパイル・結合が不要になり、HTTP/2で配信することで小さな複数のファイルのままでもネットワーク経由での読み込み速度が落ちなくなりました。また、import mapsを使用することでライブラリの名前解決を行なって直接CDNなどから取得できるようになりました。
Rails 7では上記の技術的進歩を踏まえた上で、標準ではWebpacker / Node.jsを使用しなくても良いようになっています。(以前のように使用することも可能です。)
デフォルトでは、Hotwire、Stimulus、Turboといったライブラリがインストールされ、リッチなUI/UXを実現するSPA(シングルページアプリケーション)を実装する際に利用することができます。(Webpackerはデフォルトではインストールされなくなりました。)
CSS BundlingによるTailwind CSSやPostCSSなどのサポート
cssbundling-railsというGemをインストールすることで、 Tailwind CSS、 Bootstrap、 Bulma、PostCSS、 Dart Sass を使用して、CSSファイルを結合・処理し、Railsのアセットパイプラインに載せることが可能になりました。
使用するCSSライブラリは、アプリケーションの新規作成時に以下のようにして、--css
オプションで指定することができます。例えば、Tailwind CSSを指定するには以下のようにします。
rails new myapp --css tailwind
また、既存のアプリケーションでは、Gemfileに以下の行を追加して、
gem 'cssbundling-rails'
以下のコマンドを実行することで、インストールと初期設定を行うことが可能です。
$ bin/bundle install
$ bin/rails css:install:tailwind
これにより、tailwind.config.js
などの設定ファイルが生成されます。
開発時にはyarn build:css --watch
コマンドを実行することで、リアルタイムでCSSファイルの処理を行いながらブラウザで結果を確認することができます。デプロイ時には、assets:precompile
実行時にCSSファイルの処理が行われます。
参考
Active Recordの属性の暗号化
アプリケーション内でActive Recordの属性を暗号化した状態でDBに保存したり、読み出しを行うことができるようになりました。これによって、意図せずにユーザーの機密情報をアプリケーションのログや外部に公開してしまわないように、属性レベルできめ細かい制御が行うことができます。
設定を行うにはbin/rails db:encryption:init
のコマンドを実行して、ランダムなキーを生成し、その結果をbin/rails credentials:edit
で開いたエディタの画面に貼り付けて保存します。
$ bin/rails db:encryption:init
Add this entry to the credentials of the target environment:
active_record_encryption:
primary_key: 5jQ2IWs63zd7Memm3dQ4O36Sefo32WTF
deterministic_key: H7THwnF0ZU1LSYBZsENx7gRywujlb01j
key_derivation_salt: 2Rbbe0FOLtbQWvvUanvETAIcwRFHgIs9
暗号化された属性を定義するには、以下のようにモデルで、encrypts
の後に属性名を記述します。
class User < ApplicationRecord
encrypts :name
end
暗号化を指定した属性は、他の属性と同様に保存と読み出しが可能ですが、DB内には暗号化された状態で保存されます。
> user = User.new(name: 'John')
> user.save
INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "{\"p\":\"Fz3+bYdP\",\"h\":{\"iv\":\"2kWLG6TXjNmBy1Is\",\"at\":\"eUn3MOTa4qDR93ABt3rGSA==\"}}"], ["created_at", "2021-12-25 02:55:14.599457"], ["updated_at", "2021-12-25 02:55:14.599457"]]
> user = User.find_by(name: 'John')
SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "{\"p\":\"NzM54tpR\",\"h\":{\"iv\":\"947dl7Uq1O8EuXhE\",\"at\":\"FKlBF3hEO6QKJF3KSZMl4A==\"}}"], ["LIMIT", 1]]
参考
SQLクエリにコメントを自動で付与できるように
SQLのクエリに自動的にアプリケーション側の情報(Active Recordのメソッドが呼び出された、コントローラやジョブなどを含む)をコメントとして付与する機能が追加されました。
元々、MarginaliaというBasecampで使用されていたGemの機能が、ActiveRecord::QueryLogsに移植された形です。
以下のようにRailsの設定で出力する情報をタグとして設定できます。
config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
これによって例えば以下のコントローラを使用していて、GET /booksに
アクセスした際に
class BooksController < ApplicationController
def index
@books = Book.all
end
end
以下のようなSQLのログがコメントとともに出力されるようになります。
SELECT * FROM books /*application:MyApp;controller:books;action:index*/
この機能を使うことで、例えばスロークエリが発生した際に、SQLのログを見ただけでアプリケーション側のどの部分の処理でそのクエリが発生したかを特定することができるようになります。
出力できるタグの詳細や他の設定に関しては、以下を参考にしてください。
参考
- ActiveRecord::QueryLogs
- Add Marginalia to Rails, via QueryLogs by keeran · Pull Request #42240 · rails/rails · GitHub
非同期のSQLクエリによる結果の取得
ActiveRecord::Relation#load_async
を使用することによって、非同期でSQLクエリを実行し、結果を取得することができるようになりました。
これによって、例えば以下のコードのようにコントローラの同じアクション内で、複数の相互に依存しないクエリを並列に実行することができるようになり、レスポンスを返す時間を短縮することができるようになります。
def index
@articles = Article.per(params[:per]).page(params[:page]).load_async
@categories = Category.active.load_async
end
参考
機能追加
バルクインサートで自動的にタイムスタンプを記録できるように
Active Recordのinsert_all
やupsert_all
メソッドなどでバルクインサート・バルクアップサートでレコードを作成、更新する際に、created_at
やupdated_at
などのタイムスタンプを自動でセットできるようになりました。
この機能を使用するには、以下の例のようにメソッドのrecord_timestamps
オプションにtrue
を渡す必要があります。
Article.insert_all([{ title: "What's new in Rails 7" }], record_timestamps: true)
参考
Active Recordにin_order_of
メソッドが追加
レコードを指定した順番で取得できるin_order_of
メソッドがActive Recordに追加されました。
以下の例では、ユーザーをIDが1、5、3の順番になるように取得しています。
User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)
参考
- Add
ActiveRecord::QueryMethods#in_order_of
. by kddnewton · Pull Request #42061 · rails/rails · GitHub - ActiveRecord::QueryMethods#in_order_of
Active Recordにstructurally_compatible?
メソッドが追加
Active Recordのリレーションが他のリレーションとデータの構造が同じかどうかチェックするstructurally_compatible?
メソッドが追加されました。このメソッドを使用することで、and
メソッドやor
メソッドを使用する前にエラーが起きないかどうかを事前にチェックすることができます。
Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
# => true
Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
# => false
参考
- Add
ActiveRecord::Relation#structurally_compatible?
. by kddnewton · Pull Request #41841 · rails/rails · GitHub - ActiveRecord::QueryMethods
ActiveModel::APIの追加
ActiveModel::Model
の実装を新しくできたActiveModel::API
に移し、Active Recordのモデルのように振る舞うもの以外でも使いやすいようにしました。内部的な変更のため、ActiveModel::Model
はこれまで通り使用することができます。
参考
weekday_options_for_select
ヘルパメソッドの追加
曜日選択のoptionタグを生成するweekday_options_for_select
タグが追加されました。
weekday_options_for_select
# => "<option value=\"Sunday\">Sunday</option>\n<option value=\"Monday\">Monday</option>\n
# <option value=\"Tuesday\">Tuesday</option>\n<option value=\"Wednesday\">Wednesday</option>\n
# <option value=\"Thursday\">Thursday</option>\n<option value=\"Friday\">Friday</option>\n
# <option value=\"Saturday\">Saturday</option>"
weekday_options_for_select(nil, index_as_value: true)
# => "<option value=\"0\">Sunday</option>\n<option value=\"1\">Monday</option>\n
# <option value=\"2\">Tuesday</option>\n<option value=\"3\">Wednesday</option>\n
# <option value=\"4\">Thursday</option>\n<option value=\"5\">Friday</option>\n
# <option value=\"6\">Saturday</option>"
国際化にも対応しており、rails-i18nのja.ymlファイルを使用すれば日本語の曜日選択のoptionタグを生成できるようです。
参考
データベースの設定オプションでタスクをオフにできるように
データベース設定ファイルのオプションでdatabase_tasks: false
を使用することで、そのデータベースに対するタスクをオフにできるようになりました。
複数データベースを使用していて、例えば読み取り専用のDBに対してマイグレーションを実行したくない場合などで便利に使えると思います。
例:
production:
primary:
database: my_database
adapter: mysql2
animals:
database: my_animals_database
adapter: mysql2
database_tasks: false
参考
変更点
button_to
がHTTPメソッドとしてPATCHを使用するように
以前のバージョンでは、button_to
タグはHTTPメソッドとして、POSTを使用するようになっていましたが、Rails 7からはPATCHを使用するようになりました。以前の挙動に合わせるには、button_to
メソッドのオプションとして、method: :post
を渡す必要があります。
参考
SassとCSSジェネレータの非標準化
最近ではCSSフレームワークを使うようになっていて、Sassは使用されなくなってきているのでsass-rails
Gemが標準ではGemfileに含まれないようになりました。
自動生成されるCSSファイルも拡張子が.scssではなく.cssのファイルになっています。また、モデルごとにCSSファイルを生成しないようになりました。
参考
Springの非標準化
spring
Gemが標準ではGemfileに含まれないようになりました。
マシンが高速な場合はSpringによる高速化の恩恵を受けることが少なくなったためとのことです。
参考
Zeitwerkモードでの起動が必須に
Rails 6.0と6.1では、ファイルの自動ロードのモードとして、zeitwerk
モードとclassic
モードを選択することができましたが、Rails 7ではzeitwerk
モードのみ選択できるようにして、classic
モードを廃止しました。
これに伴い、設定のconfig.autoloader=
が削除されており、また、classic
モードで使用していたActiveSupport::Dependencies
が削除されました。
参考
Byebugの代わりにruby/debugを使用するように
以前のバージョンでは、デバッグ用のGemとしてbyebug
を使用していましたが、Rails 7ではRuby標準のdebug
が標準でGemfileに含まれるようになりました。
参考
ActiveSupport::DigestがSHA-256を使用するように
ダイジェスト値を生成する際にこれまでアルゴリズムとしてSHA-1を使用していたのが、SHA-256に変更になりました。キャッシュのキーなど様々な箇所で影響があるので、以前の挙動のままにしたい場合は、以下のように設定を行います。
config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA1
参考
Active Storageの画像処理でlibvipsを使用するように
Active Storageで画像処理に使われるGemで、以前のバージョンではmini_magick
が使用されていましたが、Rails 7からはvips
が標準になりました。
参考