Action TextのBlobアタッチメントがMarkdownリンクを生成

rails/rails

Action Textの to_markdown メソッドが、レンダリングコンテキストが利用可能な場合にActiveStorage::BlobアタッチメントをMarkdownリンクとして出力するようになりました。これにより、コンテンツのMarkdown変換時に添付ファイルへの実際のリンクが含まれるようになります。

背景

#56858 でAction TextにMarkdown変換機能が追加されましたが、ActiveStorage::Blob#attachable_markdown_representation は単なるプレースホルダー [caption] を返すだけで、実際のリンクを生成していませんでした。一方、RemoteImage![title](url) の形式で正しいMarkdownリンクを生成していたため、実装に一貫性がありませんでした。

この非対称性により、同じAction Textコンテンツ内でも、リモート画像は実用的なMarkdownリンクとして変換される一方、ActiveStorageのBlobアタッチメントはプレースホルダーのままという状況が生じていました。#56894 がこの問題を解消しています。

技術的な変更

Markdown変換時にBlobアタッチメントをリンクとして出力するため、to_markdown メソッドに attachment_links パラメータが追加されました。このパラメータを true に設定すると、レンダリングコンテキストが利用可能な場合にBlobアタッチメントがMarkdownリンクとして変換されます。

メソッドシグネチャの変更

RichText#to_markdownContent#to_markdownAttachment#to_markdown の各メソッドに attachment_links パラメータが追加されました:

def to_markdown(attachment_links: false)
  body&.to_markdown(attachment_links: attachment_links).to_s
end

このパラメータはメソッドチェーン全体を通じて伝播し、最終的に各アタッチメントの attachable_markdown_representation メソッドに渡されます。

Blobのリンク生成ロジック

ActiveStorage::Blob#attachable_markdown_representation の実装が大きく変更されました:

def attachable_markdown_representation(caption = nil, attachment_links: false)
  title = (caption || filename).to_s

  if attachment_links
    renderer = ActionText::Content.renderer
    raise ArgumentError, "attachment_links requires a rendering context" unless renderer

    url = renderer.url_for(self)
    if image?
      "!#{MarkdownConversion.markdown_link(title, url)}"
    else
      MarkdownConversion.markdown_link(title, url)
    end
  else
    "[#{MarkdownConversion.escape_markdown_text(title)}]"
  end
end

attachment_links: true の場合、ActionText::Content.renderer からレンダリングコンテキストを取得し、url_for(blob) を通じてBlobのURLを生成します。画像Blobは ![title](url) 形式、非画像Blobは [title](url) 形式で出力されます。レンダリングコンテキストが利用できない場合(例:Railsコンソール)は ArgumentError が発生します。

既存の動作との互換性

attachment_links パラメータのデフォルト値は false であり、既存のコードは従来通りプレースホルダー形式 [title] で変換されます:

# デフォルト動作(変更なし)
message.content.to_markdown
# => "Check out this [report.pdf]"

# リンク生成を有効化
message.content.to_markdown(attachment_links: true)
# => "Check out this [report.pdf](http://example.com/rails/active_storage/blobs/...)"

カスタムアタッチメントへの影響

attachable_markdown_representation メソッドのシグネチャが変更されたため、このメソッドをオーバーライドしているカスタムアタッチメントは新しいパラメータに対応する必要があります。ただし、キーワード引数であるため、パラメータを使用しない実装でも動作は継続します:

# 新しいシグネチャに対応
def attachable_markdown_representation(caption, attachment_links: false)
  "[@#{name}](#{profile_url})"
end

ContentAttachmentMissingAttachableRemoteImage の各クラスも新しいシグネチャに更新されています。

設計判断

URL生成はグローバルな default_url_options ではなく、スレッドローカルの ActionText::Content.renderer を通じて現在のリクエストコンテキストから導出されます。

この設計により、hostprotocolscript_name といったURL構成要素がリクエストから自動的に取得されます。PR本文では、リクエストごとに script_name が変わるマルチテナントアプリケーションにおいて重要だと説明されています。

renderer の設定は、エンジンイニシャライザ内で around_action により行われます。コントローラーやメーラーのアクション内では常に適切なレンダリングコンテキストが利用可能ですが、Railsコンソールのような環境では利用できません。この制約により、リンク生成が明示的に要求されたにもかかわらず実行できない状況では、エラーを発生させる挙動となっています。

まとめ

本PRは、Action TextのMarkdown変換機能を実用的なものにするための拡張です。attachment_links パラメータの追加により、リクエストコンテキストが利用可能な状況ではBlobアタッチメントを実際のMarkdownリンクとして変換できるようになりました。デフォルト動作は変更されていないため、既存のコードへの影響は最小限に抑えられています。

記事メタデータ

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

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が非常に明確で、理想的な記事構成です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```ruby:filepath```)およびGitHubのPR番号リンク(`[#123](URL)`)のカスタムMarkdown構文が正しく使用されています。

対象読者への適合性 ✓ PASS

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

メソッドシグネチャ、レンダリングコンテキスト、スレッドローカル変数といった技術的な概念を前提としており、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

各セクション、各パラグラフが総論から始まり、具体例で補強する構成になっています。トピックセンテンスが明確で、1段落1トピックが守られており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

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

記事で引用されているコードブロックは、提供されたDiff情報と完全に一致しており、ファイルパスの指定も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`attachment_links`, `ActionText::Content.renderer`, `around_action`などの技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

「`attachment_links`のデフォルト値が`false`であること」「レンダリングコンテキストがない場合に`ArgumentError`が発生すること」など、Diffのコードから読み取れる挙動を正確に説明しています。

事実の突合 ⚠ WARNING

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

記事は「`attachable_markdown_representation`のシグネチャが変更されたため、カスタムアタッチメントは対応が必要」と述べていますが、PR Descriptionでは「custom attachables are unaffected」と明記されており、矛盾しています。ただし、記事は続けて「キーワード引数なので動作は継続する」と補足しており、技術的な正確さは担保されています。

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

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

PR番号(#56894, #56858)が正確に記載・リンクされています。

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

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

記事のタイトルは、PRのタイトル「Action Text `to_markdown` generates markdown links for blob」の内容を正確に和訳・要約しています。

外部知識の正確性 ✓ PASS

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

記事はPR情報(Title, Description, Diff)のみに基づいており、バージョン情報やリリース予定などの外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

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

「従来はプレースホルダーを返していた」「現在はリンクを生成できるようになった」といった時間的な前後関係の表現は、PRの内容と一致しています。