Class: Ast::Merge::SmartMergerBase Abstract

Inherits:
Object
  • Object
show all
Includes:
RegionMergeable
Defined in:
lib/ast/merge/smart_merger_base.rb

Overview

This class is abstract.

Subclass and implement #analysis_class and #perform_merge

Abstract base class for SmartMerger implementations across all *-merge gems.

SmartMergerBase provides the standard interface and common functionality for intelligent file merging. Subclasses implement format-specific parsing, analysis, and merge logic while inheriting the common API.

## Standard Options

All SmartMerger implementations support these common options:

  • ‘preference` - `:destination` (default) or `:template`

  • ‘add_template_only_nodes` - `false` (default) or `true`

  • ‘signature_generator` - Custom signature proc or `nil`

  • ‘freeze_token` - Token for freeze block markers

  • ‘match_refiner` - Fuzzy match refiner or `nil`

  • ‘regions` - Region configurations for nested merging

  • ‘region_placeholder` - Custom placeholder for regions

## Implementing a SmartMerger

Subclasses must implement:

  • ‘analysis_class` - Returns the FileAnalysis class for this format

  • ‘perform_merge` - Performs the format-specific merge logic

Subclasses may override:

  • ‘default_freeze_token` - Format-specific default freeze token

  • ‘resolver_class` - Returns the ConflictResolver class (if different)

  • ‘result_class` - Returns the MergeResult class (if different)

  • ‘aligner_class` - Returns the FileAligner class (if used)

  • ‘parse_content` - Custom parsing logic

  • ‘build_analysis_options` - Additional analysis options

  • ‘build_resolver_options` - Additional resolver options

Examples:

Implementing a custom SmartMerger

class MyFormat::SmartMerger < Ast::Merge::SmartMergerBase
  def analysis_class
    MyFormat::FileAnalysis
  end

  def default_freeze_token
    "myformat-merge"
  end

  private

  def perform_merge
    alignment = @aligner.align
    process_alignment(alignment)
    @result
  end
end

Direct Known Subclasses

Text::SmartMerger

Constant Summary

Constants included from RegionMergeable

RegionMergeable::DEFAULT_PLACEHOLDER_PREFIX, RegionMergeable::DEFAULT_PLACEHOLDER_SUFFIX

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RegionMergeable

#extract_dest_regions, #extract_template_regions, #regions_configured?, #setup_regions, #substitute_merged_regions

Constructor Details

#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, freeze_token: nil, match_refiner: nil, regions: nil, region_placeholder: nil, **format_options) ⇒ SmartMergerBase

Creates a new SmartMerger for intelligent file merging.

