JA Architecture Data Flow - hiraishikentaro/rails-factorybot-jump GitHub Wiki

アーキテクチャ: データフロー

データフロー概要

Rails FactoryBot Jump は、初期化、リンク生成、キャッシュ更新、ユーザーナビゲーションという複数の異なるフローを通してデータを処理します。各フローはパフォーマンスと信頼性のために最適化されています。

graph TB
    subgraph "データソース"
        A[ファクトリファイル]
        B[テストファイル]
        C[設定]
    end

    subgraph "処理レイヤー"
        D[ファイルスキャナー]
        E[パターン検出器]
        F[キャッシュビルダー]
    end

    subgraph "データストレージ"
        G[ファクトリキャッシュ]
        H[トレイトキャッシュ]
        I[ファイルURIキャッシュ]
    end

    subgraph "出力レイヤー"
        J[ドキュメントリンク]
        K[ナビゲーションコマンド]
        L[ユーザーインターフェース]
    end

    A --> D
    B --> E
    C --> D
    D --> F
    E --> J
    F --> G
    F --> H
    F --> I
    G --> J
    H --> J
    J --> K
    K --> L

主要なデータフロー

1. 拡張機能初期化フロー

トリガー: VSCode が Ruby ファイルを初めて開いたとき

sequenceDiagram
    participant User as ユーザー
    participant VSCode
    participant Extension as 拡張機能
    participant Provider as プロバイダー
    participant FileSystem as ファイルシステム
    participant Cache as キャッシュ

    User->>VSCode: Rubyファイルを開く
    VSCode->>Extension: activate()
    Extension->>Provider: new FactoryLinkProvider()
    Extension->>VSCode: registerDocumentLinkProvider()
    Extension->>FileSystem: createFileSystemWatcher()
    Note over Provider: 遅延初期化
    VSCode->>Provider: provideDocumentLinks()
    Provider->>Cache: 初期化済みかチェック
    Cache-->>Provider: 未初期化
    Provider->>FileSystem: getConfiguration()
    FileSystem-->>Provider: factoryPaths
    Provider->>FileSystem: findFiles(factoryPaths)
    FileSystem-->>Provider: ファクトリファイルURI
    Provider->>Cache: buildFactoryCache()
    Provider->>Cache: buildTraitCache()
    Cache-->>Provider: キャッシュ準備完了
    Provider-->>VSCode: DocumentLink[]

データ変換:

  1. 設定 → ファクトリファイルパス(glob パターン)
  2. ファイルパス → ファイル URI(VSCode workspace API)
  3. ファイル URI → ファイル内容(テキスト読み込み)
  4. ファイル内容 → ファクトリ定義(正規表現解析)
  5. ファクトリ定義 → キャッシュエントリ(Map 構造)

ソース: src/providers/factoryLinkProvider.ts

2. ファクトリキャッシュ構築フロー

入力: ファクトリファイル URI
出力: ファクトリとトレイトキャッシュ

graph LR
    A[ファクトリファイルURI] --> B[ファイル内容読み込み]
    B --> C[ファクトリ定義抽出]
    C --> D[ファクトリ名解析]
    D --> E[行番号計算]
    E --> F[ファクトリキャッシュに保存]

    B --> G[トレイト定義抽出]
    G --> H[トレイト名解析]
    H --> I[ファクトリと関連付け]
    I --> J[トレイトキャッシュに保存]

ファクトリ定義解析:

// ファクトリ検出用正規表現パターン
const factoryPattern = /factory\s+:([a-zA-Z0-9_]+)\b/g
const traitPattern = /trait\s+:([a-zA-Z0-9_]+)\s+do/g

// データ変換
factoryMatch → {
  name: string,           // ファクトリ名(例: "user")
  uri: vscode.Uri,        // ファイル場所
  lineNumber: number      // ファイル内の行
}

traitMatch → {
  name: string,           // トレイト名(例: "admin")
  factory: string,        // 親ファクトリ(例: "user")
  uri: vscode.Uri,        // ファイル場所
  lineNumber: number      // ファイル内の行
}

3. リンク生成フロー

トリガー: ユーザーがテストファイル内のファクトリ呼び出しにマウスオーバーしたとき

