Architecture Module Structure - hiraishikentaro/rails-factorybot-jump GitHub Wiki
The Rails FactoryBot Jump extension follows a modular architecture with clear separation of concerns and well-defined module boundaries.
src/
├── extension.ts # Extension entry point and lifecycle
├── providers/
│ └── factoryLinkProvider.ts # Document link provider implementation
├── models/ # Data models
│ ├── factory.ts # Factory definition model
│ ├── location.ts # Location tracking model
│ └── trait.ts # Trait definition model
├── services/ # Business services
│ ├── cacheManager.ts # Multi-level cache management
│ ├── configurationManager.ts # Configuration handling
│ ├── errorNotificationService.ts # Error handling and notifications
│ ├── factoryParser.ts # Factory file parsing logic
│ └── fileSearcher.ts # File discovery and watching
├── utils/ # Utility functions
│ ├── pathUtils.ts # Path manipulation utilities
│ └── regexPatterns.ts # Regex pattern definitions
├── constants/
│ └── defaults.ts # Default configuration values
└── test/
├── runTest.ts # Integration test runner
├── runUnitTests.ts # Unit test runner
├── suite/
│ ├── index.ts # Integration test setup
│ └── extension.test.ts # Integration test cases
└── unit/
├── index.ts # Unit test setup
├── models/ # Model unit tests
├── services/ # Service unit tests
└── utils/ # Utility unit tests
Purpose: Main extension lifecycle management and VSCode integration.
Key Responsibilities:
- Extension activation and deactivation
- Provider and command registration
- File system watcher setup
- Resource management
Public Interface:
export function activate(context: vscode.ExtensionContext): void;
export function deactivate(): void;
Dependencies:
-
vscode
- VSCode Extension API -
./providers/factoryLinkProvider
- Core business logic
Internal Structure:
function activate(context: vscode.ExtensionContext) {
// Provider registration
const provider = new FactoryLinkProvider();
context.subscriptions.push(
vscode.languages.registerDocumentLinkProvider(
{ scheme: "file", language: "ruby" },
provider
)
);
// Command registration
context.subscriptions.push(
vscode.commands.registerCommand(
"rails-factorybot-jump.gotoLine",
gotoLineHandler
)
);
// File system watcher
const watcher = vscode.workspace.createFileSystemWatcher(
"**/factories/**/*.rb"
);
// ... watcher event handlers
}
Source: src/extension.ts#L4-L42
Purpose: Core business logic for factory detection and link generation.
Key Responsibilities:
- Factory and trait detection
- Cache management
- Document link generation
- Configuration handling
Public Interface:
export class FactoryLinkProvider implements vscode.DocumentLinkProvider {
provideDocumentLinks(document: vscode.TextDocument): vscode.DocumentLink[];
initializeFactoryFiles(): Promise<void>;
}
Internal Architecture:
class FactoryLinkProvider {
// Cache structures
private factoryCache: Map<string, { uri: vscode.Uri; lineNumber: number }>;
private traitCache: Map<
string,
{ uri: vscode.Uri; lineNumber: number; factory: string }
>;
private factoryFiles: vscode.Uri[];
// Core methods
private async initializeFactoryFiles();
private async cacheFactoryDefinitions();
private async cacheTraitDefinitions();
private createDocumentLink();
}
Dependencies:
-
vscode
- VSCode APIs for file operations and link generation -
path
- Path manipulation utilities
Source: src/providers/factoryLinkProvider.ts
graph TD
A[extension.ts] --> B[providers/factoryLinkProvider.ts]
A --> C[vscode API]
B --> D[services/cacheManager.ts]
B --> E[services/factoryParser.ts]
B --> F[services/fileSearcher.ts]
B --> G[services/configurationManager.ts]
B --> H[services/errorNotificationService.ts]
D --> I[models/factory.ts]
D --> J[models/trait.ts]
D --> K[models/location.ts]
E --> I
E --> J
E --> L[utils/regexPatterns.ts]
F --> M[utils/pathUtils.ts]
G --> N[constants/defaults.ts]
O[test/runTest.ts] --> P[test/suite/extension.test.ts]
Q[test/runUnitTests.ts] --> R[test/unit/**/*.test.ts]
P --> A
P --> S[vscode/test-electron]
P --> T[mocha]
R --> U[sinon]
VSCode Extension API:
vscode.languages.registerDocumentLinkProvider
vscode.commands.registerCommand
vscode.workspace.createFileSystemWatcher
vscode.workspace.findFiles
vscode.workspace.openTextDocument
Node.js Core Modules:
-
path
- Cross-platform path operations
Development Dependencies:
-
@vscode/test-electron
- Extension testing framework -
mocha
- Test runner -
sinon
- Mocking framework
Source: package.json#L77-L90
sequenceDiagram
participant Main as extension.ts
participant Provider as factoryLinkProvider.ts
participant VSCode as VSCode APIs
participant FS as File System
Main->>Provider: new FactoryLinkProvider()
Main->>VSCode: registerDocumentLinkProvider(provider)
Main->>VSCode: createFileSystemWatcher()
Note over Provider: Lazy initialization on first use
VSCode->>Provider: provideDocumentLinks()
Provider->>VSCode: workspace.findFiles()
VSCode->>FS: Find factory files
FS-->>VSCode: File URIs
VSCode-->>Provider: Factory file list
Provider->>Provider: Build caches
graph LR
A[User Action] --> B[VSCode Core]
B --> C[extension.ts]
C --> D[factoryLinkProvider.ts]
D --> E[Factory Cache]
D --> F[Trait Cache]
E --> G[Document Links]
F --> G
G --> B
B --> H[UI Display]
Implementation:
-
FactoryLinkProvider
implementsvscode.DocumentLinkProvider
- Clean separation between VSCode integration and business logic
- Single responsibility for link generation
Benefits:
- Testable business logic
- VSCode API abstraction
- Swappable implementations
Implementation:
-
extension.ts
acts as facade to VSCode APIs - Simplifies interaction with complex VSCode subsystems
- Provides unified interface for extension functionality
Benefits:
- Simplified client interface
- Centralized configuration
- Easier testing and mocking
Implementation:
- Cache maps act as repositories for factory definitions
- Abstract data access from business logic
- Consistent data access interface
Benefits:
- Centralized data management
- Performance optimization
- Testable data layer
graph TB
A[VSCode Settings] --> B[extension.ts]
B --> C[Configuration Change Event]
C --> D[factoryLinkProvider.ts]
D --> E[Cache Invalidation]
E --> F[Factory File Re-scan]
F --> G[Cache Rebuild]
Extension Manifest (package.json
):
{
"contributes": {
"configuration": {
"properties": {
"rails-factorybot-jump.factoryPaths": {
"type": "array",
"default": ["spec/factories/**/*.rb"]
}
}
}
}
}
Runtime Access:
const config = vscode.workspace.getConfiguration("rails-factorybot-jump");
const factoryPaths = config.get<string[]>("factoryPaths", [
"spec/factories/**/*.rb",
]);
Source: package.json#L52-L66
test/
├── runTest.ts # Test runner entry point
└── suite/
├── index.ts # Mocha configuration
└── extension.test.ts # Test cases
Test Runner (runTest.ts
):
- Configures VSCode test environment
- Sets up extension development host
- Manages test execution lifecycle
Test Suite (extension.test.ts
):
- Unit tests for core functionality
- Integration tests with VSCode APIs
- Mock factory files and configurations
Test Infrastructure:
// Test module imports
import * as vscode from "vscode";
import * as assert from "assert";
import * as sinon from "sinon";
import { FactoryLinkProvider } from "../../providers/factoryLinkProvider";
Source: src/test/suite/extension.test.ts
Extension Interface:
// Main extension exports
export function activate(context: vscode.ExtensionContext): void;
export function deactivate(): void;
Provider Interface:
// VSCode DocumentLinkProvider contract
interface DocumentLinkProvider {
provideDocumentLinks(document: TextDocument): DocumentLink[];
}
Cache Interfaces:
interface FactoryDefinition {
uri: vscode.Uri;
lineNumber: number;
}
interface TraitDefinition extends FactoryDefinition {
factory: string;
}
Configuration Interface:
interface ExtensionConfig {
factoryPaths: string[];
}
graph TB
A[File System Error] --> B[factoryLinkProvider.ts]
B --> C[Error Logging]
B --> D[Graceful Degradation]
D --> E[Partial Functionality]
F[VSCode API Error] --> G[extension.ts]
G --> H[Error Logging]
G --> I[Resource Cleanup]
Module-Level Error Handling:
- Each module handles its own errors
- Errors don't propagate to break other modules
- Graceful degradation strategies
Cross-Module Error Communication:
- Logging for debugging
- Status indicators for user feedback
- Fallback behaviors
Current Architecture:
The extension has evolved from a simple two-file structure to a comprehensive service-oriented architecture:
- Models Layer: Data models with proper encapsulation and type safety
- Services Layer: Business logic separation with dedicated services for caching, parsing, configuration, and error handling
- Utils Layer: Shared utilities for common operations
- Constants Layer: Centralized configuration defaults
Future Expansion Possibilities:
src/
└── language-support/ # Multi-language support modules (planned)
Plugin Architecture:
- Provider interface for different language support
- Configurable parsing strategies
- Pluggable cache implementations
Configuration Extension:
- Module-specific configuration sections
- Dynamic module loading
- Feature flags for experimental modules
Lazy Loading Strategy:
- Core modules loaded on activation
- Feature modules loaded on first use
- Test modules only in test environment
Memory Management:
- Module-specific resource cleanup
- Shared resource pooling
- Cache size limitations
Efficient Data Passing:
- Minimal data copying between modules
- Shared data structures where appropriate
- Event-driven updates to minimize polling
This modular structure provides a solid foundation for maintainability, testability, and future extensibility while keeping the codebase organized and easy to understand.