Class: Gran::TreeTransformer

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/gran/tree_transformer.rb

Overview

A framework for transforming source file trees into destination trees through three distinct phases:

  1. SCAN - Examine source tree, collect metadata (read-only)

  2. TRANSFORM - Convert source nodes to destination nodes (core work)

  3. FINALIZE - Generate derived outputs, aggregations (post-processing)

Usage

transformer = Gran::TreeTransformer.new(
  src: "/path/to/source",
  dst: "/path/to/destination",
  transformer: MyTransformer.new,
  traversal: :preorder  # optional, default :preorder
)

# Register scanners and finalizers
transformer.add_scanner(MyScanner.new)
transformer.add_finalizer(MyFinalizer.new)

# Run all phases
transformer.run(abort_on_exc: true)

Transformer Interface

Transformers must implement:

def transform(src_node, dst_tree, context)
  # Required: perform transformation, mutate dst_tree
end

def should_transform?(src_node, context)
  # Optional: return true/false to filter nodes
  # Default: true (process all nodes)
end

Scanner Interface

Scanners must implement:

def scan(src_tree, context)
  # Examine source tree, collect metadata
  # Mutate context to share data with transform/finalize phases
end

Finalizer Interface

Finalizers must implement:

def finalize(src_tree, dst_tree, transformer, context)
  # Generate derived outputs, copy assets, etc.
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Loggable

included, #logger, #logger=

Constructor Details

#initialize(src:, dst:, transformer:, traversal: :preorder, context: {}) ⇒ TreeTransformer

Create a new TreeTransformer

Parameters:

  • src (Pathname, String, PathTree)

    Source tree - can be a filesystem path or an existing PathTree

  • dst (Pathname, String)

    Destination path where transformed output will be created

  • transformer (Object)

    Object implementing the transformer interface

  • traversal (Symbol) (defaults to: :preorder)

    Tree traversal order: :preorder (default), :postorder, or :levelorder

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

    Initial context hash for sharing data between phases



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/gran/tree_transformer.rb', line 79

def initialize(src:, dst:, transformer:, traversal: :preorder, context: {})
  @transformer = transformer
  @traversal = traversal
  @context = context

  # Build or accept source tree
  @src_tree = if src.is_a?(PathTree)
    src
  else
    src_path = Pathname.new(src).cleanpath
    raise ArgumentError, "Source path does not exist: #{src_path}" unless src_path.exist?

    PathTree.build_from_fs(src_path, prune: false)
  end

  # Create destination tree
  dst_path = Pathname.new(dst).cleanpath
  @dst_tree = PathTree.new(dst_path)

  # Store root references in context for convenience
  @context[:src_root] = @src_tree
  @context[:dst_root] = @dst_tree

  # Phase handlers
  @scanners = []
  @finalizers = []

  # Validate traversal method
  unless @src_tree.respond_to?(:"traverse_#{@traversal}")
    raise ArgumentError, "Invalid traversal order: #{@traversal}"
  end
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



64
65
66
# File 'lib/gran/tree_transformer.rb', line 64

def context
  @context
end

#dst_treeObject (readonly)

Returns the value of attribute dst_tree.



64
65
66
# File 'lib/gran/tree_transformer.rb', line 64

def dst_tree
  @dst_tree
end

#src_treeObject (readonly)

Returns the value of attribute src_tree.



64
65
66
# File 'lib/gran/tree_transformer.rb', line 64

def src_tree
  @src_tree
end

#transformerObject (readonly)

Returns the value of attribute transformer.



64
65
66
# File 'lib/gran/tree_transformer.rb', line 64

def transformer
  @transformer
end

Instance Method Details

#add_finalizer(finalizer) ⇒ Object

Add a finalizer to be run during the finalize phase

Parameters:

  • finalizer (Object)

    Object implementing the finalizer interface (must respond to #finalize)

Raises:

  • (ArgumentError)


128
129
130
131
132
# File 'lib/gran/tree_transformer.rb', line 128

def add_finalizer(finalizer)
  raise ArgumentError, "Finalizer must respond to #finalize" unless finalizer.respond_to?(:finalize)

  @finalizers << finalizer
end

#add_scanner(scanner) ⇒ Object

Add a scanner to be run during the scan phase

Parameters:

  • scanner (Object)

    Object implementing the scanner interface (must respond to #scan)

Raises:

  • (ArgumentError)


117
118
119
120
121
# File 'lib/gran/tree_transformer.rb', line 117

def add_scanner(scanner)
  raise ArgumentError, "Scanner must respond to #scan" unless scanner.respond_to?(:scan)

  @scanners << scanner
end

#run(abort_on_exc: true) ⇒ Object

Run all three transformation phases

Parameters:

  • abort_on_exc (Boolean) (defaults to: true)

    If true, abort on first exception. If false, log errors and continue.



139
140
141
142
143
144
145
146
147
# File 'lib/gran/tree_transformer.rb', line 139

def run(abort_on_exc: true)
  logger.info { "Starting tree transformation: #{@src_tree.pathname} -> #{@dst_tree.pathname}" }

  run_scan_phase
  run_transform_phase(abort_on_exc: abort_on_exc)
  run_finalize_phase(abort_on_exc: abort_on_exc)

  logger.info { "Tree transformation complete" }
end