sequenceDiagram
    participant User as ユーザー
    participant VSCode
    participant Provider as プロバイダー
    participant PatternDetector as パターン検出器
    participant Cache as キャッシュ

    User->>VSCode: ファクトリ呼び出しにマウスオーバー
    VSCode->>Provider: provideDocumentLinks(document)
    Provider->>PatternDetector: findFactoryCalls(text)
    PatternDetector->>PatternDetector: 正規表現パターン適用
    PatternDetector-->>Provider: ファクトリ呼び出しマッチ

    loop 各ファクトリ呼び出し
        Provider->>Cache: lookup(factoryName)
        Cache-->>Provider: ファクトリ定義
        Provider->>Provider: createDocumentLink()
    end

    Provider-->>VSCode: DocumentLink[]
    VSCode-->>User: クリック可能リンクを表示

パターン検出プロセス:

// ファクトリ呼び出し検出正規表現
const factoryCallPattern = /(?:create|create_list|build|build_list|build_stubbed|build_stubbed_list)\s*(?:\(\s*)?((:[a-zA-Z0-9_]+)(?:\s*,\s*(:[a-zA-Z0-9_]+))*)/g

// データ抽出
textMatch → {
  factoryName: string,    // 例: "user"
  traits: string[],       // 例: ["admin", "verified"]
  startPos: number,       // ドキュメント内の文字位置
  endPos: number          // 終了文字位置
}

// キャッシュ検索
factoryName → {
  uri: vscode.Uri,        // ターゲットファイル
  lineNumber: number      // ターゲット行
}

// リンク生成
cacheEntry → vscode.DocumentLink {
  range: vscode.Range,    // クリック可能テキスト範囲
  target: vscode.Uri      // ナビゲーションコマンドURI
}

4. ファイル変更検出フロー

トリガー: ファクトリファイルが作成、変更、または削除されたとき

graph TB
    A[ファイルシステム変更] --> B[ファイルウォッチャーイベント]
    B --> C{イベント種別}
    C -->|作成| D[キャッシュに追加]
    C -->|変更| E[キャッシュエントリ更新]
    C -->|削除| F[キャッシュから削除]
    D --> G[影響を受けるキャッシュを再構築]
    E --> G
    F --> G
    G --> H[ドキュメントリンクを無効化]
    H --> I[VSCodeがリンクを更新]

キャッシュ更新プロセス:

// ファイル変更イベント
FileSystemEvent → {
  type: 'create' | 'change' | 'delete',
  uri: vscode.Uri
}

// キャッシュ無効化戦略
if (event.type === 'delete') {
  // このファイルのすべてのエントリを削除
  removeFileFromCaches(event.uri)
} else {
  // このファイルのエントリを再解析して更新
  reparseFactoryFile(event.uri)
}

ソース: src/extension.ts#L28-L41

5. ナビゲーションコマンドフロー

トリガー: ユーザーがファクトリリンクをクリックしたとき

sequenceDiagram
    participant User as ユーザー
    participant VSCode
    participant Extension as 拡張機能
    participant FileSystem as ファイルシステム

    User->>VSCode: ファクトリリンクをクリック
    VSCode->>Extension: executeCommand("gotoLine", args)
    Extension->>FileSystem: openTextDocument(uri)
    FileSystem-->>Extension: TextDocument
    Extension->>VSCode: showTextDocument(document)
    VSCode-->>Extension: TextEditor
    Extension->>VSCode: setSelection(lineNumber)
    Extension->>VSCode: revealRange(lineNumber)
    VSCode-->>User: ファクトリ定義にナビゲート

ナビゲーションデータフロー:

// クリックイベントデータ
DocumentLink.target → vscode.Uri {
  scheme: "command",
  path: "rails-factorybot-jump.gotoLine",
  query: JSON.stringify({
    uri: string,      // ターゲットファイルURI
    lineNumber: number // ターゲット行番号
  })
}

// コマンド実行
commandArgs → {
  uri: string,        // ターゲットファイルパス
  lineNumber: number  // 0ベースの行番号
}

// VSCode操作
uri → vscode.TextDocument → vscode.TextEditor → vscode.Selection

ソース: src/extension.ts#L14-L26

データ構造

1. キャッシュデータ構造

ファクトリキャッシュ:

Map<string, FactoryDefinition>

interface FactoryDefinition {
  uri: vscode.Uri      // ファクトリを含むファイル
  lineNumber: number   // 0ベースの行番号
}

// エントリ例
{
  "user" => { uri: "file:///spec/factories/users.rb", lineNumber: 1 },
  "post" => { uri: "file:///spec/factories/posts.rb", lineNumber: 0 }
}

トレイトキャッシュ:

Map<string, TraitDefinition>

interface TraitDefinition {
  uri: vscode.Uri      // トレイトを含むファイル
  lineNumber: number   // 0ベースの行番号
  factory: string      // 親ファクトリ名
}

// エントリ例(キー形式: "factory:trait")
{
  "user:admin" => { uri: "file:///spec/factories/users.rb", lineNumber: 5, factory: "user" },
  "post:published" => { uri: "file:///spec/factories/posts.rb", lineNumber: 8, factory: "post" }
}

2. 中間データ構造

パターンマッチ結果:

interface FactoryCallMatch {
  factoryName: string; // ':'なしのファクトリ名
  traits: string[]; // ':'なしのトレイト名
  range: vscode.Range; // ドキュメント内のテキスト範囲
  fullMatch: string; // 完全一致テキスト
}

ファイル解析結果:

interface ParsedFactory {
  name: string; // ファクトリ名
  lineNumber: number; // 定義行
  traits: ParsedTrait[]; // ファクトリ内のトレイト
}

interface ParsedTrait {
  name: string; // トレイト名
  lineNumber: number; // 定義行
}

パフォーマンス最適化

1. 遅延読み込み戦略

graph LR
    A[拡張機能アクティベーション] --> B{初回リンク要求?}
    B -->|はい| C[キャッシュ初期化]
    B -->|いいえ| D[既存キャッシュ使用]
    C --> E[ファクトリファイルスキャン]
    E --> F[キャッシュ構築]
    F --> G[リンク生成]
    D --> G

メリット:

  • 拡張機能アクティベーションの高速化
  • 未使用機能のメモリ使用量削減
  • ユーザー体験の向上

2. 増分キャッシュ更新

graph TB
    A[ファイル変更イベント] --> B{影響を受けるファイル?}
    B -->|はい| C[変更されたファイルのみ解析]
    B -->|いいえ| D[アクションなし]
    C --> E[特定のキャッシュエントリを更新]
    E --> F[影響を受けないエントリを保持]

メリット:

  • 変更時の最小限の再処理
  • キャッシュ一貫性の維持
  • CPU 使用量の削減

3. 効率的なデータアクセスパターン

O(1)キャッシュ検索:

// 直接ハッシュマップアクセス
const factory = factoryCache.get(factoryName);
const trait = traitCache.get(`${factoryName}:${traitName}`);

バッチ操作:

// ドキュメント内のすべてのファクトリ呼び出しを一度に処理
const allMatches = findAllFactoryCalls(document.getText());
const links = allMatches.map((match) => createDocumentLink(match));

データフローでのエラーハンドリング

1. ファイルシステムエラーハンドリング

graph TB
    A[ファイル操作] --> B{成功?}
    B -->|はい| C[データ処理]
    B -->|いいえ| D[エラーログ]
    D --> E[利用可能データで継続]
    C --> F[キャッシュ更新]
    E --> G[優雅な機能低下]

2. 解析エラー回復

// エラー回復機能付きの堅牢な解析
try {
  const factories = parseFactoryFile(fileContent);
  updateCache(factories);
} catch (error) {
  console.warn(`ファクトリファイルの解析に失敗: ${file.path}`, error);
  // 他のファイルで継続
}

3. キャッシュ一貫性

一貫性保証:

  • アトミックキャッシュ更新(すべてまたは何も実行しない)
  • 解析失敗時のロールバック
  • ファイル監視による最終的一貫性

データフロー監視

1. パフォーマンスメトリクス

主要メトリクス:

  • キャッシュ構築時間
  • リンク生成時間
  • ファイル解析時間
  • メモリ使用量

2. デバッグ情報

データフロートレース:

  • ファイル発見イベント
  • キャッシュヒット/ミス率
  • 解析成功/失敗率
  • リンク生成数

このデータフローアーキテクチャは、優秀なパフォーマンスとユーザー体験を提供しながら、効率的で信頼性が高く、保守可能なファクトリナビゲーションを確保します。