Class: Prism::Merge::SmartMerger

Inherits:
Object
  • Object
show all
Defined in:
lib/prism/merge/smart_merger.rb

Overview

Orchestrates the smart merge process using FileAnalysis, FileAligner, ConflictResolver, and MergeResult to merge two Ruby files intelligently.

SmartMerger provides flexible configuration for different merge scenarios. When matching class or module definitions are found in both files, the merger automatically performs recursive merging of their bodies, intelligently combining nested methods, constants, and other definitions.

Examples:

Basic merge (destination customizations preserved)

merger = SmartMerger.new(template_content, dest_content)
result = merger.merge

Version file merge (template updates win)

merger = SmartMerger.new(
  template_content,
  dest_content,
  signature_match_preference: :template,
  add_template_only_nodes: true
)
result = merger.merge
# Result: VERSION = "2.0.0" (from template), new constants added

Appraisals merge (destination customizations preserved)

merger = SmartMerger.new(
  template_content,
  dest_content,
  signature_match_preference: :destination,  # default
  add_template_only_nodes: false             # default
)
result = merger.merge
# Result: Custom gem versions preserved, template-only blocks skipped

Custom signature matching

sig_gen = ->(node) { [node.class.name, node.name] }
merger = SmartMerger.new(
  template_content,
  dest_content,
  signature_generator: sig_gen
)

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template_content, dest_content, signature_generator: nil, signature_match_preference: :destination, add_template_only_nodes: false, freeze_token: FileAnalysis::DEFAULT_FREEZE_TOKEN, max_recursion_depth: Float::INFINITY, current_depth: 0) ⇒ SmartMerger

Creates a new SmartMerger for intelligent Ruby file merging.

Examples:

Basic usage

merger = SmartMerger.new(template, destination)
result = merger.merge

Template updates win (version files)

merger = SmartMerger.new(
  template,
  destination,
  signature_match_preference: :template,
  add_template_only_nodes: true
)

Destination customizations win (Appraisals)

merger = SmartMerger.new(
  template,
  destination,
  signature_match_preference: :destination,
  add_template_only_nodes: false
)

Custom signature matching with fallthrough

sig_gen = lambda do |node|
  case node
  when Prism::CallNode
    # Custom handling for gem calls - match by gem name
    if node.name == :gem
      return [:gem, node.arguments&.arguments&.first&.unescaped]
    end
  end
  # Return the node to fall through to default signature computation
  node
end

merger = SmartMerger.new(
  template,
  destination,
  signature_generator: sig_gen
)

Raises:



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/prism/merge/smart_merger.rb', line 151

def initialize(template_content, dest_content, signature_generator: nil, signature_match_preference: :destination, add_template_only_nodes: false, freeze_token: FileAnalysis::DEFAULT_FREEZE_TOKEN, max_recursion_depth: Float::INFINITY, current_depth: 0)
  @template_content = template_content
  @dest_content = dest_content
  @signature_match_preference = signature_match_preference
  @add_template_only_nodes = add_template_only_nodes
  @freeze_token = freeze_token
  @max_recursion_depth = max_recursion_depth
  @current_depth = current_depth
  @template_analysis = FileAnalysis.new(template_content, signature_generator: signature_generator, freeze_token: freeze_token)
  @dest_analysis = FileAnalysis.new(dest_content, signature_generator: signature_generator, freeze_token: freeze_token)
  @aligner = FileAligner.new(@template_analysis, @dest_analysis)
  @resolver = ConflictResolver.new(
    @template_analysis,
    @dest_analysis,
    signature_match_preference: signature_match_preference,
    add_template_only_nodes: add_template_only_nodes,
  )
  @result = MergeResult.new
end

Instance Attribute Details

#alignerFileAligner (readonly)



57
58
59
# File 'lib/prism/merge/smart_merger.rb', line 57

def aligner
  @aligner
end

#dest_analysisFileAnalysis (readonly)



54
55
56
# File 'lib/prism/merge/smart_merger.rb', line 54

def dest_analysis
  @dest_analysis
end

#resolverConflictResolver (readonly)



60
61
62
# File 'lib/prism/merge/smart_merger.rb', line 60

def resolver
  @resolver
end

#resultMergeResult (readonly)



63
64
65
# File 'lib/prism/merge/smart_merger.rb', line 63

def result
  @result
end

#template_analysisFileAnalysis (readonly)



51
52
53
# File 'lib/prism/merge/smart_merger.rb', line 51

def template_analysis
  @template_analysis
end

Instance Method Details

#mergeString

Performs the intelligent merge of template and destination files.

The merge process:

  1. Validates both files for syntax errors

  2. Finds anchors (matching sections) and boundaries (differences)

  3. Processes anchors and boundaries in order

  4. Returns merged content as a string

Merge behavior is controlled by constructor parameters:

  • signature_match_preference: Which version wins for matching nodes

  • add_template_only_nodes: Whether to add template-only content

Examples:

Basic merge

merger = SmartMerger.new(template, destination)
result = merger.merge
File.write("output.rb", result)

With error handling

begin
  result = merger.merge
rescue Prism::Merge::TemplateParseError => e
  puts "Template error: #{e.message}"
  puts "Parse errors: #{e.parse_result.errors}"
end

Raises:

See Also:



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/prism/merge/smart_merger.rb', line 202

def merge
  # Handle invalid files
  unless @template_analysis.valid?
    raise Prism::Merge::TemplateParseError.new(
      "Template file has parsing errors",
      content: @template_content,
      parse_result: @template_analysis.parse_result,
    )
  end

  unless @dest_analysis.valid?
    raise Prism::Merge::DestinationParseError.new(
      "Destination file has parsing errors",
      content: @dest_content,
      parse_result: @dest_analysis.parse_result,
    )
  end

  # Find anchors and boundaries
  boundaries = @aligner.align

  # Process the merge by walking through anchors and boundaries in order
  process_merge(boundaries)

  # Return final content
  @result.to_s
end

#merge_with_debugHash

Performs merge and returns detailed debug information.

This method provides comprehensive information about merge decisions, useful for debugging, testing, and understanding merge behavior.

Examples:

Get merge statistics

result = merger.merge_with_debug
puts "Template lines: #{result[:statistics][:kept_template]}"
puts "Replaced lines: #{result[:statistics][:replaced]}"

Debug line provenance

result = merger.merge_with_debug
puts result[:debug]
# Output shows source file and decision for each line:
# Line 1: [KEPT_TEMPLATE] # frozen_string_literal: true
# Line 2: [KEPT_TEMPLATE]
# Line 3: [REPLACED] VERSION = "2.0.0"

See Also:



259
260
261
262
263
264
265
266
# File 'lib/prism/merge/smart_merger.rb', line 259

def merge_with_debug
  content = merge
  {
    content: content,
    debug: @result.debug_output,
    statistics: @result.statistics,
  }
end