Class: Ast::Merge::ConflictResolverBase Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/ast/merge/conflict_resolver_base.rb

Overview

This class is abstract.

Subclass and implement resolve_node_pair, resolve_batch, or resolve_boundary

Base class for conflict resolvers across all *-merge gems.

Provides common functionality for resolving conflicts between template and destination content during merge operations. Supports three resolution strategies that can be selected based on the needs of each file format:

  • ‘:node` - Per-node resolution (resolve individual node pairs)

  • ‘:batch` - Batch resolution (resolve entire file using signature maps)

  • ‘:boundary` - Boundary resolution (resolve sections/ranges of content)

Examples:

Node-based resolution (commonmarker-merge style)

class ConflictResolver < Ast::Merge::ConflictResolverBase
  def initialize(preference:, template_analysis:, dest_analysis:)
    super(
      strategy: :node,
      preference: preference,
      template_analysis: template_analysis,
      dest_analysis: dest_analysis
    )
  end

  # Called for each node pair
  def resolve_node_pair(template_node, dest_node, template_index:, dest_index:)
    # Return resolution hash
  end
end

Batch resolution (psych-merge/json-merge style)

class ConflictResolver < Ast::Merge::ConflictResolverBase
  def initialize(template_analysis, dest_analysis, preference: :destination)
    super(
      strategy: :batch,
      preference: preference,
      template_analysis: template_analysis,
      dest_analysis: dest_analysis
    )
  end

  # Called once for entire merge
  def resolve_batch(result)
    # Populate result with merged content
  end
end

Boundary resolution (prism-merge style)

class ConflictResolver < Ast::Merge::ConflictResolverBase
  def initialize(template_analysis, dest_analysis, preference: :destination)
    super(
      strategy: :boundary,
      preference: preference,
      template_analysis: template_analysis,
      dest_analysis: dest_analysis
    )
  end

  # Called for each boundary (section with differences)
  def resolve_boundary(boundary, result)
    # Process boundary and populate result
  end
end

Direct Known Subclasses

Text::ConflictResolver

Constant Summary collapse

DECISION_DESTINATION =

Use destination version (customization preserved)

:destination
DECISION_TEMPLATE =

Use template version (update applied)

:template
DECISION_ADDED =

Content was added from template (template-only)

:added
DECISION_FROZEN =

Content preserved from frozen block

:frozen
DECISION_IDENTICAL =

Content was identical (no conflict)

:identical
DECISION_KEPT_DEST =

Content was kept from destination (signature match, dest preferred)

:kept_destination
DECISION_KEPT_TEMPLATE =

Content was kept from template (signature match, template preferred)

:kept_template
DECISION_APPENDED =

Content was appended from destination (dest-only)

:appended
DECISION_FREEZE_BLOCK =

Content preserved from freeze block marker

:freeze_block
DECISION_RECURSIVE =

Content requires recursive merge (container types)

:recursive
DECISION_REPLACED =

Content was replaced (signature match with different content)

:replaced

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(strategy:, preference:, template_analysis:, dest_analysis:, add_template_only_nodes: false) ⇒ ConflictResolverBase

Initialize the conflict resolver

Parameters:

  • strategy (Symbol)

    Resolution strategy (:node, :batch, or :boundary)

  • preference (Symbol, Hash)

    Which version to prefer. As Symbol: :destination or :template (applies to all nodes) As Hash: Maps node types/merge_types to preferences

    - Use :default key for fallback preference
    @example { default: :destination, lint_gem: :template }
    
  • template_analysis (Object)

    Analysis of the template file

  • dest_analysis (Object)

    Analysis of the destination file

  • add_template_only_nodes (Boolean) (defaults to: false)

    Whether to add nodes only in template (batch/boundary strategy)



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/ast/merge/conflict_resolver_base.rb', line 132

def initialize(strategy:, preference:, template_analysis:, dest_analysis:, add_template_only_nodes: false)
  unless i[node batch boundary].include?(strategy)
    raise ArgumentError, "Invalid strategy: #{strategy}. Must be :node, :batch, or :boundary"
  end

  validate_preference!(preference)

  @strategy = strategy
  @preference = preference
  @template_analysis = template_analysis
  @dest_analysis = dest_analysis
  @add_template_only_nodes = add_template_only_nodes
end

Instance Attribute Details

#add_template_only_nodesBoolean (readonly)

Returns Whether to add template-only nodes (batch strategy).

Returns:

  • (Boolean)

    Whether to add template-only nodes (batch strategy)



119
120
121
# File 'lib/ast/merge/conflict_resolver_base.rb', line 119

def add_template_only_nodes
  @add_template_only_nodes
end

#dest_analysisObject (readonly)

Returns Destination file analysis.

Returns:

  • (Object)

    Destination file analysis



116
117
118
# File 'lib/ast/merge/conflict_resolver_base.rb', line 116

def dest_analysis
  @dest_analysis
end

#preferenceSymbol, Hash (readonly)

Returns Merge preference. As Symbol: :destination or :template (applies to all nodes) As Hash: Maps node types/merge_types to preferences

@example { default: :destination, lint_gem: :template }.

Returns:

  • (Symbol, Hash)

    Merge preference. As Symbol: :destination or :template (applies to all nodes) As Hash: Maps node types/merge_types to preferences

    @example { default: :destination, lint_gem: :template }
    


110
111
112
# File 'lib/ast/merge/conflict_resolver_base.rb', line 110

def preference
  @preference
end

#strategySymbol (readonly)

Returns Resolution strategy (:node, :batch, or :boundary).

Returns:

  • (Symbol)

    Resolution strategy (:node, :batch, or :boundary)



104
105
106
# File 'lib/ast/merge/conflict_resolver_base.rb', line 104

def strategy
  @strategy
end

#template_analysisObject (readonly)

Returns Template file analysis.

Returns:

  • (Object)

    Template file analysis



113
114
115
# File 'lib/ast/merge/conflict_resolver_base.rb', line 113

def template_analysis
  @template_analysis
end

Instance Method Details

#default_preferenceSymbol

Get the default preference (used as fallback).

Returns:

  • (Symbol)

    :destination or :template



203
204
205
206
207
208
209
# File 'lib/ast/merge/conflict_resolver_base.rb', line 203

def default_preference
  if @preference.is_a?(Hash)
    @preference.fetch(:default, :destination)
  else
    @preference
  end
end

#freeze_node?(node) ⇒ Boolean

Check if a node is a freeze node using duck typing

Parameters:

  • node (Object)

    Node to check

Returns:

  • (Boolean)

    True if node is a freeze node



169
170
171
# File 'lib/ast/merge/conflict_resolver_base.rb', line 169

def freeze_node?(node)
  node.respond_to?(:freeze_node?) && node.freeze_node?
end

#per_type_preference?Boolean

Check if Hash-based per-type preferences are configured.

Returns:

  • (Boolean)

    true if preference is a Hash



214
215
216
# File 'lib/ast/merge/conflict_resolver_base.rb', line 214

def per_type_preference?
  @preference.is_a?(Hash)
end

#preference_for_node(node) ⇒ Symbol

Get the preference for a specific node.

When preference is a Hash, looks up the preference for the node’s merge_type (if wrapped with NodeTyping) or falls back to :default.

Examples:

With Symbol preference

preference_for_node(any_node)  # => returns @preference

With Hash preference and typed node

# Given preference: { default: :destination, lint_gem: :template }
preference_for_node(lint_gem_node)  # => :template
preference_for_node(other_node)     # => :destination

Parameters:

  • node (Object, nil)

    The node to get preference for

Returns:

  • (Symbol)

    :destination or :template



188
189
190
191
192
193
194
195
196
197
198
# File 'lib/ast/merge/conflict_resolver_base.rb', line 188

def preference_for_node(node)
  return default_preference unless @preference.is_a?(Hash)
  return default_preference unless node

  # Check if node has a merge_type (from NodeTyping)
  merge_type = NodeTyping.merge_type_for(node)
  return @preference.fetch(merge_type) { default_preference } if merge_type

  # Fall back to default
  default_preference
end

#resolve(*args, **kwargs) ⇒ Object

Resolve conflicts using the configured strategy

For :node strategy, this delegates to resolve_node_pair For :batch strategy, this delegates to resolve_batch For :boundary strategy, this delegates to resolve_boundary

Parameters:

  • args (Array)

    Arguments passed to the strategy method

Returns:

  • (Object)

    Resolution result (format depends on strategy)



154
155
156
157
158
159
160
161
162
163
# File 'lib/ast/merge/conflict_resolver_base.rb', line 154

def resolve(*args, **kwargs)
  case @strategy
  when :node
    resolve_node_pair(*args, **kwargs)
  when :batch
    resolve_batch(*args)
  when :boundary
    resolve_boundary(*args)
  end
end