Deployment Configuration - hiraishikentaro/rails-factorybot-jump GitHub Wiki
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.
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
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
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
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
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();
}
});
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"
]
}
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();
}
}
}
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;
}
}
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,
});
}
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);
});
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
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);
}
}
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-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);
}
Configuration Levels:
- User Settings: Global configuration across all projects
- Workspace Settings: Project-specific configuration
- Folder Settings: Multi-root workspace folder configuration
Priority Order (highest to lowest):
- Folder settings
- Workspace settings
- User settings
- Default values
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"]
}
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`);
});
}
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.