Class: Ast::Merge::Recipe::Config

Inherits:
Preset
  • Object
show all
Defined in:
lib/ast/merge/recipe/config.rb

Overview

Loads and represents a merge recipe from YAML configuration.

A recipe extends Preset with:

  • Template file specification
  • Target file patterns
  • Injection point configuration
  • when_missing behavior

Examples:

Loading a recipe

recipe = Config.load(".merge-recipes/gem_family_section.yml")
recipe.name          # => "gem_family_section"
recipe.template_path # => "GEM_FAMILY_SECTION.md"
recipe.targets       # => ["README.md", "vendor/*/README.md"]

Recipe YAML format

name: gem_family_section
description: Update gem family section in README files

template: GEM_FAMILY_SECTION.md

targets:
  - "README.md"
  - "vendor/*/README.md"

injection:
  anchor:
    type: heading
    text: /Gem Family/
  position: replace
  boundary:
    type: heading
    same_or_shallower: true

merge:
  preference: template
  add_missing: true

when_missing: skip

See Also:

Instance Attribute Summary collapse

Attributes inherited from Preset

#description, #freeze_token, #merge_config, #name, #parser, #preset_path

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Preset

#add_missing, #add_missing?, #match_refiner, #node_typing, #normalize_whitespace, #preference, #rehydrate_link_references, #script_loader, #signature_generator, #to_h

Constructor Details

#initialize(config, preset_path: nil, recipe_path: nil) ⇒ Config

Create a recipe from a hash (parsed YAML or programmatic).

Parameters:

  • config (Hash)

    Recipe configuration

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

    Path to recipe file (for relative path resolution)

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

    Alias for preset_path (backward compatibility)



87
88
89
90
91
92
93
94
95
96
# File 'lib/ast/merge/recipe/config.rb', line 87

def initialize(config, preset_path: nil, recipe_path: nil)
  # Support both preset_path and recipe_path for backward compatibility
  effective_path = preset_path || recipe_path
  super(config, preset_path: effective_path)

  @template_path = config["template"] || raise(ArgumentError, "Recipe must have 'template' key")
  @targets = Array(config["targets"] || ["*.md"])
  @injection = parse_injection(config["injection"] || {})
  @when_missing = (config["when_missing"] || "skip").to_sym
end

Instance Attribute Details

#injectionHash (readonly)

Returns Injection point configuration.

Returns:

  • (Hash)

    Injection point configuration



58
59
60
# File 'lib/ast/merge/recipe/config.rb', line 58

def injection
  @injection
end

#targetsArray<String> (readonly)

Returns Glob patterns for target files.

Returns:

  • (Array<String>)

    Glob patterns for target files



55
56
57
# File 'lib/ast/merge/recipe/config.rb', line 55

def targets
  @targets
end

#template_pathString (readonly)

Returns Path to template file (relative to recipe or absolute).

Returns:

  • (String)

    Path to template file (relative to recipe or absolute)



52
53
54
# File 'lib/ast/merge/recipe/config.rb', line 52

def template_path
  @template_path
end

#when_missingSymbol (readonly)

Returns Behavior when injection anchor not found (:skip, :add, :error).

Returns:

  • (Symbol)

    Behavior when injection anchor not found (:skip, :add, :error)



61
62
63
# File 'lib/ast/merge/recipe/config.rb', line 61

def when_missing
  @when_missing
end

Class Method Details

.load(path) ⇒ Config

Load a recipe from a YAML file.

Parameters:

  • path (String)

    Path to the recipe YAML file

Returns:

Raises:

  • (ArgumentError)

    If file doesn’t exist or is invalid



74
75
76
77
78
79
# File 'lib/ast/merge/recipe/config.rb', line 74

def load(path)
  raise ArgumentError, "Recipe file not found: #{path}" unless File.exist?(path)

  yaml = YAML.safe_load_file(path, permitted_classes: [Regexp, Symbol])
  new(yaml, preset_path: path)
end

Instance Method Details

#expand_targets(base_dir: nil) ⇒ Array<String>

Expand target globs to actual file paths.

Parameters:

  • base_dir (String) (defaults to: nil)

    Base directory for glob expansion

Returns:

  • (Array<String>)

    Absolute paths to target files



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ast/merge/recipe/config.rb', line 113

def expand_targets(base_dir: nil)
  base = base_dir || (preset_path ? File.dirname(preset_path) : Dir.pwd)

  targets.flat_map do |pattern|
    if File.absolute_path?(pattern)
      Dir.glob(pattern)
    else
      # Expand and normalize to remove .. segments
      expanded_pattern = File.expand_path(pattern, base)
      Dir.glob(expanded_pattern)
    end
  end.uniq.sort
end

#finder_queryHash

Build an InjectionPointFinder query from the injection config.

Returns:

  • (Hash)

    Arguments for InjectionPointFinder#find



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/ast/merge/recipe/config.rb', line 130

def finder_query
  anchor = injection[:anchor] || {}
  boundary = injection[:boundary] || {}

  query = {
    type: anchor[:type],
    text: anchor[:text],
    position: injection[:position] || :replace,
    boundary_type: boundary[:type],
    boundary_text: boundary[:text],
  }

  # Support tree-depth based boundary detection
  # same_or_shallower: true means "end at next sibling (same tree level or above)"
  if boundary[:same_or_shallower]
    query[:boundary_same_or_shallower] = true
  end

  query.compact
end

#recipe_pathObject

Alias for compatibility - recipe_path points to the same file as preset_path



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

def recipe_path
  preset_path
end

#replace_mode?Boolean

Whether to use replace mode (template replaces section entirely).

Returns:

  • (Boolean)


154
155
156
# File 'lib/ast/merge/recipe/config.rb', line 154

def replace_mode?
  merge_config[:replace_mode] == true
end

#template_absolute_path(base_dir: nil) ⇒ String

Get the absolute path to the template file.

Parameters:

  • base_dir (String) (defaults to: nil)

    Base directory for relative paths

Returns:

  • (String)

    Absolute path to template



102
103
104
105
106
107
# File 'lib/ast/merge/recipe/config.rb', line 102

def template_absolute_path(base_dir: nil)
  return @template_path if File.absolute_path?(@template_path)

  base = base_dir || (preset_path ? File.dirname(preset_path) : Dir.pwd)
  File.expand_path(@template_path, base)
end