`insert!` が `:unique_by` オプションを受け入れ、一貫性を向上

rails/rails

insert!:unique_by キーワードを受け取れるようになり、insert と同様の重複回避オプションが利用可能になりました。これにより、ActiveRecord の挿入系メソッド間でインターフェースの齟齬が解消され、ドキュメント通りにオプションを指定できるようになります。

背景

insert! のシグネチャが unique_by: を欠いていたことがエラーの根本原因でした。 ActiveRecord::Persistence::ClassMethods#insert#insert_all!:unique_by を受け付けますが、#insert!def insert!(attributes, returning: nil, record_timestamps: nil) という定義のみで、unique_by: を渡すと ArgumentError: unknown keyword: :unique_by が発生していました。ドキュメントは #insert_all! で利用できるオプションを参照させていたため、実装と記述が食い違っていました。この不整合は PostgreSQL や SQLite で重複回避機能を活用したいケースで顕在化していました。

技術的な変更

insert! のメソッド定義に unique_by: キーワード引数を追加し、insert_all! へ転送する実装に変更しました。 変更前後を示すと以下の通りです。

-def insert!(attributes, returning: nil, record_timestamps: nil)
-  insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
+def insert!(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
+  insert_all!([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)

このシグネチャ拡張はデフォルトで unique_by: nil を設定し、既存の呼び出し側に影響を与えません。さらに、テストスイートに test_insert_bang_accepts_unique_by が追加され、supports_insert_conflict_target? が true のデータベース (PostgreSQL/SQLite) で insert! が正しくオプションを受け取ることを検証しています。

+def test_insert_bang_accepts_unique_by
+  skip unless supports_insert_conflict_target?
+
+  assert_difference "Book.count", +1 do
+    Book.insert!({ name: "UniqueBy", author_id: 1, isbn: "unique-by-insert-bang" }, unique_by: :isbn)
+  end
+end

CHANGELOG には「insert! now accepts the :unique_by option, consistent with insert」というエントリが追記され、ユーザーへの告知が行われています。

設計判断

メソッドシグネチャの統一を優先し、互換性を維持する方針が採られました。 unique_by: をオプション化しデフォルトを nil としたことで、既存コードがそのまま動作し続けます。一方で、insert!insert のインターフェースが揃うことで、開発者はどのメソッドでも同一のオプションセットを利用でき、ドキュメントと実装の乖離が解消されました。新たに導入されたテストは、プラットフォーム固有の条件下でも安全に動作することを保証しています。

まとめ

insert!:unique_by オプションを追加し insert_all! へ転送することで、ActiveRecord の挿入系メソッド間の一貫性が確保されました。既存呼び出しへの影響はなく、重複回避機能をシンプルに利用できるようになった点が主な価値です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
c218c79a

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文が冒頭にあり、背景、技術的変更、設計判断、まとめの各セクションが揃っているので『総論→各論→結論』の構成が明確です。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは `ruby:ファイルパス` 形式で正しく記述されていますが、PRリンクが `[PR #57614](URL)` となっており、仕様の `[ #123 ](URL)` 形式と完全に一致していません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Rails の内部実装に詳しいエンジニア向けの専門的な記述で、初心者向けの余計な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各段落はトピックセンテンスで始まり、1段落1トピック、6文以内で書かれ、空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供された Diff と完全に一致し、ファイル名・変更内容とも合致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`unique_by`、`insert!`、`insert_all!` などの用語は PR 記述と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的主張は PR の説明とコード変更に根拠があり、論理的に正しいです。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全ての事実は PR のタイトル、説明、Diff で裏付けられており、捏造や推測はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #57614 などの数値は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の主旨「insert! が :unique_by を受け入れる」ことを正しく表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事は PR に記載された情報以外の外部知識(LTS、リリース日等)を含んでいません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間に関する表現はなく、PR と食い違う箇所もありません。