`number_to_currency` が `precision: nil` の負数でクラッシュするバグを修正
number_to_currency が precision: nil を指定した負の数値で TypeError を起こす不具合を、ガード条件に nil 判定を追加するだけのシンプルな修正で解消します。これにより、同様の挙動を持つ number_to_rounded や number_to_percentage と同一の呼び出し体験が提供されます。
背景
number_to_currency は正の数に対しては precision: nil(丸めなし)を受け付け、"$1,234.5678" のように小数部をそのまま出力しますが、負の数を渡すと TypeError: nil can't be coerced into Integer が発生していました。原因は負数ブランチで使用されている次のガード式です。
format = options[:negative_format] if (number_d * 10 ** options[:precision]) >= 0.5
options[:precision] が nil だった場合に 10 ** nil が評価され、例外が投げられます。一方、number_to_rounded と number_to_percentage は同様のロジックで precision: nil を許容しており、負数でも正しく動作します。この不整合がユーザーに予期せぬエラーをもたらしていました。
技術的な変更
負数ブランチのガード式に options[:precision].nil? のチェックを追加し、precision が nil の場合は直接 negative_format を適用するよう変更しました。変更点は以下の通りです。
変更前:
format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
変更後:
format = options[:negative_format] if options[:precision].nil? || (number_d * 10**options[:precision]) >= 0.5
この1行の修正により、precision が nil の場合は丸めが行われず、負の符号はそのまま保持されます。さらに、回帰テストとして以下の3ケースを activesupport/test/number_helper_test.rb に追加しました。
assert_equal("$1,234.5678", number_helper.number_to_currency(1234.5678, precision: nil))
assert_equal("-$1,234.5678", number_helper.number_to_currency(-1234.5678, precision: nil))
assert_equal("-$1,234.5678", number_helper.number_to_currency("-1234.5678", precision: nil))
テストが全て通過し、他の数値ヘルパーメソッドとの挙動の統一が確認できました。
設計判断
今回の修正は 最小侵入 を理念とし、既存ロジックへの影響を限定的に抑える形で実装されています。負数ブランチに nil 判定を加えるだけで、positive パスと同様のフローを再利用でき、コードサイズや実行コストの増加は事実上ありません。また、precision: nil が「丸めなし」を意味するという設計意図を保ちつつ、符号保持のロジックを統一した点が評価されます。変更はバグ修正に留まり、機能追加や非互換な API 変更を伴わないため、既存利用者へのリグレッションリスクはありません。
まとめ
number_to_currency が precision: nil の負数でクラッシュしていた根本原因は、負数ガードで 10 ** nil が評価されていたことでした。options[:precision].nil? のチェックを追加したシンプルな修正により、同様の数値ヘルパーと挙動を揃え、例外を排除しました。テスト追加により回帰が防止され、既存コードへの影響は最小限に抑えられています。