Parameters:

  • template_content (String)

    Template source content

  • dest_content (String)

    Destination source content

  • signature_generator (Proc, nil) (defaults to: nil)

    Optional proc to generate custom signatures. The proc receives a node and should return one of:

    • An array representing the node’s signature

    • ‘nil` to indicate the node should have no signature

    • The original node to fall through to default signature computation

  • preference (Symbol, Hash) (defaults to: :destination)

    Controls which version to use when nodes have matching signatures but different content:

    • ‘:destination` (default) - Use destination version (preserves customizations)

    • ‘:template` - Use template version (applies updates)

    • Hash for per-type preferences: ‘{ default: :destination, special: :template }`

  • add_template_only_nodes (Boolean) (defaults to: false)

    Controls whether to add nodes that only exist in template:

    • ‘false` (default) - Skip template-only nodes

    • ‘true` - Add template-only nodes to result

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

    Token to use for freeze block markers. Default varies by format (e.g., “prism-merge”, “markly-merge”)

  • match_refiner (#call, nil) (defaults to: nil)

    Optional match refiner for fuzzy matching. Default: nil (fuzzy matching disabled)

  • regions (Array<Hash>, nil) (defaults to: nil)

    Region configurations for nested merging. Each hash should contain:

    • ‘:detector` - RegionDetectorBase instance

    • ‘:merger_class` - SmartMerger class for the region (optional)

    • ‘:merger_options` - Options for the region merger (optional)

    • ‘:regions` - Nested region configs (optional, for recursive regions)

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

    Custom placeholder prefix for regions. Default: “<<<AST_MERGE_REGION_”

  • format_options (Hash)

    Format-specific parser options passed to FileAnalysis. These are merged with freeze_token and signature_generator in build_full_analysis_options. Examples:

    • Markly: ‘flags: Markly::FOOTNOTES, extensions: [:table, :strikethrough]`

    • Commonmarker: ‘options: { parse: { smart: true } }`

    • Prism: (no additional parser options needed)

Raises:



145
146
147
148
149
150
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
180
181
182
183
184
185
# File 'lib/ast/merge/smart_merger_base.rb', line 145

def initialize(
  template_content,
  dest_content,
  signature_generator: nil,
  preference: :destination,
  add_template_only_nodes: false,
  freeze_token: nil,
  match_refiner: nil,
  regions: nil,
  region_placeholder: nil,
  **format_options
)
  @template_content = template_content
  @dest_content = dest_content
  @signature_generator = signature_generator
  @preference = preference
  @add_template_only_nodes = add_template_only_nodes
  @freeze_token = freeze_token || default_freeze_token
  @match_refiner = match_refiner
  @format_options = format_options

  # Set up region support
  setup_regions(regions: regions || [], region_placeholder: region_placeholder)

  # Extract regions before parsing (if configured)
  template_for_parsing = extract_template_regions(@template_content)
  dest_for_parsing = extract_dest_regions(@dest_content)

  # Parse and analyze both files
  @template_analysis = parse_and_analyze(template_for_parsing, :template)
  @dest_analysis = parse_and_analyze(dest_for_parsing, :destination)

  # Set up aligner (if applicable)
  @aligner = build_aligner if respond_to?(:aligner_class, true) && aligner_class

  # Set up resolver
  @resolver = build_resolver

  # Set up result
  @result = build_result
end

Instance Attribute Details

#add_template_only_nodesBoolean (readonly)

Returns Whether to add template-only nodes.

Returns:

  • (Boolean)

    Whether to add template-only nodes



87
88
89
# File 'lib/ast/merge/smart_merger_base.rb', line 87

def add_template_only_nodes
  @add_template_only_nodes
end

#alignerObject? (readonly)

Returns Aligner for finding matches (if applicable).

Returns:

  • (Object, nil)

    Aligner for finding matches (if applicable)



75
76
77
# File 'lib/ast/merge/smart_merger_base.rb', line 75

def aligner
  @aligner
end

#dest_analysisObject (readonly)

Returns Analysis of the destination file.

Returns:

  • (Object)

    Analysis of the destination file



72
73
74
# File 'lib/ast/merge/smart_merger_base.rb', line 72

def dest_analysis
  @dest_analysis
end

#dest_contentString (readonly)

Returns Destination source content.

Returns:

  • (String)

    Destination source content



66
67
68
# File 'lib/ast/merge/smart_merger_base.rb', line 66

def dest_content
  @dest_content
end

#freeze_tokenString (readonly)

Returns Token for freeze block markers.

Returns:

  • (String)

    Token for freeze block markers



90
91
92
# File 'lib/ast/merge/smart_merger_base.rb', line 90

def freeze_token
  @freeze_token
end

#match_refinerObject? (readonly)

Returns Match refiner for fuzzy matching.

Returns:

  • (Object, nil)

    Match refiner for fuzzy matching



96
97
98
# File 'lib/ast/merge/smart_merger_base.rb', line 96

def match_refiner
  @match_refiner
end

#preferenceSymbol, Hash (readonly)

Returns Preference for signature matches.

Returns:

  • (Symbol, Hash)

    Preference for signature matches



84
85
86
# File 'lib/ast/merge/smart_merger_base.rb', line 84

def preference
  @preference
end

#resolverObject (readonly)

Returns Resolver for handling conflicts.

Returns:

  • (Object)

    Resolver for handling conflicts



78
79
80
# File 'lib/ast/merge/smart_merger_base.rb', line 78

def resolver
  @resolver
end

#resultObject (readonly)

Returns Result object tracking merged content.

Returns:

  • (Object)

    Result object tracking merged content



81
82
83
# File 'lib/ast/merge/smart_merger_base.rb', line 81

def result
  @result
end

#signature_generatorProc? (readonly)

Returns Custom signature generator.

Returns:

  • (Proc, nil)

    Custom signature generator



93
94
95
# File 'lib/ast/merge/smart_merger_base.rb', line 93

def signature_generator
  @signature_generator
end

#template_analysisObject (readonly)

Returns Analysis of the template file.

Returns:

  • (Object)

    Analysis of the template file



69
70
71
# File 'lib/ast/merge/smart_merger_base.rb', line 69

def template_analysis
  @template_analysis
end

#template_contentString (readonly)

Returns Template source content.

Returns:

  • (String)

    Template source content



63
64
65
# File 'lib/ast/merge/smart_merger_base.rb', line 63

def template_content
  @template_content
end

Instance Method Details

#mergeString

Perform the merge operation and return the merged content as a string.

Returns:

  • (String)

    The merged content



190
191
192
# File 'lib/ast/merge/smart_merger_base.rb', line 190

def merge
  merge_result.to_s
end

#merge_resultObject

Perform the merge operation and return the full result object.

This method is memoized - subsequent calls return the cached result.

Returns:

  • (Object)

    The merge result (format-specific MergeResult subclass)



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/ast/merge/smart_merger_base.rb', line 199

def merge_result
  return @merge_result if @merge_result

  @merge_result = DebugLogger.time("#{self.class.name}#merge") do
    result = perform_merge

    # Substitute merged regions back into the result if configured
    if regions_configured? && (merged_content = result.to_s)
      final_content = substitute_merged_regions(merged_content)
      update_result_content(result, final_content)
    end

    result
  end
end

#merge_with_debugHash

Perform the merge and return detailed debug information.

Returns:

  • (Hash)

    Hash containing:

    • ‘:content` [String] - Final merged content

    • ‘:statistics` [Hash] - Merge decision counts



220
221
222
223
224
225
226
227
# File 'lib/ast/merge/smart_merger_base.rb', line 220

def merge_with_debug
  content = merge

  {
    content: content,
    statistics: @result.decision_summary,
  }
end

#statsHash

Get merge statistics.

Returns:

  • (Hash)

    Statistics about the merge



232
233
234
235
# File 'lib/ast/merge/smart_merger_base.rb', line 232

def stats
  merge_result # Ensure merge has run
  @result.decision_summary
end