OutputPaths Migration Guide
This guide helps developers migrate from legacy path handling to the new OutputPaths system in Codexis.
Overview
As part of Task #20, Codexis has migrated to a unified path management system using the OutputPaths struct. This provides consistent, centralized path handling across all builders and core modules.
Key Changes
1. BuildOrchestrator Changes
Deprecated Functions
BuildOrchestrator.subdir/2is now deprecated
New Functions
BuildOrchestrator.create_output_paths/3BuildOrchestrator.get_output_path/5
2. OutputManager Enhancements
New Functions
OutputManager.copy_with_output_paths/3OutputManager.copy_directory_with_output_paths/3
3. OutputPaths New Utilities
New Functions
OutputPaths.ensure_directories/3OutputPaths.available_formats/1OutputPaths.has_format?/2OutputPaths.get_all_paths/2
Migration Examples
1. Basic Path Resolution
Before (Deprecated)
# Old approach using subdir
pdf_dir = BuildOrchestrator.subdir(orchestrator, "pdf")
pdf_file = Path.join(pdf_dir, "#{book_name}.pdf")
After (Recommended)
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
pdf_file = OutputPaths.get(output_paths, :pdf, :temp_file)
2. Output File Copying
Before (Legacy)
# Old approach
{:ok, final_path} = OutputManager.copy_format_output(orchestrator, "pdf", book_name)
After (Recommended)
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
{:ok, final_path} = OutputManager.copy_with_output_paths(output_paths, :pdf)
3. Directory Copying
Before (Legacy)
# Old approach - manual path construction
temp_dir = BuildOrchestrator.output_dir(orchestrator)
html_temp = Path.join(temp_dir, "html")
project_html = Path.join(project_root, "output", "html")
{:ok, _} = OutputManager.copy_directory(html_temp, project_html)
After (Recommended)
# New approach using OutputPaths
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
{:ok, final_dir} = OutputManager.copy_directory_with_output_paths(output_paths, :html)
4. Working with Multiple Formats
Before (Legacy)
# Old approach - repetitive path construction
pdf_dir = BuildOrchestrator.subdir(orchestrator, "output")
epub_dir = BuildOrchestrator.subdir(orchestrator, "output")
html_dir = BuildOrchestrator.subdir(orchestrator, "output")
pdf_file = Path.join([pdf_dir, "pdf", "#{book_name}.pdf"])
epub_file = Path.join([epub_dir, "epub", "#{book_name}.epub"])
html_dir = Path.join([html_dir, "html"])
After (Recommended)
# New approach - centralized path management
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
pdf_file = OutputPaths.get(output_paths, :pdf, :temp_file)
epub_file = OutputPaths.get(output_paths, :epub, :temp_file)
html_dir = OutputPaths.get(output_paths, :html, :temp_dir)
# Check what formats are available
available = OutputPaths.available_formats(output_paths)
# [:pdf, :html, :markdown, :wikijs, :epub, :docx, :rtf]
Builder Pattern Migration
Before (in Builders)
defmodule MyBuilder do
def do_build(orchestrator, source_dir, book_name, _options) do
# Manual path construction
output_dir = BuildOrchestrator.output_dir(orchestrator)
format_dir = Path.join(output_dir, "myformat")
File.mkdir_p!(format_dir)
temp_file = Path.join(format_dir, "#{book_name}.ext")
# Build logic...
# Manual copying
project_root = File.cwd!()
target_dir = Path.join([project_root, "output", "myformat"])
File.mkdir_p!(target_dir)
target_file = Path.join(target_dir, "#{book_name}.ext")
File.cp!(temp_file, target_file)
{:ok, target_file}
end
end
After (Recommended)
defmodule MyBuilder do
def do_build(orchestrator, source_dir, book_name, _options) do
# Use OutputPaths for consistent path management
output_paths = BuildOrchestrator.create_output_paths(orchestrator, book_name)
# Get paths using OutputPaths
temp_file = OutputPaths.get(output_paths, :myformat, :temp_file)
temp_dir = OutputPaths.get(output_paths, :myformat, :temp_dir)
# Ensure directories exist
OutputPaths.ensure_directories(output_paths, :myformat, :temp)
# Build logic...
# Copy using OutputManager with OutputPaths
case OutputManager.copy_with_output_paths(output_paths, :myformat) do
{:ok, final_path} -> {:ok, final_path}
{:error, reason} -> {:error, reason}
end
end
end
Path Types Reference
| Path Type | Description | Example |
|---|---|---|
:temp_dir |
Temporary build directory | /tmp/codexis_build_123/output/pdf |
:temp_file |
Temporary output file | /tmp/codexis_build_123/output/pdf/book.pdf |
:project_dir |
Final project directory | /project/output/pdf |
:project_file |
Final project file | /project/output/pdf/book.pdf |
:images_dir |
Images directory (HTML/Markdown) | /tmp/codexis_build_123/output/html/images |
Supported Formats
| Format | Description | File Extension |
|---|---|---|
:pdf |
PDF documents | .pdf |
:html |
HTML pages | .html |
:markdown |
Markdown files | .md |
:wikijs |
WikiJS content | .md |
:epub |
EPUB e-books | .epub |
:docx |
Word documents | .docx |
:rtf |
Rich Text Format | .rtf |
Error Handling
The new OutputPaths system provides better error handling:
# Clear error messages for missing formats
try do
OutputPaths.get(output_paths, :invalid_format, :temp_file)
rescue
ArgumentError, msg ->
# "OutputPaths: format :invalid_format not found."
end
# Clear error messages for missing path types
try do
OutputPaths.get(output_paths, :pdf, :invalid_type)
rescue
ArgumentError, msg ->
# "OutputPaths: key :invalid_type not found for format :pdf."
end
Testing Considerations
Mock Compatibility
The OutputPaths system maintains compatibility with existing mocks:
# Your existing BuildOrchestrator mocks will continue to work
# OutputPaths.new/3 accepts any struct that responds to the expected interface
Test Helpers
Consider creating test helpers for common OutputPaths operations:
defmodule TestHelpers do
def create_test_output_paths(orchestrator, book_name \\ "test_book") do
BuildOrchestrator.create_output_paths(orchestrator, book_name, "/tmp/test")
end
end
Rollback Plan
If issues arise, you can temporarily revert to legacy functions:
- Legacy functions remain available with deprecation warnings
- No breaking changes to existing builder interfaces
- Gradual migration path allows testing individual modules
Performance Benefits
The new system provides:
- Reduced Memory Usage: Single OutputPaths struct vs multiple path variables
- Better Caching: Paths computed once and reused
- Fewer I/O Operations: Centralized directory creation
- Type Safety: Compile-time format validation
Next Steps
- Update your builders: Migrate builders one at a time to use OutputPaths
- Update tests: Ensure test mocks work with the new system
- Remove deprecated calls: Search for
subdir/2usage and replace - Update documentation: Update any module docs that reference old patterns
Questions?
For questions about the migration:
- Check the updated module documentation
- Look at the examples in existing migrated builders
- Refer to the test suite for usage patterns