Class: Uniword::Assembly::DocumentAssembler

Inherits:
Object
  • Object
show all
Defined in:
lib/uniword/assembly/document_assembler.rb

Overview

Main orchestrator for document assembly operations.

Responsibility: Coordinate document assembly from components. Single Responsibility: Only orchestrates, delegates to specialized classes.

The DocumentAssembler:

  • Loads and validates assembly manifests

  • Coordinates component loading and merging

  • Applies variable substitution

  • Generates table of contents

  • Resolves cross-references

  • Produces final assembled document

Architecture:

  • DocumentAssembler (orchestrator) - this class

  • AssemblyManifest (manifest parsing)

  • ComponentRegistry (component management)

  • VariableSubstitutor (variable replacement)

  • Toc (TOC creation)

  • CrossReferenceResolver (reference resolution)

Examples:

Basic usage

assembler = DocumentAssembler.new(
  components_dir: 'components/'
)
doc = assembler.assemble('assembly.yml')
doc.save('output.docx')

With variable overrides

doc = assembler.assemble('assembly.yml',
  variables: { version: '2.0' }
)

Constant Summary collapse

TOC_COMPONENT =

Special component marker for TOC

"__toc__"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(components_dir:, cache_components: true) ⇒ DocumentAssembler

Initialize document assembler.

Examples:

Create assembler

assembler = DocumentAssembler.new(
  components_dir: 'document_components/'
)

Disable caching

assembler = DocumentAssembler.new(
  components_dir: 'components/',
  cache_components: false
)

Parameters:

  • components_dir (String)

    Path to components directory

  • cache_components (Boolean) (defaults to: true)

    Enable component caching



64
65
66
67
68
69
70
# File 'lib/uniword/assembly/document_assembler.rb', line 64

def initialize(components_dir:, cache_components: true)
  @components_dir = components_dir
  @registry = ComponentRegistry.new(
    components_dir,
    cache_enabled: cache_components,
  )
end

Instance Attribute Details

#components_dirString (readonly)

Returns Components directory path.

Returns:

  • (String)

    Components directory path



44
45
46
# File 'lib/uniword/assembly/document_assembler.rb', line 44

def components_dir
  @components_dir
end

#registryComponentRegistry (readonly)

Returns Component registry.

Returns:



41
42
43
# File 'lib/uniword/assembly/document_assembler.rb', line 41

def registry
  @registry
end

Instance Method Details

#assemble(manifest_path, variables: {}) ⇒ Document

Assemble document from manifest.

Examples:

Assemble document

doc = assembler.assemble('assembly.yml')

With variables

doc = assembler.assemble('assembly.yml',
  variables: { custom_var: "Value" }
)

Parameters:

  • manifest_path (String)

    Path to assembly.yml file

  • variables (Hash) (defaults to: {})

    Additional/override variables

Returns:

  • (Document)

    Assembled document



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/uniword/assembly/document_assembler.rb', line 85

def assemble(manifest_path, variables: {})
  # Load manifest
  manifest = AssemblyManifest.new(
    manifest_path,
    override_variables: variables,
  )

  # Create new document
  document = Wordprocessingml::DocumentRoot.new

  # Initialize processors
  substitutor = VariableSubstitutor.new(manifest.variables)
  resolver = CrossReferenceResolver.new

  # Process sections
  manifest.section_list.each do |section|
    process_section(document, section, substitutor, resolver)
  end

  # Resolve cross-references
  resolver.resolve(document)

  document
end

#assemble_and_save(manifest_path, output_path: nil, variables: {}) ⇒ String

Assemble and save document.

Examples:

Assemble and save

path = assembler.assemble_and_save('assembly.yml')

Custom output

path = assembler.assemble_and_save('assembly.yml',
  output_path: 'custom.docx'
)

Parameters:

  • manifest_path (String)

    Path to assembly.yml file

  • output_path (String, nil) (defaults to: nil)

    Output file path (from manifest if nil)

  • variables (Hash) (defaults to: {})

    Additional/override variables

Returns:

  • (String)

    Output file path



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/uniword/assembly/document_assembler.rb', line 124

def assemble_and_save(manifest_path, output_path: nil, variables: {})
  # Load manifest to get output path
  manifest = AssemblyManifest.new(
    manifest_path,
    override_variables: variables,
  )

  # Determine output path
  output = output_path || manifest.output_path

  # Assemble document
  document = assemble(manifest_path, variables: variables)

  # Save document
  document.save(output)

  output
end

#cache_statsHash

Get cache statistics.

Returns:

  • (Hash)

    Cache statistics



191
192
193
# File 'lib/uniword/assembly/document_assembler.rb', line 191

def cache_stats
  @registry.cache_stats
end

#clear_cachevoid

This method returns an undefined value.

Clear component cache.



184
185
186
# File 'lib/uniword/assembly/document_assembler.rb', line 184

def clear_cache
  @registry.clear_cache
end

#preview(manifest_path) ⇒ Hash

Preview assembly (dry run).

Examples:

Preview assembly

info = assembler.preview('assembly.yml')
puts "Components: #{info[:component_count]}"

Parameters:

  • manifest_path (String)

    Path to assembly.yml file

Returns:

  • (Hash)

    Assembly preview information



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/uniword/assembly/document_assembler.rb', line 151

def preview(manifest_path)
  manifest = AssemblyManifest.new(manifest_path)

  components = []
  manifest.section_list.each do |section|
    if section["component"] == TOC_COMPONENT
      components << { type: :toc, options: section["options"] }
    elsif section["component"].include?("*")
      # Resolve wildcard
      resolved = @registry.resolve(
        section["component"],
        order: section["order"],
      )
      resolved.each do |comp|
        components << { type: :component, name: comp[:name] }
      end
    else
      components << { type: :component, name: section["component"] }
    end
  end

  {
    output_path: manifest.output_path,
    template: manifest.template_name,
    variables: manifest.variables,
    component_count: components.size,
    components: components,
  }
end