number_to_delimited の符号処理バグを修正

rails/rails

number_to_delimited が符号("-"/"+")付きの数値を整形する際、桁区切り文字が符号直後に誤って挿入されるバグを解消しました。負数やプラス記号付き文字列が正しくフォーマットされることで、請求書やレポートへの誤出力リスクが排除されます。

背景

このバグは NumberToDelimitedConverter#parts が整数部を 3 桁ごとに分割するロジックで、offset = left.size % 3 を用いた高速パスがデフォルト化されたことに起因します。符号が left 文字列の先頭に含まれるため、最初のグループが符号単体となり、区切り文字が "-," の形で出力されていました。結果として number_to_delimited(-123456)"-,123,456" という誤った文字列を返し、 downstream の number_to_roundednumber_to_human でも同様の破損が発生しました。

技術的な変更

NumberToDelimitedConverter#parts で整数部の先頭が符号かどうかを判定し、符号があれば一時的に除去してから 3 桁区切りを行い、最後に符号を再結合するよう修正しました。これにより符号は区切り処理の対象から外れ、正しいフォーマット "-123,456" が得られます。変更は activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb の数行に留まり、既存の delimiter_pattern 正規表現パスはそのまま残しています。

- offset = left.size % 3
- if offset > 0
-   left_parts << left[0, offset]
+ sign = left.slice!(0) if left.start_with?("-", "+")
+ offset = left.size % 3
+ if offset > 0
+   left_parts << left[0, offset]
   end
   # ...省略...
- left = left_parts.join(options[:delimiter])
+ left = "#{sign}#{left_parts.join(options[:delimiter])}"

テストスイートにも activesupport/test/number_helper_test.rb に符号付きケースを網羅するテストを追加し、負数・正数・文字列形式の両方で期待通りの出力が得られることを検証しました。新しいテストは test_to_delimited_with_negative_numberstest_to_delimited_with_leading_plus_sign の二つで、様々な桁数・小数部の組み合わせをチェックしています。

+def test_to_delimited_with_negative_numbers
+  assert_equal "-123,456", NumberHelper.number_to_delimited(-123456)
+  # ...他ケース省略...
+end
+
+def test_to_delimited_with_leading_plus_sign
+  assert_equal "+123,456", NumberHelper.number_to_delimited("+123456")
+  # ...他ケース省略...
+end

設計判断

符号除去・再付加のロジックは NumberToCurrencyConverter が採用している方式と同様で、既存の API を変更せずに内部実装だけを調整するという最小侵襲の方針が取られました。delimiter_pattern の正規表現パスは残したため、従来の高速パスとレガシーパスの両方が同等に動作し、後方互換性が確保されています。

この修正は number_to_roundednumber_to_human といった他のヘルパーが内部で同じコンバータを利用している点でも効果を及ぼし、追加の変更なしで広範囲にわたるフォーマット不整合が解消されました。結果として、金額や統計情報の表示に関わる全てのユースケースで安全に区切り文字を使用できるようになります。

まとめ

符号付き数値の区切り処理バグを根本的に修正し、負数・プラス記号付き文字列が正しく整形されるようになりました。実装変更は局所的で後方互換性を損なわず、関連ヘルパー全体にプラスの影響を与えることから、Rails の数値ヘルパー群の信頼性が向上したと言えます。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文→背景→技術的な変更→設計判断→まとめの流れが明確で、各要素が揃っている。まとめはリード文の単なる繰り返しではなく意義を付加している。

カスタムMarkdown構文 ✓ PASS

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

コードブロックは ````ruby:ファイルパス```` 形式で正しく記述され、PRリンクは [PR #57620](URL) の形でリンク化されている。コミットIDの記載は不要。

対象読者への適合性 ✓ PASS

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

Rails の数値ヘルパーに詳しいエンジニア向けの内容で、初心者向けの余計な説明はない。

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

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

各セクションが総論→各論→結論の構成になっており、段落の冒頭が要点を示すトピックセンテンスになっている。段落は6文未満で空行で区切られている。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供されたDiffと一致しており、削除・追加行が正しく反映されている。省略部分は説明に支障なし。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語(NumberToDelimitedConverter、number_to_delimited、delimiter など)は PR と一致し、誤用は見られない。

説明の技術的正確性 ✓ PASS

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

技術的な説明は Diff と PR の内容に基づき正確。因果関係も矛盾なく示されている。

事実の突合 ✓ PASS

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

すべての主張が PR のタイトル、説明、Diff に裏付けられており、外部知識の付加や推測はない。

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

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

PR 番号 #57620 が正しく記載されている。コード中の行番号や数値の記述に誤りはない。

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

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

記事タイトルは PR の内容を的確に日本語化しており、意味合いにズレはない。

外部知識の正確性 ✓ PASS

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

LTS、EOL、リリース日程等の外部情報は記載されていないため問題なし。

時間表現の正確性 ✓ PASS

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

時間表現の記載はなく、PR の記述と矛盾する表現もない。