Class: Ast::Merge::Recipe::Runner

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

Overview

Executes a merge recipe against target files.

The runner:

  1. Loads the template file
  2. Expands target file globs
  3. For each target, finds the injection point and performs the merge
  4. Collects results for reporting

Examples:

Running a recipe

recipe = Recipe::Config.load(".merge-recipes/gem_family_section.yml")
runner = Recipe::Runner.new(recipe, dry_run: true)
results = runner.run
puts runner.summary

With custom parser

runner = Recipe::Runner.new(recipe, parser: :markly, base_dir: "/path/to/project")
results = runner.run

See Also:

Defined Under Namespace

Classes: Result, StepResult

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(recipe, dry_run: false, base_dir: nil, parser: nil, verbose: false, target_files: nil, context: nil, **_options) ⇒ Runner

Initialize a recipe runner.

Parameters:

  • recipe (Config)

    The recipe to execute

  • dry_run (Boolean) (defaults to: false)

    If true, don't write files

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

    Base directory for path resolution

  • parser (Symbol, nil) (defaults to: nil)

    Which parser to use; when omitted, prefer an explicitly configured recipe parser, otherwise default to :markly

  • verbose (Boolean) (defaults to: false)

    Enable verbose output

  • target_files (Array<String>, nil) (defaults to: nil)

    Override recipe targets with these files

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

    Caller-supplied runtime context for ruby_script steps



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ast/merge/recipe/runner.rb', line 77

def initialize(recipe, dry_run: false, base_dir: nil, parser: nil, verbose: false, target_files: nil,
               context: nil, **_options)
  @recipe = recipe
  @dry_run = dry_run
  @base_dir = base_dir || Dir.pwd
  @parser = resolve_parser(parser)
  @verbose = verbose
  @target_files = target_files
  @context = normalize_runtime_context(context)
  @results = []
end

Instance Attribute Details

#base_dirString (readonly)

Returns Base directory for path resolution.

Returns:

  • (String)

    Base directory for path resolution



54
55
56
# File 'lib/ast/merge/recipe/runner.rb', line 54

def base_dir
  @base_dir
end

#contextHash (readonly)

Returns Caller-supplied runtime context available to ruby_script steps.

Returns:

  • (Hash)

    Caller-supplied runtime context available to ruby_script steps



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

def context
  @context
end

#dry_runBoolean (readonly)

Returns Whether this is a dry run.

Returns:

  • (Boolean)

    Whether this is a dry run



51
52
53
# File 'lib/ast/merge/recipe/runner.rb', line 51

def dry_run
  @dry_run
end

#parserSymbol (readonly)

Returns Parser to use (:markly, :commonmarker, :prism, :psych, etc.).

Returns:

  • (Symbol)

    Parser to use (:markly, :commonmarker, :prism, :psych, etc.)



57
58
59
# File 'lib/ast/merge/recipe/runner.rb', line 57

def parser
  @parser
end

#recipeConfig (readonly)

Returns The recipe being executed.

Returns:

  • (Config)

    The recipe being executed



48
49
50
# File 'lib/ast/merge/recipe/runner.rb', line 48

def recipe
  @recipe
end

#resultsArray<Result> (readonly)

Returns Results from the last run.

Returns:

  • (Array<Result>)

    Results from the last run



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

def results
  @results
end

#target_filesArray<String>? (readonly)

Returns Target files override (from command line).

Returns:

  • (Array<String>, nil)

    Target files override (from command line)



60
61
62
# File 'lib/ast/merge/recipe/runner.rb', line 60

def target_files
  @target_files
end

Instance Method Details

#results_by_statusHash<Symbol, Array<Result>>

Get results grouped by status.

Returns:

  • (Hash<Symbol, Array<Result>>)


145
146
147
# File 'lib/ast/merge/recipe/runner.rb', line 145

def results_by_status
  @results.group_by(&:status)
end

#results_tableArray<Hash>

Format results as an array of hashes for TableTennis.

Returns:

  • (Array<Hash>)


167
168
169
170
171
172
173
174
175
176
# File 'lib/ast/merge/recipe/runner.rb', line 167

def results_table
  @results.map do |r|
    {
      file: r.relative_path,
      status: r.status.to_s,
      changed: r.changed ? 'yes' : 'no',
      message: r.message
    }
  end
end

#runArray<Result>

Run the recipe against all target files.

Returns:

  • (Array<Result>)

    Results for each processed file



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/ast/merge/recipe/runner.rb', line 92

def run
  @results = []
  ensure_file_recipe!

  template_content = load_template

  # Use command-line targets if provided, otherwise expand from recipe
  files_to_process = if @target_files && !@target_files.empty?
                       # Expand paths relative to base_dir
                       @target_files.map { |f| File.expand_path(f, @base_dir) }
                     else
                       # Let the recipe expand targets from its own location
                       recipe.expand_targets
                     end

  files_to_process.each do |target_path|
    result = process_file(target_path, template_content)
    @results << result
    yield result if block_given?
  end

  @results
end

#run_content(template_content:, destination_content:, target_path: nil, relative_path: nil, context: nil, **_options) {|@results.first| ... } ⇒ Result

Run the recipe against caller-provided content instead of on-disk files.

This is the execution path for content-only recipes that omit template/target bindings from YAML and expect the caller to provide the template and destination strings directly.

Parameters:

  • template_content (String)

    Source/template content for the recipe

  • destination_content (String)

    Existing destination content

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

    Optional synthetic path for reporting

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

    Optional synthetic relative path for reporting

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

    Optional per-call runtime context merged into runner context

Yields:

Returns:



128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ast/merge/recipe/runner.rb', line 128

def run_content(template_content:, destination_content:, target_path: nil, relative_path: nil, context: nil,
                **_options)
  @results = [process_file_steps(
    target_path || relative_path || '(memory)',
    relative_path || target_path || '(memory)',
    template_content,
    destination_content,
    write_changes: false,
    context: runtime_context(context)
  )]
  yield @results.first if block_given?
  @results.first
end

#summaryHash

Get a summary hash of the run.

Returns:

  • (Hash)


152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ast/merge/recipe/runner.rb', line 152

def summary
  by_status = results_by_status
  {
    total: @results.size,
    updated: (by_status[:updated] || []).size,
    would_update: (by_status[:would_update] || []).size,
    unchanged: (by_status[:unchanged] || []).size,
    skipped: (by_status[:skipped] || []).size,
    errors: (by_status[:error] || []).size
  }
end

#summary_tableArray<Hash>

Format summary as an array of hashes for TableTennis.

Returns:

  • (Array<Hash>)


181
182
183
184
185
186
187
188
189
190
# File 'lib/ast/merge/recipe/runner.rb', line 181

def summary_table
  s = summary
  [
    { metric: 'Total files', value: s[:total] },
    { metric: 'Updated', value: dry_run ? s[:would_update] : s[:updated] },
    { metric: 'Unchanged', value: s[:unchanged] },
    { metric: 'Skipped (no anchor)', value: s[:skipped] },
    { metric: 'Errors', value: s[:errors] }
  ]
end