Class: Retouch::Batch

Inherits:
Object
  • Object
show all
Defined in:
lib/retouch/batch.rb

Class Method Summary collapse

Class Method Details

.expand(inputs) ⇒ Object



5
6
7
8
9
10
11
12
13
14
# File 'lib/retouch/batch.rb', line 5

def self.expand(inputs)
  Array(inputs).flat_map do |input|
    pattern = String(input)
    if pattern.match?(/[?*{\[]/)
      Dir.glob(pattern)
    else
      pattern
    end
  end.sort
end

.output_path(template, input, index) ⇒ Object

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
# File 'lib/retouch/batch.rb', line 16

def self.output_path(template, input, index)
  path = String(template).gsub("{name}", File.basename(input, File.extname(input)))
                         .gsub("{ext}", File.extname(input).delete_prefix("."))
                         .gsub("{dir}", File.dirname(input))
                         .gsub(/\{index(?::(\d+))?\}/) { format("%0*d", Regexp.last_match(1).to_i, index) }
  raise ArgumentError, "unknown output template token in #{path}" if path.match?(/\{[^}]+\}/)

  path
end

.run(inputs, to:, jobs: 1, force: false, dry_run: false) ⇒ Object

Raises:



26
27
28
29
30
31
32
33
34
35
36
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/retouch/batch.rb', line 26

def self.run(inputs, to:, jobs: 1, force: false, dry_run: false)
  paths = expand(inputs)
  raise Error, "no input files matched" if paths.empty?
  raise Error, "input file does not exist: #{paths.find { |path| !File.file?(path) }}" unless paths.all? { |path| File.file?(path) }

  outputs = paths.each_with_index.map { |path, index| output_path(to, path, index) }
  resolved_outputs = outputs.map { |path| destination_key(path) }
  raise Error, "output template produces duplicate paths" unless resolved_outputs.uniq.length == outputs.length

  jobs = Integer(jobs)
  raise ArgumentError, "jobs must be positive" unless jobs.positive?
  if !force && (existing = outputs.find { |path| File.exist?(path) })
    raise Error, "refusing to overwrite #{existing}; use --force"
  end
  return outputs if dry_run

  worker_count = Process.respond_to?(:fork) ? jobs : 1
  children = []
  failed = false
  paths.zip(outputs).each do |input, output|
    FileUtils.mkdir_p(File.dirname(output)) unless File.dirname(output) == "."
    if worker_count > 1
      children << Process.fork do
        yield input, output
        exit! 0
      rescue StandardError => e
        warn("retouch: #{e.message}")
        exit! 1
      end
      if children.length >= worker_count
        _pid, status = Process.wait2(children.shift)
        failed ||= !status.success?
      end
    else
      yield input, output
    end
  end
  children.each do |pid|
    _child, status = Process.wait2(pid)
    failed ||= !status.success?
  end
  raise Error, "one or more batch jobs failed" if failed

  outputs
end