Enumerable#in_order_of が filter:false で nil を除去しないよう修正

rails/rails

Enumerable#in_order_offilter: false オプションは、シリーズに未定義の要素を結果に残すことが仕様とされていますが、以前の実装では compact が誤って適用され、nil が除去されていました。本稿ではその原因と修正内容を詳しく解説します。

背景

Enumerable#in_order_of は、キー関数と順序配列(series)を受け取り、要素を指定順に並べ替えるユーティリティです。ドキュメントには次のように記載されています。「filter オプションが false に設定されている場合、シリーズに含まれない要素は結果に残す」(quoted). この期待は、nil 要素も含めて保持されることを意味します。

しかし実装の filter: false ブランチで compact が呼び出されており、nil が意図せず除去されていました。具体例として、[3, nil, 1, 2].in_order_of(:itself, [1, 2, 3], filter: false)[1, 2, 3] という結果になり、nil が失われていました。この挙動は filter: true 用のコードをコピーしたことが原因です。

結果として、API 仕様と実装が食い違い、利用側が nil を保持できないというバグが発生していました。ドキュメントどおりに動作させるための修正が必要でした。

技術的な変更

filter: false ブランチから余計な compact 呼び出しを削除しました。変更前後のコードは以下の通りです。

# 変更前
sort_by { |v| series.index(v.public_send(key)) || series.size }.compact

# 変更後
sort_by { |v| series.index(v.public_send(key)) || series.size }

このシンプルな差分により、nil 要素がそのままソート結果に残ります。機能的な変更はなく、既存の filter: true ロジックにも影響を与えません。

また、回帰テストを追加し、nil が保持されることを自動的に検証できるようにしました。テストコードは次のとおりです。

def test_in_order_of_with_filter_false_preserves_nil_elements
  values = [3, nil, 1, 2]
  assert_equal [1, 2, 3, nil], values.in_order_of(:itself, [1, 2, 3], filter: false)
end

さらに、CHANGELOG に「Enumerable#in_order_offilter: falsenil を除去していたバグを修正」旨のエントリを追加し、利用者に変更点を周知できるようにしています。

この修正は 1 行の削除とテスト追加のみであり、既存の挙動に副作用を与えることはありません。filter: true のロジックはそのまま残ります。

設計判断

filter: truefilter: false の二つの分岐はそれぞれ異なる目的を持つため、共通化しすぎない設計が採られています。filter: true では group_by の結果に nil が生じるため compact が必要ですが、filter: false では sort_bynil を生成しないので compact は不要です。今回の修正は、分岐ごとのロジックを正しく保ち、意図しない副作用を除去するという設計方針を再確認したものです。

この判断により、API の契約(contract)と実装の整合性が保たれ、将来的な機能追加時にも各分岐の責務が明確なまま保守しやすくなります。

まとめ

Enumerable#in_order_offilter: false オプションが nil を除去していたバグを、余計な compact 呼び出しの削除で修正しました。追加した回帰テストにより、今後同様のミスが再発しにくくなっています。ドキュメント通りの動作が保証されたことで、利用者は安心して filter: false を活用できるようになりました。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的詳細、設計判断(任意)、まとめの構成が揃っており、3部構成が明確に表現されています。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックのファイル名付きハイライトは正しい形式です。PRリンクは「[PR #57553](URL)」となっており、#番号だけの形式ではないため軽度のフォーマット違いと判断します。

対象読者への適合性 ✓ PASS

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

対象はRails/ActiveSupportに精通したエンジニアで、専門用語中心の記述となっており、初心者向けの過剰説明はありません。

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

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

各セクションは総論・各論・結論のパラグラフ構成で、トピックセンテンスが先頭にあり、1段落1トピック、6文以内で区切られています。空行も適切です。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは提供されたDiffと完全に一致し、削除・追加箇所が正確に示されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語(compact, filter, sort_by など)はPRと一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

技術的説明はPRの変更点と整合し、因果関係も正しく記述されています。

事実の突合 ✓ PASS

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

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

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

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

PR番号 #57553 などの数値は正確です。その他数値・定数の誤りは見られません。

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

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

記事タイトルはPRタイトルの意味を適切に日本語化しており、内容と一致しています。

外部知識の正確性 ✓ PASS

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

バージョンサポートやリリース日程等の外部情報は記載されていません。

時間表現の正確性 ✓ PASS

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

時間表現は使用しておらず、PRと矛盾はありません。