LoginSignup
4

Ruby on Rails 7.1の主要な新機能・機能追加・変更点

Posted at

はじめに

Rubyを使用したOSSのWebアプリケーションフレームワーク、Ruby on Rails(以下Rails)の最新バージョンである7.1(以下Rails 7.1)が2023年10月5日にリリースされました

Rails 7.1はデプロイ用のDockerfileの自動生成、複合プライマリキーのサポート、Active Recordの非同期クエリの機能追加、Trilogy MySQLアダプタのサポートなど、多くの機能追加が行われました。

本記事では、Railsの公式ブログRailsガイド、GitHubのRailsプロジェクトのIssuesPull Requestsの内容をもとに、Rails 7.1の主要な機能追加・変更点の紹介を行います。

※ 以前のバージョンのRailsの主要な新機能・機能追加・変更点については以下を参照してください。

注意点

Rubyのバージョン

Rails 7.1を動かすには、Ruby 2.7.0以上が必要になります。可能であれば、現時点で最新のRuby 3.2系列にアップグレードを行ってください。

Railsのサポート対象のバージョン

Rails 7.1のリリース後は、バグ修正の対象がバージョン7.1、セキュリティアップデートの対象が7.1、7.0、6.1系列となります。それより前のバージョンに関してはサポートは行われません。

参考

新機能・機能追加

Dockerfile(デプロイ用)の自動生成

アプリケーションをrails newで新規作成した際に、本番環境デプロイ用のDockerfileをはじめとした、Docker関連のファイルを自動生成するようになりました。
--skip-dockerオプションを渡すことで、Docker関連のファイルの生成をスキップすることができます。)

以下のコマンドでDockerイメージのビルドとボリューム作成、実行を行うことができます。

$ docker build -t app .
$ docker volume create app-storage
$ docker run --rm -it -v app-storage:/rails/storage -p 3000:3000 --env RAILS_MASTER_KEY=<config/master.keyの値> app

※ 元々この機能は本番環境へのデプロイを想定していて、開発環境で使用することを意図していないことに注意してください。(開発環境ではHTTPSの設定や開発用のRubyGemsパッケージのインストールなどカスタマイズが必要です。)

参考

複合プライマリキー

複合プライマリキーがサポートされ、マイグレーションや、Active RecordのクエリメソッドなどRailsのアプリケーション全般で使用できるようになりました。

DBに複合プライマリキーを使用したテーブルを作成するには、以下のようにマイグレーションのchange_tableprimary_key:オプションにカラム名の配列を渡します。

class CreateProducts < ActiveRecord::Migration[7.1]
  def change
    create_table :products, primary_key: [:store_id, :sku] do |t|
      t.integer :store_id
      t.string :sku
      t.text :description
    end
  end
end

findwhereのActive Recordのクエリメソッドでも、複合プライマリキーに対応する引数を指定することができるようになっています。

Product.find([3, "XYZ12345"]) # store_id: 3, sku: "XYZ12345"を指定
Product.where(Product.primary_key => [[1, "ABC98765"], [7, "ZZZ11111"]]) # store_id: 3, sku: "XYZ12345"とstore_id: 7, sku: "ZZZ11111"

firstメソッドでは、2つのプライマリキーを組み合わせた順番で並び替えを行い、その最初のレコードを取得するようになっています。

Product.first # SQLで実行: SELECT * FROM products ORDER BY products.store_id ASC, products.sku ASC LIMIT 1

モデルの関連で、従来のidではなく、複合プライマリキーを使用するには、has_manybelongs_toなどのオプションでquery_constraintsを使用します。

class Author < ApplicationRecord
  self.primary_key = [:first_name, :last_name]
  has_many :books, query_constraints: [:first_name, :last_name]
end

class Book < ApplicationRecord
  belongs_to :author, query_constraints: [:author_first_name, :author_last_name]
end

他にも、フォームへルパやルーティングなどでもサポートされます。詳しい内容に関しては、以下のRails guidesの記事を参照してください。

参考

ActiveRecord::Base.normalizesによるモデルの属性の正規化

メールアドレスの小文字化、電話番号のハイフン削除などの正規化を、モデル内に以下のようにnormalizesで指定することで、属性に値がアサインされたり、更新された際に行うことができるようになりました。

class User < ApplicationRecord
  normalizes :email, with: -> email { email.downcase }
  normalizes :tel, with: -> tel { tel.delete("-") }
end

上記のモデル定義を使用した場合、以下のようにemailtelの正規化が行われます。

 user = User.new(email: 'JOHN@example.com', tel: '01-2345-6789')
=> #<User:0x0000000108142a68 id: nil, email: "john@example.com", tel: "0123456789", created_at: nil, updated_at: nil>

normalizesを指定している場合、Active Recordのwhereexists?に正規化前の値を渡すと、正規化されてSQLが実行されます。

