Class: Ast::Merge::MergerConfig

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

Overview

Configuration object for SmartMerger options.

This class encapsulates common configuration options used across all *-merge gem SmartMerger implementations. It provides a standardized interface for merge configuration and validates option values.

Examples:

Creating a config with defaults

config = MergerConfig.new
config.preference  # => :destination
config.add_template_only_nodes     # => false

Creating a config for template-wins merge

config = MergerConfig.new(
  preference: :template,
  add_template_only_nodes: true
)

Using with SmartMerger

config = MergerConfig.new(preference: :template)
merger = SmartMerger.new(template, dest, **config.to_h)

Per-node-type preferences with node_typing

node_typing = {
  CallNode: ->(node) {
    return node unless node.name == :gem
    gem_name = node.arguments&.arguments&.first&.unescaped
    if gem_name&.start_with?("rubocop")
      Ast::Merge::NodeTyping.with_merge_type(node, :lint_gem)
    else
      node
    end
  }
}

config = MergerConfig.new(
  node_typing: node_typing,
  preference: {
    default: :destination,
    lint_gem: :template  # Use template versions for lint gems
  }
)

Constant Summary collapse

VALID_PREFERENCES =

Valid values for preference (when using Symbol)

i[destination template].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(preference: :destination, add_template_only_nodes: false, freeze_token: nil, signature_generator: nil, node_typing: nil) ⇒ MergerConfig

Initialize a new MergerConfig.

