Rubyの文字列補間式からクラス名を抽出

tailwindlabs/tailwindcss

Tailwind CSSのRuby向けエクストラクタにおいて、ヒアドキュメント内の文字列補間式(#{...})からクラス名が抽出されない問題が修正されました。コメント判定ロジックの改善により、補間式内の文字列リテラルが正しく認識されるようになります。

背景

Tailwind CSSのRuby向けエクストラクタは、コメント(#で始まる行)を無視する処理を実装していました。しかし、この処理がRubyの文字列補間式(#{...})も誤ってコメントとして扱ってしまい、ヒアドキュメント内の補間式からクラス名が抽出されない問題が発生していました。#19728 でこの問題が報告されています。

既に strict locals の定義(<%# locals: ... %>)に対する例外処理は実装されていましたが、文字列補間式については考慮されていませんでした。以下のコードでは、'w-100' が抽出されない状態でした:

def width_class_heredoc(width = nil)
  <<~STYLE_CLASS
    inline-flex
    #{width || 'w-100'}
  STYLE_CLASS
end

この問題はヒアドキュメント内の補間式に特有で、通常の文字列補間("#{width || 'w-99'}")や単純な式(width || 'w-98')からは正しくクラス名が抽出されていました。

技術的な変更

crates/oxide/src/extractor/pre_processors/ruby.rsコメント判定ロジック に、文字列補間式を除外する条件が追加されました。

変更前:

b'#' if !matches!(cursor.prev(), b'%') => {
    result[cursor.pos] = b' ';
    cursor.advance();
    // ... コメント行のスキップ処理
}

変更後:

b'#' if !matches!(cursor.prev(), b'%') && !matches!(cursor.next(), b'{') => {
    result[cursor.pos] = b' ';
    cursor.advance();
    // ... コメント行のスキップ処理
}

!matches!(cursor.next(), b'{') という条件により、# の次の文字が { である場合(つまり #{ パターン)はコメントとして扱わないようになりました。これにより、文字列補間式内の文字列リテラルがクラス名として正しく抽出されます。

追加されたリグレッションテストでは、ヒアドキュメント内の補間式から 'w-100' が抽出されることを確認しています:

#[test]
fn test_interpolated_expressions() {
    let input = r#"
        def width_class(width = nil)
          <<~STYLE_CLASS
            #{width || 'w-100'}
          STYLE_CLASS
        end
    "#;

    Ruby::test_extract_contains(input, vec!["w-100"]);
}

設計判断

コメント判定の条件に1文字先読みを追加する方式 が採用されました。

Rubyのコメント構文と文字列補間式は共に # で始まるため、文脈を考慮した判定が必要です。本修正では、既存の cursor.prev() による前方文字チェック(strict locals用)に加えて、cursor.next() による後方文字チェックを追加しています。この判定順序により、以下の3パターンを正しく区別できます:

  • <%# locals: ... %>: strict localsの定義(%# パターン)→ スキップしない
  • #{ ... }: 文字列補間式(#{ パターン)→ スキップしない
  • # comment: 通常のコメント → スキップする

1文字の先読みのみで判定を完結させることで、パーサの状態管理を複雑化させずに問題を解決しています。

まとめ

本PRは、Rubyのコメント判定ロジックに文字列補間式の除外条件を追加した修正です。1文字の先読みチェックを加えるだけで、ヒアドキュメント内の補間式からのクラス名抽出が可能になり、Rubyプロジェクトでのユーティリティファーストなクラス定義パターンがより柔軟になります。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が明確で、読者が理解しやすい構造になっています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライトやGitHubのIssue/PRへのリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

Tailwind CSSの内部実装に関するトピックであり、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で書かれています。

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

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

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。これにより、高い可読性が確保されています。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロック(変更前、変更後、テストコード)は、提供されたDiff情報と完全に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「文字列補間式」「ヒアドキュメント」「エクストラクタ」など、技術用語が正確かつ文脈に適した形で使用されています。

説明の技術的正確性 ✓ PASS

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

`!matches!(cursor.next(), b'{')`という条件追加がなぜ問題を解決するのかについて、技術的に正確で論理的な説明がなされています。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescriptionやDiff内のコード・コメントによって裏付けられており、ハルシネーション(創作)は見られません。

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

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

PR番号(#19730)やIssue番号(#19728)、ファイルパスなどの固有名詞はすべて正確です。

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

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

記事のタイトル「Rubyの文字列補間式からクラス名を抽出」は、PRの主題を的確に要約しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、バージョン情報やリリース予定といったPR外の知識を持ち込んでいません。

時間表現の正確性 ✓ PASS

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

PR Descriptionにある「already」を「既に」と正しく訳すなど、時間に関する表現はPR情報と一致しています。