ContentSecurityPolicyの`build_directive`で`flat_map`を使用するリファクタリング

rails/rails

build_directiveメソッドがflat_mapを使うよう変更され、validateメソッド内での不要なflatten呼び出しが除去されました。あわせてbuild_directiveの戻り値がvalidateの戻り値に依存しない形に修正されています。

背景

validateメソッドはCSPディレクティブのソースに不正な文字(セミコロンや空白)が含まれていないかを検証する役割を持ちます。変更前はsources.flatten.eachと書かれており、validateflattenの責務を兼ねていました。これは、build_directivemapで生成したネストした配列をvalidate側でならす設計になっていたことを意味します。

この構造では、build_directiveの戻り値がvalidateの戻り値(eachの戻り値、すなわちflatten後の配列)に暗黙的に依存していました。明示的なreturnがなく、Rubyの「最後の式が戻り値になる」という性質に頼っていたため、意図が読み取りにくいコードになっていました。

技術的な変更

build_directiveでのmapflat_mapに変えることで、配列の平坦化をbuild_directive内で完結させています。

変更前:

def validate(directive, sources)
  sources.flatten.each do |source|
    if source.include?(";") || source != source.gsub(/[[:space:]]/, "")
      raise InvalidDirectiveError, <<~MSG.squish
        Invalid Content Security Policy #{directive}: "#{source}".
      MSG
    end
  end
end

def build_directive(directive, sources, context)
  resolved_sources = sources.map { |source| resolve_source(source, context) }

  validate(directive, resolved_sources)
end

変更後:

def validate(directive, sources)
  sources.each do |source|
    if source.include?(";") || source != source.gsub(/[[:space:]]/, "")
      raise InvalidDirectiveError, <<~MSG.squish
        Invalid Content Security Policy #{directive}: "#{source}".
      MSG
    end
  end
end

def build_directive(directive, sources, context)
  resolved_sources = sources.flat_map { |source| resolve_source(source, context) }

  validate(directive, resolved_sources)

  resolved_sources
end

validateは「与えられたソース配列を検証する」という単一の責務に整理され、平坦化は不要になりました。build_directiveは末尾にresolved_sourcesを明示的に返すことで、戻り値がvalidateの実装詳細に依存しない形になっています。

設計判断

各メソッドの責務を明確に分離する方針が取られています。

変更前は「配列を平坦化する」という責務がvalidateに混入していました。validateは本来バリデーションのみを担うべきメソッドであり、入力の整形を行うのは呼び出し元のbuild_directiveが適切です。flat_mapへの変更はこの責務の整理を実現しています。

また、build_directiveresolved_sourcesの明示的なreturnを追加した点も重要です。変更前はvalidateの戻り値(eachの戻り値)がbuild_directiveの戻り値として暗黙的に使われており、validateの実装が変わるとbuild_directiveの戻り値も変わってしまう脆弱な構造でした。明示的なresolved_sourcesの返却により、この依存関係が断ち切られています。

まとめ

本PRは動作を変えずに各メソッドの責務を整理したリファクタリングです。flat_mapへの置き換えと明示的な戻り値の追加という小さな変更が、「平坦化はデータを生成する側の責務」「戻り値は実装詳細に依存させない」という設計原則を明確に体現しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
f2347bff

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が記事全体で明確に適用されており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```ruby:ファイルパス)とGitHubのPRリンク記法([#56998](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

専門用語(flat_map, 責務, 依存関係)を適切に使用し、冗長な初心者向けの説明を省くことで、専門知識を持つエンジニアという対象読者に適した内容になっています。

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

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

各セクションが総論→各論の構成で書かれ、各パラグラフの先頭にトピックセンテンスが置かれているため、非常に読みやすく、内容を素早く把握できます。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは、提供されたDiff情報を正確に反映しています。変更前後のコードが正しく引用され、ファイル名も一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

PR情報で使われている`flat_map`, `validate`, `build_directive`などの技術用語を正確に使用しており、文脈に合った適切な用語選択がなされています。

説明の技術的正確性 ✓ PASS

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

「flat_mapへの変更によりflattenが不要になった」「明示的なreturnで依存関係を断ち切った」といった説明は、DiffとPR Descriptionによって裏付けられており、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

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

記事のすべての主張がPRのタイトル、説明、Diff内容に基づいており、根拠のない推測や創作(ハルシネーション)は見られません。「設計判断」セクションも、コードの変更から論理的に導き出せる範囲の解説に留まっています。

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

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

PR番号(#56998)やメソッド名、ファイル名などの固有名詞がすべて正確に記載されています。

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

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

記事のタイトルは元のPRのタイトル「Use `flat_map` in ContentSecurityPolicy」の内容を的確に反映しており、記事全体の主題と一致しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、バージョン情報やリリース予定など、PR外の知識を持ち出しての解説は行われていません。

時間表現の正確性 ✓ PASS

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

記事内には時間表現に関する記述はなく、PR情報との不整合もありません。