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
-
.claim(staged) ⇒ Object
Creates the staged file before anyone writes to it.
-
.write(destination) ⇒ Object
Yields the path to build, and returns whatever the block returned.
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 |