Deployment Configuration - hiraishikentaro/rails-factorybot-jump GitHub Wiki

Deployment: Configuration

Configuration Overview

Rails FactoryBot Jump uses a multi-layered configuration system that includes extension manifest configuration, user settings, and runtime configuration management. This guide covers all aspects of configuring the extension for deployment and usage.

Extension Manifest Configuration

1. Package.json Configuration

Core Extension Metadata:

{
  "name": "rails-factorybot-jump",
  "displayName": "Rails FactoryBot Jump",
  "description": "Jump to FactoryBot factory definition from Rails test files",
  "version": "1.2.0",
  "icon": "images/icon.png",
  "publisher": "hir4ken",
  "engines": {
    "vscode": "^1.85.0"
  }
}

Key Configuration Elements:

  • Name: Unique identifier for the extension
  • Display Name: User-friendly name shown in VSCode
  • Version: Semantic versioning for releases
  • Engine: Minimum VSCode version requirement
  • Publisher: Marketplace publisher identity

Source: package.json#L2-L10

2. Activation Configuration

Activation Events:

{
  "activationEvents": ["onLanguage:ruby"]
}

Language Support Configuration:

{
  "contributes": {
    "documentLinkProviders": [
      {
        "scheme": "file",
        "language": "ruby"
      }
    ]
  }
}

Benefits:

  • Extension only activates when Ruby files are opened
  • Minimal resource usage for non-Ruby projects
  • Automatic cleanup when no Ruby files are open

Source: package.json#L35-L50

3. Command Configuration

Registered Commands:

{
  "contributes": {
    "commands": [
      {
        "command": "rails-factorybot-jump.jumpToFactory",
        "title": "Jump to FactoryBot Factory"
      }
    ]
  }
}

Command Integration:

// src/extension.ts
context.subscriptions.push(
  vscode.commands.registerCommand(
    "rails-factorybot-jump.gotoLine",
    async (args: { uri: string; lineNumber: number }) => {
      const uri = vscode.Uri.parse(args.uri);
      const document = await vscode.workspace.openTextDocument(uri);
      const editor = await vscode.window.showTextDocument(document);
      const position = new vscode.Position(args.lineNumber, 0);
      editor.selection = new vscode.Selection(position, position);
      editor.revealRange(new vscode.Range(position, position));
    }
  )
);

Source: package.json#L40-L45, src/extension.ts#L14-L26

User Configuration

1. Settings Schema

Configuration Properties:

{
  "contributes": {
    "configuration": {
      "title": "Rails FactoryBot Jump",
      "properties": {
        "rails-factorybot-jump.factoryPaths": {
          "type": "array",
          "default": ["spec/factories/**/*.rb"],
          "description": "Paths to search for factory files. Supports glob patterns.",
          "items": {
            "type": "string"
          }
        }
      }
    }
  }
}

Configuration Features:

  • Type Validation: Array of strings enforced by VSCode
  • Default Values: Sensible defaults for Rails projects
  • Description: User-friendly help text
  • Glob Pattern Support: Flexible file matching

Source: package.json#L52-L66

2. Configuration Access

Runtime Configuration Reading:

// src/providers/factoryLinkProvider.ts
private getFactoryPaths(): string[] {
  const config = vscode.workspace.getConfiguration("rails-factorybot-jump");
  const defaultPath = path.posix.join("spec", "factories", "**", "*.rb");
  return config.get<string[]>("factoryPaths", [defaultPath]);
}

Configuration Change Handling:

// Listen for configuration changes
vscode.workspace.onDidChangeConfiguration(async (event) => {
  if (event.affectsConfiguration("rails-factorybot-jump.factoryPaths")) {
    await this.initializeFactoryFiles();
  }
});

3. Configuration Examples

Default Configuration (minimal setup):

{
  "rails-factorybot-jump.factoryPaths": ["spec/factories/**/*.rb"]
}

