Composite Primary Keyで find(nil) が RecordNotFound を返すように修正
Composite primary key を持つモデルで Model.find(nil) が ActiveRecord::RecordNotFound を返すように実装を調整しました。これにより、ドキュメント通りの例外が一貫して発生します。
背景
Composite primary key を持つモデルで find(nil) を実行すると NoMethodError: undefined method 'first' for nil が発生していました。Key::Composite#expects_multiple_ids? が value.first.is_a?(Array) だけを評価し、nil に対して first を呼び出すガードが欠如していたことが原因です。結果として、ActiveRecord が期待する ActiveRecord::RecordNotFound ではなく、内部エラーが外部へ漏れていました。
この挙動はシングルキーのパスとは対照的です。シングルキーの場合は Key::Single#expects_multiple_ids? が value.is_a?(Array) と nil‑safe にチェックするため、nil が渡っても正しく RecordNotFound がスローされます。テストスイートでもシングルキーは既にこの動作を検証しており、差異が問題視されていました。
技術的な変更
Key::Composite#expects_multiple_ids? に nil 安全ガード を追加し、配列判定を二段階で行うようにしました。
@@
- value.first.is_a?(Array)
+ value.is_a?(Array) && value.first.is_a?(Array)
この変更により、value が nil の場合は false を返すため、検索ロジックは RecordNotFound へフォールバックします。既存の nil 以外の入力に対しては従来と同等の挙動を維持し、機能的な影響はありません。
テストも追加して、Composite キーのモデルで find と find(nil) が期待通り ActiveRecord::RecordNotFound をスローすることを明示しました。
@@
def test_find_with_ids_with_no_id_passed
assert_equal "id", exception.primary_key
end
+
+ def test_find_with_no_id_passed_on_composite_primary_key_model
+ assert_raises(ActiveRecord::RecordNotFound) { Cpk::Book.find }
+ assert_raises(ActiveRecord::RecordNotFound) { Cpk::Book.find(nil) }
+ end
設計判断
この修正は インターフェースの変更を伴わないガード条件の追加 だけで問題を解決しています。既存の Key::Composite の API はそのまま残り、外部からの呼び出し方に影響はありません。
また、Key::Single と同様の nil‑safe 判定 を採用したことで、コードベース全体の一貫性が向上しました。二重の配列チェックは意図的に value.is_a?(Array) を先に評価し、不要なメソッド呼び出しを防ぐシンプルなトレードオフです。
まとめ
Key::Composite#expects_multiple_ids? に nil 安全ガードを加えることで、Composite primary key モデルでの find(nil) が正しい例外 ActiveRecord::RecordNotFound を返すようになりました。変更は最小限で後方互換性を維持しつつ、ドキュメント記載通りの挙動を実現しています。