`underscore`メソッドで重複する頭字語の順序依存性を解消

rails/rails

RailsのActiveSupport::Inflector#underscoreメソッドで、重複する頭字語(acronym)を定義した際の挙動が不安定になる問題が修正されました。

背景

ActiveSupport::Inflectorでは、inflect.acronymを使ってAPIやURLなどの頭字語を登録できます。しかし、「USD」と「USDC」のように一方が他方の部分文字列になっているケースでは、登録順序によってunderscoreの結果が変わる問題がありました。

これは内部で生成される正規表現が交替演算子(|)を使用しており、左から順に評価されるためです。短い頭字語が先に登録されていると、長い頭字語が正しくマッチすべき場合でも短い方が優先されてしまいます。

技術的な変更

変更前:

def define_acronym_regex_patterns
  @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
  @acronyms_camelize_regex   = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
  @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
end

変更後:

def define_acronym_regex_patterns
  sorted_acronyms = @acronyms.empty? ? [] : @acronyms.values.sort_by { |a| -a.length }
  @acronym_regex = sorted_acronyms.empty? ? /(?=a)b/ : /#{sorted_acronyms.join("|")}/
  @acronyms_camelize_regex   = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
  @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
end

頭字語を長さの降順(sort_by { |a| -a.length })でソートしてから正規表現を構築するようになりました。これにより、常に長い頭字語が優先的にマッチするようになります。

動作例:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym "USD"
  inflect.acronym "USDC"
end

"USD".underscore  # => "usd"
"USDC".underscore # => "usdc"

登録順序に関わらず、「USDC」は「usdc」に、「USD」は「usd」に正しく変換されます。

設計判断

この修正は正規表現の評価順序を制御することで問題を解決しています。代替案として頭字語の境界判定ロジックを複雑化する方法も考えられますが、ソートによる解決は実装がシンプルで、パフォーマンスへの影響も最小限です。

また、sort_byの結果をキャッシュせず、define_acronym_regex_patternsが呼ばれるたびにソートを実行する設計になっています。これは頭字語の登録が初期化時にのみ行われる想定のため、実行時のオーバーヘッドよりもコードの明瞭性を優先した判断と考えられます。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

ガイドライン準拠 ✓ PASS

記事構成とDiffDaily Styleへの準拠状況

記事構成の必須要素(Title, Context, Technical Detail)がすべて網羅され、Design Insightも適切に含まれています。カスタムMarkdown構文も正しく使用されており、ガイドラインに完全に準拠しています。

  • 記事構成(Title、Context、Technical Detail)
  • DiffDaily Styleガイド準拠
  • カスタムMarkdown活用
  • 対象読者への適合性
技術的整合性 ⚠ WARNING

技術的な正確性と表現の適切性

Diff情報が提供されていないため、コードの引用を元Diffと完全に照合できませんでした。しかし、記事内のコード例と技術的な説明は整合性が取れており、Rubyの構文や正規表現の挙動に関する説明も正確です。技術的な誤りはありません。

  • 技術用語の正確性
  • コード例の正確性
  • 説明の技術的正確性
PR内容との整合性 ⚠ WARNING

元のPR情報との一致度

PRのDescriptionが提供されていないため、『Design Insight』セクションの背景にある議論を完全に裏付けることはできませんでした。しかし、主張はPR Titleとコード変更から妥当に推測できる範囲であり、技術的な憶測として適切です。ハルシネーションは見られません。

  • タイトル・説明の一致
  • Diff内容の正確な反映
  • 推測の排除