Date: 2025-03-10
Status
Proposed
Context
The Codexis book building system needs a consistent approach to implementing builders for different output formats. The current pseudocode shows a need for specialized builders (PDF, EPUB, MOBI, KFX, WikiJS) that share common patterns but have format-specific requirements. We need to establish a standardized builder pattern that ensures:
- Consistent interface across all format builders
- Proper handling of format-specific requirements
- Integration with the validation system
- Efficient asset management
- Error handling and recovery
- Progress reporting
Decision
We will implement a comprehensive builder pattern with the following components:
1. Base Builder Behavior
defmodule Codexis.Builder do
@callback build(content :: Content.t(), options :: Keyword.t()) :: {:ok, Output.t()} | {:error, term()}
@callback validate(output :: Output.t()) :: :ok | {:error, term()}
@callback cleanup(output :: Output.t()) :: :ok | {:error, term()}
defmacro __using__(_opts) do
quote do
@behaviour Codexis.Builder
alias Codexis.Content
alias Codexis.Output
alias Codexis.Assets
def build(content, options \\ []) do
with {:ok, validated_content} <- validate_content(content),
{:ok, processed_assets} <- process_assets(content.assets),
{:ok, output} <- do_build(validated_content, processed_assets, options),
:ok <- validate(output) do
{:ok, output}
end
end
defp validate_content(content), do: Content.validate(content)
defp process_assets(assets), do: Assets.process(assets)
defoverridable [build: 2]
end
end
end
2. Format-Specific Builders
defmodule Codexis.Builders.PDFBuilder do
use Codexis.Builder
@impl true
def do_build(content, assets, options) do
# PDF-specific build logic
end
@impl true
def validate(output) do
# PDF-specific validation
end
@impl true
def cleanup(output) do
# PDF-specific cleanup
end
end
defmodule Codexis.Builders.WikiJSBuilder do
use Codexis.Builder
@impl true
def do_build(content, assets, options) do
# WikiJS-specific build logic
end
@impl true
def validate(output) do
# WikiJS-specific validation
end
@impl true
def cleanup(output) do
# WikiJS-specific cleanup
end
end
3. Common Builder Utilities
defmodule Codexis.BuilderUtils do
def process_cover_image(image_path, format) do
# Common cover image processing logic
end
def handle_assets(assets, format) do
# Common asset handling logic
end
def apply_metadata(content, format) do
# Common metadata application logic
end
end
4. Builder Registry
defmodule Codexis.BuilderRegistry do
use GenServer
@builders %{
pdf: Codexis.Builders.PDFBuilder,
epub: Codexis.Builders.EPUBBuilder,
mobi: Codexis.Builders.MOBIBuilder,
kfx: Codexis.Builders.KFXBuilder,
wikijs: Codexis.Builders.WikiJSBuilder
}
def get_builder(format) when is_atom(format) do
Map.get(@builders, format)
end
end
5. Progress Reporting
defmodule Codexis.BuildProgress do
def report_progress(format, stage, percentage) do
:telemetry.execute(
[:codexis, :builder, :progress],
%{percentage: percentage},
%{format: format, stage: stage}
)
end
end
Consequences
Positive
- Consistency: All builders follow the same pattern and interface
- Extensibility: Easy to add new format builders
- Maintainability: Common functionality is shared through utilities
- Reliability: Built-in validation and error handling
- Observability: Integrated progress reporting and telemetry
Negative
- Complexity: More complex than simple functions
- Learning Curve: Developers need to understand the pattern
- Overhead: Some performance overhead from abstractions
Risks
- Performance Impact: Need to monitor builder abstraction overhead
- Resource Management: Must ensure proper cleanup across builders
- Error Propagation: Need to handle errors consistently
Compliance
Builders must:
- Implement all required callbacks
- Use provided utilities for common operations
- Report progress appropriately
- Handle cleanup properly
- Validate outputs before completion
Notes
- Consider adding property-based tests for builders
- Monitor performance metrics
- Document format-specific requirements
- Maintain builder compatibility matrix