`ActiveSupport::Notifications.subscribe` に `prepend: true` オプションを追加

rails/rails

ActiveSupport::Notifications.subscribeprepend: true オプションが追加され、サブスクライバーをリストの先頭に登録できるようになりました。これにより、他のサブスクライバーが処理する前にイベントペイロードを変更することが可能になります。

背景

これまで、既存のサブスクライバーより先にイベントペイロードを変更する手段がありませんでした。たとえば sql.active_record イベントの name フィールドを特定の条件下で書き換えたい場合、既存のサブスクライバーの実行順序を制御する方法がなく、モンキーパッチに頼らざるを得ない状況でした。

PRの動機として挙げられているのは、IdentityCache使用時に name"[IDC] #{name}" のように書き換えるユースケースです。ペイロードの変更が他のサブスクライバーより先に行われることを保証できなければ、意図した変更が他のサブスクライバーの処理に反映されません。prepend: true はこの問題を、実行順序を明示的にコントロールするという形で解決します。

技術的な変更

変更は Notifications モジュール、Fanout クラスの2層にわたり、それぞれに prepend: キーワード引数が追加されています。

ActiveSupport::Notifications のパブリックAPIの変更:

変更前:

def subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: false, &block)
end

def monotonic_subscribe(pattern = nil, callback = nil, &block)
  notifier.subscribe(pattern, callback, monotonic: true, &block)
end

変更後:

def subscribe(pattern = nil, callback = nil, prepend: false, &block)
  notifier.subscribe(pattern, callback, monotonic: false, prepend: prepend, &block)
end

def monotonic_subscribe(pattern = nil, callback = nil, prepend: false, &block)
  notifier.subscribe(pattern, callback, monotonic: true, prepend: prepend, &block)
end

Fanout#subscribe の内部実装の変更:

prepend: true の場合、@string_subscribers[pattern] に対して << の代わりに unshift を呼び出すことでサブスクライバーをリストの先頭に挿入します。

def subscribe(pattern = nil, callable = nil, monotonic: false, prepend: false, &block)
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
  @mutex.synchronize do
    case pattern
    when String
      if prepend
        @string_subscribers[pattern].unshift(subscriber)
      else
        @string_subscribers[pattern] << subscriber
      end
      clear_cache(pattern)
    when NilClass, Regexp
      if prepend
        raise ArgumentError, "Cannot prepend Regex subscribers"
      end
      @other_subscribers << subscriber
      clear_cache
    else

Regexpおよびnilパターン(全イベント購読)に対して prepend: true を指定すると ArgumentError が発生します。これは @other_subscribers が文字列パターンとは別に管理されており、Regexp購読は実行順序が保証される構造になっていないためです。

テストでは、通常の順序で登録した :first:second に加え prepend: true で登録した :prepended[:prepended, :first, :second] の順で実行されることを確認しています。

設計判断

prepend: true をStringパターン限定とした設計が採用されています。

@string_subscribers はパターン文字列をキーとするHashであり、配列の先頭への挿入(unshift)によって順序を確実に制御できます。一方、@other_subscribers に格納されるRegexpおよびnilパターンのサブスクライバーは、マッチング処理の構造上、同様の順序保証を提供することが困難です。ArgumentError を明示的に発生させることで、サポートされないパターンの使用を早期に検出できるようにしています。

また、prepend: のデフォルト値を false とすることで、既存のすべての呼び出しコードへの影響をゼロに抑えています。

まとめ

本PRは、最小限のAPIの拡張でサブスクライバーの実行順序を制御する手段を提供した変更です。unshift という単純な配列操作と prepend: false のデフォルト値により後方互換性を完全に維持しつつ、モンキーパッチなしにペイロードの前処理が可能になりました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
0592624e

この記事は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リンク記法の正確性

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

対象読者への適合性 ✓ PASS

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

専門用語が適切に使用されており、専門知識を持つエンジニアという対象読者に適合した内容です。

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

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

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

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

記事内のコード引用は、提供されたDiffの内容と完全に一致しており、変更点が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「サブスクライバー」「ペイロード」「後方互換性」などの技術用語が、文脈に応じて正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

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

`unshift` を使う実装や、Regexpパターンで `ArgumentError` が発生する理由など、技術的な説明が正確で論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのDescriptionやDiff内のコードで裏付けられており、ハルシネーションは検出されませんでした。

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

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

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

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

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

記事のタイトルはPR「Allow to prepend AS::Notification subscribers」の内容を的確に要約しており、主題の不一致はありません。

外部知識の正確性 ✓ PASS

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

PR情報に含まれないバージョン情報やリリース予定などの外部知識の追記はなく、提供された情報に忠実です。

時間表現の正確性 ✓ PASS

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

「これまで...手段がありませんでした」といった時間表現が、PRの文脈と正確に一致しています。