class FilePipeline::VersionedFile

VersionedFile creates a directory where it stores any versions of file.

Attributes

basename[R]

The basename of the versioned file.

history[R]

A hash with file paths as keys, information on the modifications applied to create the version as values (instances of FileOperations::Results).

original[R]

The path to the original file of self.

target_suffix[R]

A String that is appended to the file basename when the file written by finalize is not replacing the original.

Public Class Methods

new(file, target_suffix: SecureRandom.hex(4)) click to toggle source

Returns a new instance with file as the original.

Arguments
  • file - Path to the file the instance will be based on. That file should not be touched unless finalize is called with the :overwrite option set to true.

Caveat it can not be ruled out that buggy or malignant file operations modify the original.

Options

<tt>target_suffix</ttm> is a string to be appended to the file that will be written by finalize (the last version) if finalize is to preserve the original. It is recommended to use a randomized string (default) to avoid clashes with other files in the directory.

# File lib/file_pipeline/versioned_file.rb, line 69
def initialize(file, target_suffix: SecureRandom.hex(4))
  raise Errors::MissingVersionFileError, file: file unless File.exist? file

  @original = file
  @basename = File.basename(file, '.*')
  @history = Versions::History.new
  @directory = nil
  @target_suffix = target_suffix
  history[original] = nil
end

Public Instance Methods

<<(version_info) click to toggle source

Adds a new version to history and returns self.

version_info must be a path to an existing file or an array with the path and optionally a FileOperations::Results instance: ['path/to/file', results_object]. Will raise MisplacedVersionFileError if it is in another directory.

# File lib/file_pipeline/versioned_file.rb, line 86
def <<(version_info)
  version, info = Versions::Validator[version_info, self]
  version ||= @current
  @history[version] = info
  self
rescue StandardError => e
  reset
  raise e
end
changed?() click to toggle source

Returns true if there are versions (file has been modified).

Warning: It will also return true if the file has been cloned.

# File lib/file_pipeline/versioned_file.rb, line 99
def changed?
  current != original
end
clone() click to toggle source

Creates a new identical version of current. Will only add the path of the file to history, but no FileOperations::Results.

# File lib/file_pipeline/versioned_file.rb, line 105
def clone
  filename = FilePipeline.new_basename + current_extension
  clone_file = Versions.copy(current, directory, filename)
  self << clone_file
end
Also aliased as: touch
current() click to toggle source

Returns the path to the current file or the original if no versions have been created.

# File lib/file_pipeline/versioned_file.rb, line 113
def current
  versions.last || original
end
current_extension() click to toggle source

Returns the file extension for the current file.

# File lib/file_pipeline/versioned_file.rb, line 118
def current_extension
  File.extname current
end
directory() click to toggle source

Returns the path to the directory where the versioned of self are stored. Creates the directory if it does not exist.

# File lib/file_pipeline/versioned_file.rb, line 124
def directory
  @directory ||= workdir
end
finalize(overwrite: false) { |self| ... } click to toggle source

Writes the current version to basename, optionally the target_suffix, and the current_extension in original_dir. Deletes all versions and resets the history to an empty Hash. Returns the path to the written file.

If the optional block is passed, it will be evaluated before the file is finalized.

Options
  • overwrite - true or false

# File lib/file_pipeline/versioned_file.rb, line 142
def finalize(overwrite: false)
  yield(self) if block_given?
  return original unless changed?

  filename = overwrite ? replacing_target : preserving_target
  FileUtils.rm original if overwrite
  @original = Versions.copy(current, original_dir, filename)
ensure
  reset
end
metadata(for_version: :current) click to toggle source

Returns the Exif metadata

Options
  • :for_version - current or original

    • current (default) - Metadata for the current file will be returned.

    • original - Metadata for the original file will be returned.

# File lib/file_pipeline/versioned_file.rb, line 166
def metadata(for_version: :current)
  file = case for_version
         when :current, :original
           public_send for_version
         else
           for_version
         end
  read_exif(file).first
end
modify() { |current, directory, original| ... } click to toggle source

Creates a new version. Requires a block that must return a path to an existing file or an array with the path and optionally a FileOperations::Results instance: ['path/to/file', results_object].

The actual file modification logic will be in the block.

The block must take three arguments: for the current file (from which the modified version will be created), the work directory (to where the modified file will be written), and the original file (which will only be used in modifications that need the original file for reference, such as modifications that restore file metadata that was lost in other modifications).

# File lib/file_pipeline/versioned_file.rb, line 189
def modify
  self << yield(current, directory, original)
end
original_dir() click to toggle source

Returns the directory where original is stored.

# File lib/file_pipeline/versioned_file.rb, line 194
def original_dir
  File.dirname original
end
recovered_metadata() click to toggle source

Returns a hash into which all captured data from file operations with the FileOperations::CapturedDataTags::DROPPED_EXIF_DATA has been merged.

# File lib/file_pipeline/versioned_file.rb, line 200
def recovered_metadata
  return unless changed?

  captured_data_with(FileOperations::CapturedDataTags::DROPPED_EXIF_DATA)
    &.reduce({}) { |recovered, data| recovered.merge data }
end
touch()
Alias for: clone

Private Instance Methods

preserving_target() click to toggle source

Returns the filename for a target file that will not overwrite the original.

# File lib/file_pipeline/versioned_file.rb, line 213
def preserving_target
  "#{basename}_#{target_suffix}#{current_extension}"
end
replacing_target() click to toggle source

Returns the filename for a target file that will overwrite the original.

# File lib/file_pipeline/versioned_file.rb, line 219
def replacing_target
  basename + current_extension
end
reset() click to toggle source

Deletes the work directory and resets versions

# File lib/file_pipeline/versioned_file.rb, line 224
def reset
  FileUtils.rm_r directory, force: true
  history.clear!
end
workdir() click to toggle source

Creates the directory containing all version files. Directory name is composed of the basename plus '_version'.

Raises SystemCallError if the directory already exists.

# File lib/file_pipeline/versioned_file.rb, line 233
def workdir
  filedir = File.dirname(original)
  dirname = File.join filedir, "#{basename}_versions"
  FileUtils.mkdir(dirname)
  File.path dirname
end