Custom Configuration (multiple directories):

{
  "rails-factorybot-jump.factoryPaths": [
    "spec/factories/**/*.rb",
    "test/factories/**/*.rb",
    "lib/factories/**/*.rb"
  ]
}

Advanced Configuration (specific subdirectories):

{
  "rails-factorybot-jump.factoryPaths": [
    "spec/factories/users/**/*.rb",
    "spec/factories/posts/**/*.rb",
    "app/test_support/factories/**/*.rb"
  ]
}

Runtime Configuration

1. Dynamic Configuration Updates

Live Configuration Reloading:

class FactoryLinkProvider {
  private configurationChangeListener: vscode.Disposable;

  constructor() {
    // Set up configuration change listener
    this.configurationChangeListener =
      vscode.workspace.onDidChangeConfiguration(
        this.handleConfigurationChange.bind(this)
      );
  }

  private async handleConfigurationChange(
    event: vscode.ConfigurationChangeEvent
  ) {
    if (event.affectsConfiguration("rails-factorybot-jump")) {
      console.log("Configuration changed, reinitializing...");

      // Clear existing caches
      this.factoryCache.clear();
      this.traitCache.clear();

      // Reinitialize with new configuration
      await this.initializeFactoryFiles();
    }
  }
}

2. Configuration Validation

Path Validation:

private validateFactoryPaths(paths: string[]): string[] {
  return paths.filter(path => {
    // Validate glob pattern syntax
    if (!path || typeof path !== 'string') {
      console.warn(`Invalid factory path: ${path}`);
      return false;
    }

    // Ensure path ends with .rb
    if (!path.endsWith('.rb')) {
      console.warn(`Factory path should end with .rb: ${path}`);
      return false;
    }

    return true;
  });
}

Configuration Sanitization:

private sanitizeConfiguration(): void {
  const config = vscode.workspace.getConfiguration("rails-factorybot-jump");
  const factoryPaths = config.get<string[]>("factoryPaths", []);

  // Convert Windows paths to POSIX for consistency
  const normalizedPaths = factoryPaths.map(path =>
    path.replace(/\\/g, '/')
  );

  // Validate and filter paths
  const validPaths = this.validateFactoryPaths(normalizedPaths);

  if (validPaths.length === 0) {
    console.warn("No valid factory paths found, using default");
    this.factoryPaths = ["spec/factories/**/*.rb"];
  } else {
    this.factoryPaths = validPaths;
  }
}

Environment-Specific Configuration

1. Development Configuration

Development Settings:

{
  "rails-factorybot-jump.factoryPaths": [
    "spec/factories/**/*.rb",
    "test/fixtures/factories/**/*.rb"
  ],
  "rails-factorybot-jump.debugMode": true
}

Debug Configuration Features:

// Development-only configuration
if (process.env.NODE_ENV === "development") {
  // Enable verbose logging
  console.log("Factory discovery paths:", this.factoryPaths);
  console.log("Cache statistics:", {
    factories: this.factoryCache.size,
    traits: this.traitCache.size,
  });
}

2. Testing Configuration

Test Environment Setup:

// src/test/suite/extension.test.ts
setup(() => {
  // Mock configuration for testing
  const mockConfig = {
    get: (key: string) => {
      if (key === "factoryPaths") {
        return ["test/fixtures/factories/**/*.rb"];
      }
      return undefined;
    },
  };

  sinon.stub(vscode.workspace, "getConfiguration").returns(mockConfig as any);
});

3. Production Configuration

Optimized Production Settings:

{
  "rails-factorybot-jump.factoryPaths": ["spec/factories/**/*.rb"]
}

Performance Considerations:

  • Minimize number of search paths
  • Use specific patterns over broad wildcards
  • Avoid deeply nested directory structures

Configuration Migration

1. Version Compatibility

Backward Compatibility Strategy:

