JA Development Guides Code Styles - hiraishikentaro/rails-factorybot-jump GitHub Wiki

開発ガイド: コードスタイル

コードスタイル概要

Rails FactoryBot Jump は、コードの可読性、保守性、チーム協力を維持するために一貫した TypeScript コーディング規約に従っています。プロジェクトでは自動化されたコード品質実行のために ESLint を使用しています。

TypeScript スタイルガイドライン

1. 命名規約

クラス: PascalCase

// 良い例
class FactoryLinkProvider implements vscode.DocumentLinkProvider {
  // ...
}

class CacheManager {
  // ...
}

// 避けるべき例
class factoryLinkProvider {
  // ...
}

メソッドと変数: camelCase

// 良い例
private factoryCache: Map<string, FactoryDefinition>;
private async initializeFactoryFiles(): Promise<void> {
  // ...
}

// 避けるべき例
private factory_cache: Map<string, FactoryDefinition>;
private async initialize_factory_files(): Promise<void> {
  // ...
}

定数: SCREAMING_SNAKE_CASE

// 良い例
const DEFAULT_FACTORY_PATH = "spec/factories/**/*.rb";
const MAX_CACHE_SIZE = 1000;

// 避けるべき例
const defaultFactoryPath = "spec/factories/**/*.rb";

インターフェース: 説明的な名前で PascalCase

// 良い例
interface FactoryDefinition {
  uri: vscode.Uri;
  lineNumber: number;
}

interface TraitDefinition extends FactoryDefinition {
  factory: string;
}

// 避けるべき例
interface IFactoryDefinition {
  // ハンガリアン記法は使用しない
}

ソース: src/providers/factoryLinkProvider.ts

2. 関数宣言スタイル

非同期関数: 常に明示的に async を宣言

// 良い例
async initializeFactoryFiles(): Promise<void> {
  await this.buildCache();
}

// アロー関数の良い例
const processFile = async (uri: vscode.Uri): Promise<void> => {
  await vscode.workspace.openTextDocument(uri);
};

// 避けるべき例
initializeFactoryFiles() {
  return this.buildCache(); // 暗黙的なPromise返却
}

メソッド可視性: アクセス修飾子を明示

// 良い例
export class FactoryLinkProvider {
  public async initializeFactoryFiles(): Promise<void> {
    // パブリックインターフェース
  }

  private async cacheFactoryDefinitions(): Promise<void> {
    // 内部実装
  }

  protected getConfiguration(): vscode.WorkspaceConfiguration {
    // 継承可能性のため
  }
}

3. 型注釈

明示的な戻り値型: パブリックメソッドは常に戻り値型を指定

// 良い例
provideDocumentLinks(document: vscode.TextDocument): vscode.DocumentLink[] {
  return this.generateLinks(document);
}

// 複雑な型の良い例
private buildFactoryCache(): Map<string, FactoryDefinition> {
  const cache = new Map<string, FactoryDefinition>();
  return cache;
}

// 避けるべき例
provideDocumentLinks(document: vscode.TextDocument) {
  return this.generateLinks(document); // 推論される戻り値型
}

パラメータ型: 常にパラメータ型を指定

// 良い例
private createDocumentLink(
  range: vscode.Range,
  factoryName: string,
  lineNumber: number
): vscode.DocumentLink {
  // 実装
}

// 避けるべき例
private createDocumentLink(range, factoryName, lineNumber) {
  // 型情報なし
}

コード組織

1. インポート組織

インポート順序:

// 1. Node.js組み込みモジュール
import * as path from "path";
import * as fs from "fs";

// 2. 外部依存関係
import * as vscode from "vscode";

// 3. 内部モジュール(相対インポート)
import { FactoryDefinition } from "./types";
import { ConfigurationManager } from "../utils/config";

インポートスタイル:

// 良い例: 特定インポート
import { Uri, TextDocument, DocumentLink } from "vscode";

// 良い例: 多くの項目の名前空間インポート
import * as vscode from "vscode";

// 避けるべき例: 特定と名前空間の混合
import vscode, { Uri, TextDocument } from "vscode";

2. クラス構造

一貫したクラス組織:

