Module: Vivlio::PDF::StagedFile

Defined in:
lib/vivlio/pdf/staged_file.rb

Overview

Builds a file beside where it belongs and moves it into place only once it is finished, so a run that fails part way through never leaves a half-written file where the caller expects a complete one.

Class Method Summary collapse

Class Method Details

.claim(staged) ⇒ Object

Creates the staged file before anyone writes to it. The staged name is predictable, so in a world-writable output directory someone could plant a symlink there and have the PDF bytes written through it into a file of their choosing. Deleting whatever is at the name (unlinking a symlink cannot touch its target) and then creating with EXCL closes that: if something reappears in between, the build fails instead of writing through it. The deletion also clears a stale .part left behind by a killed run.



32
33
34
35
36
37
# File 'lib/vivlio/pdf/staged_file.rb', line 32

def claim(staged)
  FileUtils.rm_f(staged)
  File.open(staged, File::WRONLY | File::CREAT | File::EXCL) {} # rubocop:disable Lint/EmptyBlock
rescue Errno::EEXIST
  raise Error, "staged file reappeared while claiming it: #{staged}"
end

.write(destination) ⇒ Object

Yields the path to build, and returns whatever the block returned.



14
15
16
17
18
19
20
21
22
# File 'lib/vivlio/pdf/staged_file.rb', line 14

def write(destination)
  staged = "#{destination}.part"
  claim(staged)
  result = yield staged
  File.rename(staged, destination)
  result
ensure
  FileUtils.rm_f(staged)
end