Class: Ask::Tools::Shell::FileMutationQueue

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/tools/shell/file_mutation_queue.rb

Overview

Queues file mutations and applies them atomically. Rollback on any failure — no partial writes.

queue = FileMutationQueue.new
queue.stage("path/to/file.rb", ->(content) {
  content.gsub("old", "new")
})
queue.apply!  # reads all staged files, applies transforms, writes back

Defined Under Namespace

Classes: ApplyError

Instance Method Summary collapse

Constructor Details

#initialize(operations: nil) ⇒ FileMutationQueue

Returns a new instance of FileMutationQueue.



18
19
20
21
# File 'lib/ask/tools/shell/file_mutation_queue.rb', line 18

def initialize(operations: nil)
  @staged = []
  @operations = operations || DefaultEditOperations.new
end

Instance Method Details

#apply!Array<Hash>

Apply all staged mutations atomically. Reads all files, applies transforms, then writes all back. If any write fails, all written files are rolled back to original.

Returns:

  • (Array<Hash>)

    results with :path, :original_size, :new_size, :success

Raises:

  • (ApplyError)

    if any mutation fails (after rollback)



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ask/tools/shell/file_mutation_queue.rb', line 37

def apply!
  snapshots = []

  # Phase 1: read all files and apply transforms
  @staged.each do |entry|
    path = entry[:path]
    original = @operations.read_file(path)
    modified = entry[:block].call(original)
    snapshots << { path: path, original: original, modified: modified }
  end

  # Phase 2: write all files, track rollback info
  written = []
  begin
    snapshots.each do |s|
      @operations.write_file(s[:path], s[:modified])
      written << s[:path]
    end
  rescue => e
    # Rollback: restore originals for files we already wrote
    written.each do |path|
      snapshot = snapshots.find { |s| s[:path] == path }
      @operations.write_file(path, snapshot[:original]) if snapshot
    rescue => rb_e
      $stderr.puts "[FileMutationQueue] Rollback failed for #{path}: #{rb_e.message}"
    end
    raise ApplyError, "Mutation failed at #{e.class}: #{e.message}. Rolled back #{written.size} files."
  end

  snapshots.map do |s|
    { path: s[:path], original_size: s[:original].length,
      new_size: s[:modified].length, success: true }
  end
end

#clear!Object

Clear all staged mutations without applying.



73
74
75
# File 'lib/ask/tools/shell/file_mutation_queue.rb', line 73

def clear!
  @staged.clear
end

#sizeObject

Number of staged mutations.



78
79
80
# File 'lib/ask/tools/shell/file_mutation_queue.rb', line 78

def size
  @staged.size
end

#stage(path) {|content| ... } ⇒ Object

Stage a mutation for a file.

Parameters:

  • path (String)

    absolute path to the file

Yields:

  • (content)

    raw file content, returns modified content

Yield Parameters:

  • content (String)

    the original file content

Yield Returns:

  • (String)

    the modified content



28
29
30
# File 'lib/ask/tools/shell/file_mutation_queue.rb', line 28

def stage(path, &block)
  @staged << { path: File.expand_path(path), block: block }
end