export class FactoryLinkProvider implements vscode.DocumentLinkProvider {
  // 1. プライベートフィールド
  private factoryCache: Map<string, FactoryDefinition> = new Map();
  private traitCache: Map<string, TraitDefinition> = new Map();
  private isInitialized = false;

  // 2. コンストラクタ
  constructor() {
    this.initializeFactoryFiles();
  }

  // 3. パブリックインターフェースメソッド
  public async provideDocumentLinks(
    document: vscode.TextDocument
  ): Promise<vscode.DocumentLink[]> {
    // 実装
  }

  // 4. パブリックユーティリティメソッド
  public async findFactoryFile(
    factoryName: string
  ): Promise<vscode.Uri | undefined> {
    // 実装
  }

  // 5. プライベート実装メソッド
  private async initializeFactoryFiles(): Promise<void> {
    // 実装
  }

  private async cacheFactoryDefinitions(): Promise<void> {
    // 実装
  }
}

3. メソッド組織

メソッドグループ化: 関連メソッドをまとめる

// キャッシュ管理メソッド
private async initializeFactoryFiles(): Promise<void> {}
private async cacheFactoryDefinitions(): Promise<void> {}
private async cacheTraitDefinitions(): Promise<void> {}

// リンク生成メソッド
private generateFactoryLinks(): vscode.DocumentLink[] {}
private generateTraitLinks(): vscode.DocumentLink[] {}
private createDocumentLink(): vscode.DocumentLink {}

// ユーティリティメソッド
private getConfiguration(): vscode.WorkspaceConfiguration {}
private normalizeFactoryPath(): string {}

エラーハンドリングスタイル

1. エラー伝播

優雅なエラーハンドリング:

// 良い例: 優雅な機能低下
async initializeFactoryFiles(): Promise<void> {
  try {
    const factoryFiles = await this.discoverFactoryFiles();
    await this.processFactoryFiles(factoryFiles);
  } catch (error) {
    console.warn("ファクトリ初期化に失敗、空のキャッシュで継続:", error);
    // 完全失敗ではなく空のキャッシュで継続
  }
}

// 良い例: 特定のエラーハンドリング
private async processFactoryFile(uri: vscode.Uri): Promise<void> {
  try {
    const document = await vscode.workspace.openTextDocument(uri);
    this.parseFactoryDefinitions(document);
  } catch (error) {
    console.warn(`ファクトリファイル${uri.path}の処理に失敗:`, error);
    // このファイルをスキップして他のファイルを継続
  }
}

2. エラーログ

一貫したエラーログ:

// 良い例: コンテキスト豊富なエラーメッセージ
try {
  await this.operation();
} catch (error) {
  console.error("FactoryLinkProvider: ファクトリファイル初期化に失敗", {
    error: error.message,
    stack: error.stack,
    context: "initializeFactoryFiles",
  });
}

// 良い例: ユーザーフレンドリーフォールバック
provideDocumentLinks(document: vscode.TextDocument): vscode.DocumentLink[] {
  try {
    return this.generateLinks(document);
  } catch (error) {
    console.error("ドキュメントリンク生成に失敗:", error);
    return []; // 例外投げずに空配列を返す
  }
}

ドキュメンテーションスタイル

1. コードコメント

JSDoc コメント: パブリック API に対して

/**
 * Rubyテストファイルのファクトリコールにドキュメントリンクを提供します。
 *
 * @param document 分析するテキストドキュメント
 * @returns クリック可能なドキュメントリンクの配列
 */
public async provideDocumentLinks(
  document: vscode.TextDocument
): Promise<vscode.DocumentLink[]> {
  // 実装
}

/**
 * 指定されたファクトリ定義を含むファクトリファイルを検索します。
 *
 * @param factoryName 検索するファクトリの名前
 * @returns ファクトリファイルのURI、見つからない場合はundefined
 */
public async findFactoryFile(factoryName: string): Promise<vscode.Uri | undefined> {
  // 実装
}

インラインコメント: 複雑なロジックに対して

// O(1)検索パフォーマンスのためのファクトリ定義キャッシュ
private factoryCache: Map<string, FactoryDefinition> = new Map();