private migrateConfiguration(): void {
  const config = vscode.workspace.getConfiguration("rails-factorybot-jump");

  // Handle legacy configuration keys
  const legacyFactoryPath = config.get<string>("factoryPath");
  if (legacyFactoryPath && !config.has("factoryPaths")) {
    console.log("Migrating legacy factoryPath to factoryPaths");
    config.update("factoryPaths", [legacyFactoryPath], true);
  }
}

2. Configuration Validation

Schema Validation:

private validateConfiguration(): boolean {
  const config = vscode.workspace.getConfiguration("rails-factorybot-jump");
  const factoryPaths = config.get("factoryPaths");

  // Type validation
  if (!Array.isArray(factoryPaths)) {
    console.error("factoryPaths must be an array");
    return false;
  }

  // Content validation
  const validPaths = factoryPaths.every(path =>
    typeof path === 'string' && path.length > 0
  );

  if (!validPaths) {
    console.error("All factory paths must be non-empty strings");
    return false;
  }

  return true;
}

Workspace Configuration

1. Multi-Root Workspace Support

Workspace-Specific Configuration:

private getWorkspaceConfiguration(workspaceFolder?: vscode.WorkspaceFolder): vscode.WorkspaceConfiguration {
  return vscode.workspace.getConfiguration(
    "rails-factorybot-jump",
    workspaceFolder?.uri
  );
}

private async initializeForWorkspace(workspaceFolder: vscode.WorkspaceFolder): Promise<void> {
  const config = this.getWorkspaceConfiguration(workspaceFolder);
  const factoryPaths = config.get<string[]>("factoryPaths", []);

  // Process factory files specific to this workspace
  await this.processWorkspaceFactories(workspaceFolder, factoryPaths);
}

2. Configuration Scope

Configuration Levels:

  1. User Settings: Global configuration across all projects
  2. Workspace Settings: Project-specific configuration
  3. Folder Settings: Multi-root workspace folder configuration

Priority Order (highest to lowest):

  1. Folder settings
  2. Workspace settings
  3. User settings
  4. Default values

3. Configuration Storage

Settings.json Example:

{
  "rails-factorybot-jump.factoryPaths": [
    "spec/factories/**/*.rb",
    "test/factories/**/*.rb"
  ]
}

Workspace Configuration:

// .vscode/settings.json
{
  "rails-factorybot-jump.factoryPaths": ["custom/factories/**/*.rb"]
}

Configuration Troubleshooting

1. Common Configuration Issues

Path Resolution Problems:

private debugPathResolution(): void {
  const workspaceFolders = vscode.workspace.workspaceFolders;
  if (!workspaceFolders) {
    console.error("No workspace folders found");
    return;
  }

  console.log("Workspace folders:", workspaceFolders.map(f => f.uri.path));

  const config = this.getFactoryPaths();
  console.log("Configured factory paths:", config);

  // Test each path
  config.forEach(async (pattern) => {
    const files = await vscode.workspace.findFiles(pattern);
    console.log(`Pattern "${pattern}" found ${files.length} files`);
  });
}

2. Configuration Validation

Runtime Configuration Check:

public async validateConfiguration(): Promise<boolean> {
  const config = vscode.workspace.getConfiguration("rails-factorybot-jump");
  const factoryPaths = config.get<string[]>("factoryPaths");

  if (!factoryPaths || factoryPaths.length === 0) {
    vscode.window.showWarningMessage(
      "Rails FactoryBot Jump: No factory paths configured"
    );
    return false;
  }

  // Test if any files are found
  for (const pattern of factoryPaths) {
    const files = await vscode.workspace.findFiles(pattern, null, 1);
    if (files.length > 0) {
      return true;
    }
  }

  vscode.window.showWarningMessage(
    "Rails FactoryBot Jump: No factory files found in configured paths"
  );
  return false;
}

This comprehensive configuration system provides flexibility for various development environments while maintaining simplicity for standard Rails projects.

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