Parameters:

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

    Which version to prefer on match. As Symbol: :destination or :template As Hash: Maps node types/merge_types to preferences

    @example { default: :destination, lint_gem: :template }
    
  • add_template_only_nodes (Boolean) (defaults to: false)

    Whether to add template-only nodes

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

    Token for freeze block markers (nil uses gem default)

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

    Custom signature generator

  • node_typing (Hash{Symbol,String => #call}, nil) (defaults to: nil)

    Node typing configuration

Raises:

  • (ArgumentError)

    If preference is invalid

  • (ArgumentError)

    If node_typing is invalid



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ast/merge/merger_config.rb', line 90

def initialize(
  preference: :destination,
  add_template_only_nodes: false,
  freeze_token: nil,
  signature_generator: nil,
  node_typing: nil
)
  validate_preference!(preference)
  NodeTyping.validate!(node_typing) if node_typing

  @preference = preference
  @add_template_only_nodes = add_template_only_nodes
  @freeze_token = freeze_token
  @signature_generator = signature_generator
  @node_typing = node_typing
end

Instance Attribute Details

#add_template_only_nodesBoolean (readonly)

Returns Whether to add nodes that only exist in template

  • false (default) - Skip template-only nodes

  • true - Add template-only nodes to result.

Returns:

  • (Boolean)

    Whether to add nodes that only exist in template

    • false (default) - Skip template-only nodes

    • true - Add template-only nodes to result



64
65
66
# File 'lib/ast/merge/merger_config.rb', line 64

def add_template_only_nodes
  @add_template_only_nodes
end

#freeze_tokenString (readonly)

Returns Token used for freeze block markers.

Returns:

  • (String)

    Token used for freeze block markers



67
68
69
# File 'lib/ast/merge/merger_config.rb', line 67

def freeze_token
  @freeze_token
end

#node_typingHash{Symbol,String => #call}? (readonly)

Returns Node typing configuration. Maps node type names to callable objects that can transform nodes and optionally add merge_type attributes for per-node-type preferences.

Returns:

  • (Hash{Symbol,String => #call}, nil)

    Node typing configuration. Maps node type names to callable objects that can transform nodes and optionally add merge_type attributes for per-node-type preferences.



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

def node_typing
  @node_typing
end

#preferenceSymbol, Hash (readonly)

Returns Which version to prefer when nodes have matching signatures. As Symbol:

  • :destination (default) - Keep destination version (preserves customizations)

  • :template - Use template version (applies updates)

As Hash:

  • Keys are node types (Symbol) or merge_types from node_typing

  • Values are :destination or :template

  • Use :default key for fallback preference

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

Returns:

  • (Symbol, Hash)

    Which version to prefer when nodes have matching signatures. As Symbol:

    • :destination (default) - Keep destination version (preserves customizations)

    • :template - Use template version (applies updates)

    As Hash:

    • Keys are node types (Symbol) or merge_types from node_typing

    • Values are :destination or :template

    • Use :default key for fallback preference

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



59
60
61
# File 'lib/ast/merge/merger_config.rb', line 59

def preference
  @preference
end

#signature_generatorProc? (readonly)

Returns Custom signature generator proc.

Returns:

  • (Proc, nil)

    Custom signature generator proc



70
71
72
# File 'lib/ast/merge/merger_config.rb', line 70

def signature_generator
  @signature_generator
end

Class Method Details

.destination_wins(freeze_token: nil, signature_generator: nil, node_typing: nil) ⇒ MergerConfig

Create a config preset for “destination wins” merging. Destination customizations are preserved, template-only content is skipped.

Parameters:

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

    Optional freeze token

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

    Optional signature generator

  • node_typing (Hash, nil) (defaults to: nil)

    Optional node typing configuration

Returns:



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

def self.destination_wins(freeze_token: nil, signature_generator: nil, node_typing: nil)
  new(
    preference: :destination,
    add_template_only_nodes: false,
    freeze_token: freeze_token,
    signature_generator: signature_generator,
    node_typing: node_typing,
  )
end

.template_wins(freeze_token: nil, signature_generator: nil, node_typing: nil) ⇒ MergerConfig

Create a config preset for “template wins” merging. Template updates are applied, template-only content is added.

Parameters:

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

    Optional freeze token

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

    Optional signature generator

  • node_typing (Hash, nil) (defaults to: nil)

    Optional node typing configuration

Returns:



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

def self.template_wins(freeze_token: nil, signature_generator: nil, node_typing: nil)
  new(
    preference: :template,
    add_template_only_nodes: true,
    freeze_token: freeze_token,
    signature_generator: signature_generator,
    node_typing: node_typing,
  )
end

Instance Method Details

#per_type_preference?Boolean

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

Returns:

  • (Boolean)

    true if preference is a Hash



162
163
164
# File 'lib/ast/merge/merger_config.rb', line 162

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

#prefer_destination?Boolean

Check if destination version should be preferred on signature match. For Hash preferences, checks the :default key.

Returns:

  • (Boolean)

    true if destination preference



111
112
113
114
115
116
117
# File 'lib/ast/merge/merger_config.rb', line 111

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

#prefer_template?Boolean

Check if template version should be preferred on signature match. For Hash preferences, checks the :default key.

Returns:

  • (Boolean)

    true if template preference



123
124
125
126
127
128
129
# File 'lib/ast/merge/merger_config.rb', line 123

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

#preference_for(type) ⇒ Symbol

Get the preference for a specific node type or merge_type.

When preference is a Hash, looks up the preference for the given type, falling back to :default, then to :destination.

Examples:

With Symbol preference

config = MergerConfig.new(preference: :template)
config.preference_for(:any_type)  # => :template

With Hash preference

config = MergerConfig.new(
  preference: { default: :destination, lint_gem: :template }
)
config.preference_for(:lint_gem)   # => :template
config.preference_for(:other_type) # => :destination

Parameters:

  • type (Symbol, nil)

    The node type or merge_type to look up

Returns:

  • (Symbol)

    :destination or :template



149
150
151
152
153
154
155
156
157
# File 'lib/ast/merge/merger_config.rb', line 149

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

#to_h(default_freeze_token: nil) ⇒ Hash

Note:

Uses :preference key to match SmartMerger’s API (not :preference)

Convert config to a hash suitable for passing to SmartMerger.

Parameters:

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

    Default freeze token to use if none specified

Returns:

  • (Hash)

    Configuration as keyword arguments hash



171
172
173
174
175
176
177
178
179
180
# File 'lib/ast/merge/merger_config.rb', line 171

def to_h(default_freeze_token: nil)
  result = {
    preference: @preference,
    add_template_only_nodes: @add_template_only_nodes,
  }
  result[:freeze_token] = @freeze_token || default_freeze_token if @freeze_token || default_freeze_token
  result[:signature_generator] = @signature_generator if @signature_generator
  result[:node_typing] = @node_typing if @node_typing
  result
end

#with(**options) ⇒ MergerConfig

Create a new config with updated values.

Parameters:

  • options (Hash)

    Options to override

Returns:



186
187
188
189
190
191
192
193
194
# File 'lib/ast/merge/merger_config.rb', line 186

def with(**options)
  self.class.new(
    preference: options.fetch(:preference, @preference),
    add_template_only_nodes: options.fetch(:add_template_only_nodes, @add_template_only_nodes),
    freeze_token: options.fetch(:freeze_token, @freeze_token),
    signature_generator: options.fetch(:signature_generator, @signature_generator),
    node_typing: options.fetch(:node_typing, @node_typing),
  )
end