> User.where(email: 'JOHN@example.com')
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."email" = ? /* loading for pp */ LIMIT ?  [["email", "john@example.com"], ["LIMIT", 11]]
> 9> User.exists?(email: 'JOHN@example.com')
  User Exists? (0.3ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "john@example.com"], ["LIMIT", 1]]

参考

Active Recordに非同期クエリのメソッドが追加

Active Recordに、以下のasync_で始まる各種の非同期クエリのメソッドが追加されました。

  • async_count
  • async_sum
  • async_minimum
  • async_maximum
  • async_average
  • async_pluck
  • async_pick
  • async_ids
  • async_find_by_sql
  • async_count_by_sql

これらのメソッドは呼出後に、クエリの実行状態を持つActiveRecord::Promiseのオブジェクトを返し、実行が完了している場合は、#valueメソッドで値を取得することができます。

promise = Post.where(published: true).async_count
promise.value # => 10

参考

Trilogy MySQLアダプタのサポート

GitHubによってOSS化された、MySQL互換のDBに対するアダプタであるTrilogyがサポートされるようになりました。
trilogyは従来のmysql2アダプタに比べて高速で軽量に動作し、GitHub以外にもShopifyなどで利用されています。(詳しくは参考にあげたページを参照してください。)

Railsアプリケーションではconfig/database.ymlで以下のようにadapter: trilogyを指定することで、従来のmysql2アダプタから移行することができます。

development:
  adapter: trilogy
  database: blog_development
  pool: 5

参考

ジョブの一括エンキュー(perform_all_later

複数のジョブを一括でエンキューする、perform_all_laterActiveJobのクラスメソッドとして追加されました。
このメソッドを使用することで、Sidekiqなど、Active Jobのアダプタでenqueue_allメソッドを使用できるジョブ実行システムの場合では、個別にジョブをエンキューすることなく効率よくジョブを実行できるようになっています。(Sidekiqの場合はpush_bulkで処理が行われます。)
この場合Active Jobのエンキュー用のコールバックが実行されないことに注意してください。
また、enqueue_allを使用できない場合は、ループで個別のジョブをエンキューします。

参考

ActiveSupport::MessagePack

Rubyオブジェクトをシリアライズする際に従来はJSONやMarshalのシリアライザが使用されていましたが、より高速で、データが軽量になるmsgpack gemを使用したActiveSupport::MessagePackが利用できるようにようになりました。
Railsアプリケーションの各種設定でも、以下の例のように:message_packが指定できるようになりました。

config.active_support.message_serializer = :message_pack
config.action_dispatch.cookies_serializer = :message_pack
config.cache_store = :file_store, "tmp/cache", { serializer: :message_pack }

参考

config.autoload_lib / config.autoload_lib_once

Railsアプリケーションのlib/ディレクトリ以下にあるクラスやモジュールを自動読み込みする設定が追加されました。
以下のように指定することで、lib/assetslib/taskslib/generators以外のlibディレクトリ内のファイルにある、モジュールやクラスが起動時に自動読み込みされ、起動中にファイルの変更を行った場合は(モデルやコントローラと同様に)リロードされます。

config.autoload_lib(ignore: %w[assets tasks generators])

autoload_libの代わりにconfig.autoload_lib_onceを使用した場合は、起動時に1度だけ読み込まれ、変更後はリロードされません。

参考

JSONとHTMLのresponse.parsed_bodyでパターンマッチングが利用できるように

ActionDispatch::IntegrationTestを使用した際に、response.parsed_bodyに対して以下のようにパターンマッチングを使用したテストを書くことができるようになりました。

以下はコンテンツがJSONの場合で、Rails 6からresponse.parsed_bodyActiveSupport::HashWithIndifferentAccessまたはArrayを返すようになっており、それに対してパターンマッチングを行なっています。
(以下の例ではMinitestに追加されたassert_patternでテストを行なっています。)

get "/posts/42.json" # JSONで{ "title": "Title" }を返す
assert_pattern { response.parsed_body => [{ title: /title/i }] } # マッチする

また、コンテンツがHTMLの場合は、Rails 7.1でresponse.parsed_bodyNokogiri::HTML5::Documentを返すようになり、それに対してパターンマッチングを行えるようになっています。

get "/posts" # <main><h1>Some main content</h1></main>を含むHTMLを返す
assert_pattern { html.at("main") => { content: "Some main content" } } # マッチする

参考

変更点

自動読み込みのパスがデフォルトで$LOAD_PATHに追加されなくなった

Rails 7.1から自動読み込みパスを$LOAD_PATHに追加しないようになりました。これは、Rails 7.0からZeitwerkモードでの起動が必須になり、Zeitwerkは内部的に絶対パスを使用していて、requirerequire_relativeのように$LOAD_PATHに登録された情報は使用しないからのようです。
libディレクトリはZeitwerk外からも使用されるため、$LOAD_PATHに追加されます。)

参考

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4