// 様々な構文パターンでファクトリコールを検出する正規表現
const factoryCallPattern = /(?:create|build|create_list|build_list|build_stubbed|build_stubbed_list)\s*(?:\(\s*)?((:[a-zA-Z0-9_]+)(?:\s*,\s*(:[a-zA-Z0-9_]+))*)/g;

// マッチ位置の前の改行をカウントして行番号を計算
const lines = text.substring(0, match.index).split("\n");
const lineNumber = lines.length - 1;

2. TODO コメント

構造化された TODO 形式:

// TODO(priority): 説明
// TODO(high): ファクトリ継承のサポートを実装
// TODO(medium): 正規表現コンパイルのキャッシュを追加
// TODO(low): カスタムファクトリメソッドのサポートを検討

// FIXME: 拡張機能実行中にファクトリファイルが削除された場合のエッジケースを処理
// HACK: VSCode API制限の一時的な回避策

テストコードスタイル

1. テスト組織

説明的なテスト名:

suite("ファクトリ検出", () => {
  test("括弧付きの基本ファクトリコールを検出する", () => {
    // テスト実装
  });

  test("括弧なしのファクトリコールを検出する", () => {
    // テスト実装
  });

  test("複数トレイト付きのファクトリコールを処理する", () => {
    // テスト実装
  });

  test("コメント内のファクトリコールを無視する", () => {
    // テスト実装
  });
});

一貫したテスト構造:

test("特定の動作を実行する", async () => {
  // Arrange: テストデータを設定
  const mockDocument = createMockDocument("user = create(:user)");
  const provider = new FactoryLinkProvider();

  // Act: 動作を実行
  const links = await provider.provideDocumentLinks(mockDocument);

  // Assert: 期待される結果を検証
  assert.strictEqual(links.length, 1);
  assert.ok(links[0].target?.toString().includes("gotoLine"));
});

2. モックとスタブスタイル

明確なモック設定:

setup(() => {
  // 各テストで新しいモックを作成
  mockWorkspace = sinon.stub(vscode.workspace, "findFiles");
  mockConfiguration = sinon.stub(vscode.workspace, "getConfiguration");

  // デフォルト動作を設定
  mockWorkspace.resolves([]);
  mockConfiguration.returns({
    get: sinon
      .stub()
      .withArgs("factoryPaths")
      .returns(["spec/factories/**/*.rb"]),
  });
});

teardown(() => {
  // すべてのモックをクリーンアップ
  sinon.restore();
});

ESLint 設定

1. ルール強制

TypeScript ESLint ルール.eslintrc.json):

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module"
  },
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/no-unused-vars": "error",
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "warn",
    "@typescript-eslint/prefer-const": "error",
    "prefer-arrow-callback": "error",
    "no-var": "error"
  }
}

ソース: package.json#L82-L83

2. コード品質コマンド

リンティングコマンド:

# リンティング実行
npm run lint

# 自動修正可能な問題を修正
npm run lint -- --fix

# 特定ファイルをリント
npm run lint src/providers/factoryLinkProvider.ts

ベストプラクティス要約

1. 一貫性ガイドライン

  • コードベース全体で一貫した命名規約を使用
  • 一貫したインデンテーション(2 スペース)を維持
  • 確立されたインポート順序に従う
  • パブリック API に明示的な型を使用
  • ユーザー体験を壊すことなくエラーを優雅に処理

2. パフォーマンス考慮事項

  • 適切なデータ構造を使用(O(1)検索のための Map)
  • 高価な操作をキャッシュ
  • 正規表現の再コンパイルを最小化
  • 適切な場所で遅延初期化を使用

3. 保守性

  • メソッドを集中し単一目的に保つ
  • 説明的な変数と関数名を使用
  • 複雑なビジネスロジックにコメントを追加
  • 新機能の包括的なテストを記述

ソース: src/providers/factoryLinkProvider.ts

これらのコードスタイルガイドラインに従うことで、Rails FactoryBot Jump 拡張機能がすべての貢献において高いコード品質、可読性、一貫性を維持することが保証されます。

⚠️ **GitHub.com Fallback** ⚠️