2つのオープンなPRが、投稿の生テキスト(raw)内のBBCode属性を編集するための異なるアプローチを導入しています:
-
#43187 — Add recurring Event successor topic mode は
BbcodeRewriterを導入し、レンダリングされたイベントを特定して、自動ローラーオーバー時にそれに対応するBBCode開始タグを安全に書き換えます。 -
#42932 — Add Event actions to workflows は
PrettyText.update_bbcode_attributesを導入し、ワークフローがイベントを開くまたは閉じる際に、パースされたBBCodeブロックを特定してその属性を更新します。
#43187 のレビュー中、Martin氏はイベント固有のサービスから汎用的なBBCode操作機能を抽出することを提案しました。
当初、私は別のPRからの関連するトークンベースの機能を含めていましたが、これにより #43187 内に2つの書き換えメカニズムが生じました。トークンベースのAPIはそのブランチに本番環境での呼び出し元がなかったため、コミット 6d13371 で削除しました。
トークンベースのAPIは、#42932 に対して独立した有用性を保っています。
これらの2つのアプローチは最終的に1つの再利用可能なコアAPIに統合されるべきか、それとも異なる要件が両方の保持を正当化するか疑問に思いました。
現在の実装の比較
この表は、どちらのアプローチも本質的に誤りであることを示唆するのではなく、コードが現在実装している内容を説明しています。
| 機能 | BbcodeRewriter (#43187) |
トークンベースAPI (#42932) | 意味と重要性 |
|---|---|---|---|
| 1. DiscourseのBBCodeパーサーの使用 | Markdownエンジンで使用されているのと同じ開始タグ文法である parseBBCodeTag を使用して、候補となる開始タグをスキャンします。 |
DiscourseMarkdownIt.parse を使用してMarkdownトークンを取得し、一致するBBCodeブロックを探します。 |
両方のアプローチはBBCode構文を理解しており、単一の独立した正規表現だけに依存していません。対象とするタグの特定方法が異なります。 |
| 2. 対象BBCodeブロックの選択 | 候補となる開始タグを一時的にマークし、投稿をレンダリングして、選択されたHTML要素(例: div.discourse-post-event)に対応する候補を特定します。 |
meta.bbcode が要求されたタグ名と一致する最初のパース済みBBCodeトークンを選択します。 |
対象の同一性は重要です。 #43187 では、生のBBCodeをDiscourseが実際にレンダリングしたイベントに関連付ける必要があります。#42932 のワークフローは、最初の一致するパース済みイベントブロックを使用します。 |
| 3. 欠落または曖昧な対象の処理 | 対象を確実に特定できない場合、TargetNotFound、AmbiguousTarget、または UnsupportedRenderer を送出します。 |
一致するパース済みブロックが存在しない場合は null を返し、それ以外の場合は最初の一致を選択します。 |
失敗時の挙動が異なります。 #43187 は意図的に曖昧な自動編集を拒否します。呼び出し側が関連するブロックが1つだけであると期待する場合、最初のパース済みブロックを選択することは十分かもしれません。 |
| 4. 生テキストからレンダリング結果へのマッピングの検証 | マークされたコンテンツをレンダリングし、意図したHTML要素上のマーカーを確認し、マーカーの挿入と削除によってレンダリングされたHTMLが変更されないことを確認します。 | Markdownパーサーのトークンメタデータを使用して、追加のレンダリングHTML同一性チェックなしに開始タグを特定します。 | これは間違ったレンダリング対象を編集することに対するガードです。 バックグラウンドジョブが既存のイベントを自動的にアーカイブする場合に特に重要ですが、追加のレンダリングと複雑さを伴います。 |
| 5. 選択された開始タグの確認 | 書き換え前に、提供された生テキストを再スキャンし、選択された候補がまだ予想される開始タグのテキストと一致していることを確認します。 | 更新呼び出し中に一致する開始タグを特定し、別個の予想開始タグとの比較は行いません。 | これは内部的な整合性チェックです。 以前の選択と一致しなくなった候補を書き換えるのを避けるのに役立ちます。それ自体では、並行するデータベース編集に対する保護ではありません。 |
| 6. 属性名と既存値の処理 | パーサーの属性境界を使用して選択された属性を書き換えます。また、等価なレンダリング属性名を正規化し、Recurrence と recurrence のようなバリアントが一貫して削除できるようにします。 |
属性トークンの一致を使用して要求された名前を置換または削除し、共有BBCode属性シリアライザーを使用して値を挿入します。 | どちらも無関係な属性を保存しようとしています。 等価な名の正規化は、イベントをアーカイブする場合に特に重要です。リカレンスエイリアスを残すと、完了したイベントがリカレンスとして残り続ける可能性があるためです。 |
| 7. 書き換えられたタグの検証 | 書き換えられた開始タグを再度パースし、その属性が意図した値と一致することを確認します。無効なラウンドトリップの場合、エラーが発生します。 | 同等の最終的なパースと比較ステップなしに、直接書き換えられた開始タグを生成します。 | ラウンドトリップ検証は、属性値の変更や不正な境界などのシリアライズ問題を捕捉します。 トークンベースのアプローチはシンプルですが、この追加のランタイムチェックではなく、その書き換えロジックとテストに依存しています。 |
| 8. 無関係な投稿内容の保存 | 選択された開始タグを変更しながら、周囲の生テキスト内容を保持します。また、無関係な属性値が書き換え後も生存することを確認します。 | 選択された開始タグの部分文字列を置き換えながら、周囲の生テキスト内容及び修正が要求されていない属性を保持します。 | どちらも意図しない投稿変更を避ける必要があります。 イベント関連の編集は、周囲の議論や無関係なBBCode属性を黙って変更すべきではありません。 |
| 9. 実装コスト | 候補のスキャン、一時的なマーカー、投稿のレンダリング、HTML検査、属性の書き換えと検証が必要です。 | Markdownトークンをパースし、最初の一致する開始タグを選択して直接書き換えます。 | より強力なランタイムチェックにはコストがかかります。 追加のセーフガードをすべての呼び出し側に利用可能にするべきか、オプションにするべきか、それともアプリケーションが必要とする場所にのみ実装すべきかという問題です。 |
なぜ要件が異なるのか
#43187 において、後継者ローラーオーバーは発生が終了したときに自動的に実行されます。元のイベントは非リカレンスな発生としてアーカイブされ、次の発生のために新しいトピックが作成されます。
間違った [event] ブロックが変更されたり、書き換えられた属性が意図したイベントを表さなくなったりした場合、エラーは自動ローラーオーバー試行の間持続する可能性があります。
#42932 において、ワークフローアクションは明示的にイベントを開くまたは閉じることを要求します。現在のトークンベースの実装は、最初の一致するパース済みイベントブロックを変更します。
したがって、トークンベースのアプローチは、そのユースケースにとって必ずしも不適切とは限りません。単に異なる選択と検証の保証を提供しているだけです。
両方のアプローチは、parseBBCodeTag や serializeBBCodeAttr など、一部の基盤インフラストラクチャをすでに共有しています。
共有抽象化のための可能な方向性
1つの可能性としては、3つの責任を分離することです:
-
BBCode開始タグの特定 — パーサートークン、明示的な候補選択、またはレンダリングされた要素のマッピングを使用します。
-
その属性の書き換え — 属性境界、シリアライズ、および無関係な値の保存のために、1つの共有実装を使用します。
-
結果の検証 — 呼び出し側に応じて、対象の同一性の強制、曖昧さの検出、レンダリング内容の同等性、または属性のラウンドトリップチェックを任意で適用します。
これにより、よりシンプルなワークフローは素朴なトークン選択を使用できる一方、より機微な自動編集操作はより強力なセーフガードを要求できるようになります。
しかし、2つの小さなAPIを維持することと比較して、不要な複雑さを導入する可能性もあります。
メンテナーへの質問
異なる対象選択要件を考慮すると、2つのPRがそれぞれ個別の書き換えAPIを保持することが望ましいでしょうか?
あるいは、設定可能な対象選択と検証を備えた共有の低レベル属性書き換え器の方が、長期的にはより良い方向性でしょうか?
特に、BbcodeRewriter で現在実装されている追加の安全性チェックが、一般的に有用なコア機能と見なされるべきか、それともより強い保証が必要なユースケースに固有のものであるべきかについて関心があります。
どちらかのPRが他方をブロックする必要があると提案しているわけではありません。両方の機能が発展するにつれて、DiscourseのコアBBCodeインフラストラクチャにおける不要な重複を避けることが目的です。