Module: CopyTunerClient::Copyray::Rewriter

Defined in:
lib/copy_tuner_client/copyray/rewriter.rb

Overview

完成 HTML を走査し、マーカートークンを含むテキストノードの親要素・属性値を持つ要素に data-copyray-key 属性を付与し、トークンを完全に除去する。 CopyrayMiddleware の HTML 後処理(CSS/JS 挿入)の前段で呼ばれる。

Constant Summary collapse

DATA_ATTR =
'data-copyray-key'.freeze
MAX_REWRITE_BYTESIZE =

NOTE: マーカーを含む巨大 HTML を Nokogiri で parse/traverse/再シリアライズすると サイズに比例して重くなる(実測 ~1MB で 167ms, ~3MB で 514ms)。development 限定機能で レスポンスを大きく悪化させないため、この閾値超では Nokogiri を通さず可視トークン除去のみ行う。 失うのは data-copyray-key(オーバーレイ編集導線)だけで、フォールバックの gsub は巨大ページでも数ms。

1_000_000

Class Method Summary collapse

Class Method Details

.rewrite(html) ⇒ Object

NOTE: 戻り値は [html, skipped]。skipped は data-copyray-key を付与できなかったことを表す (巨大DOMでのスキップ・Nokogiri 例外の双方で true)。ミドルウェアがこれを JS に伝え、 オーバーレイ非対応である旨をツールバーで案内する。



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/copy_tuner_client/copyray/rewriter.rb', line 22

def rewrite(html)
  # NOTE: ボディが ASCII-8BIT に転落していると UTF-8 の Marker::PREFIX との include? 比較が
  # Encoding::CompatibilityError を投げる(ミドルウェアのボディ連結で非ASCIIバイトを含む
  # ASCII-8BIT チャンクが混じると発生)。実バイト列は本来 UTF-8 なので判定用に UTF-8 とみなす。
  # String.new でエンコーディングだけ付け替える(元オブジェクトを破壊せずバッファもコピーしない)。
  scannable = html.encoding == Encoding::UTF_8 ? html : String.new(html, encoding: Encoding::UTF_8)

  # NOTE: マーカーが無ければ Copyray 無効時・マーカーの無い通常ページなので一切変形しない(高速パス)。
  # これによりマーカー非注入の HTML は完全に無傷で、Nokogiri の正規化も通らない。
  # (そもそも CopyrayMiddleware は development 限定で本番のスタックには登録されず、本番では呼ばれない。)
  # 判定は正規表現より安い部分文字列検索で行う(プレフィックスがあれば必ずマーカー候補)。
  return [html, false] unless scannable.include?(Marker::PREFIX)

  # NOTE: 閾値超は Nokogiri を通さず可視トークン除去のみ。skipped=true で編集導線を諦めた旨を伝える。
  return [strip_markers(scannable), true] if scannable.bytesize > MAX_REWRITE_BYTESIZE

  [rewrite_with_nokogiri(scannable), false]
rescue StandardError => e
  # NOTE: Copyray は開発支援機能なので、壊れた HTML 等で Nokogiri 処理が落ちても
  # ページを 500 にしない。data-copyray-key 付与(編集導線)は諦め、最低限可視トークンだけ除去する。
  warn_rewrite_failure(e)
  [strip_markers(scannable), true]
end