Class: FilePipeline::VersionedFile
- Inherits:
-
Object
- Object
- FilePipeline::VersionedFile
- Includes:
- FileOperations::ExifManipulable
- Defined in:
- lib/file_pipeline/versioned_file.rb
Overview
VersionedFile creates a directory where it stores any versions of file.
Instance Attribute Summary collapse
-
#basename ⇒ Object
readonly
The basename of the versioned file.
-
#history ⇒ Object
readonly
A hash with file paths as keys, information on the modifications applied to create the version as values (instances of FileOperations::Results).
-
#original ⇒ Object
readonly
The path to the original file of self.
-
#target_suffix ⇒ Object
readonly
A String that is appended to the file basename when the file written by #finalize is not replacing the original.
Class Method Summary collapse
-
.copy(src, dir, filename) ⇒ Object
Copies the file with path src to /dir/filename.
-
.move(src, dir, filename) ⇒ Object
Moves the file with path src to /dir/filename.
Instance Method Summary collapse
-
#<<(version_info) ⇒ Object
Adds a new version to #history and returns self.
-
#captured_data ⇒ Object
Returns a two-dimesnional array, where each nested array has two items; the file operation object and data captured by the operartion (if any).
-
#captured_data_for(operation_name, **options) ⇒ Object
Returns any data captured by
operation_name. -
#captured_data_with(tag) ⇒ Object
Returns an array with all data captured by operations with
taghas. -
#changed? ⇒ Boolean
Returns
trueif there are #versions (file has been modified). -
#clone ⇒ Object
(also: #touch)
Creates a new identical version of #current.
-
#current ⇒ Object
Returns the path to the current file or the #original if no versions have been created.
-
#current_extension ⇒ Object
Returns the file extension for the #current file.
-
#directory ⇒ Object
Returns the path to the directory where the versioned of
selfare stored. -
#finalize(overwrite: false) ⇒ Object
Writes the #current version to #basename, optionally the #target_suffix, and the #current_extension in #original_dir.
-
#initialize(file, target_suffix: SecureRandom.uuid) ⇒ VersionedFile
constructor
Returns a new instance with
fileas the #original. -
#log ⇒ Object
Returns an array of triplets (arryas with three items each): the name of the file operation class (a string), options (a hash), and the actual log (an array).
-
#metadata(for_version: :current) ⇒ Object
Returns the Exif metadata.
-
#modify ⇒ Object
Creates a new version.
-
#original_dir ⇒ Object
Returns the directory where #original is stored.
-
#recovered_metadata ⇒ Object
Returns a hash into which all captured data from file operations with the FileOperations::CapturedDataTags::DROPPED_EXIF_DATA has been merged.
-
#versions ⇒ Object
Returns an array with paths to the version files of
self(excluding #original).
Methods included from FileOperations::ExifManipulable
#delete_tags, file_tags, #missing_exif_fields, #parse_exif_errors, parse_tag_error, #read_exif, strip_path, #write_exif
Constructor Details
#initialize(file, target_suffix: SecureRandom.uuid) ⇒ VersionedFile
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:overwriteoption set totrue.
Caveat it can not be ruled out that buggy or malignant file operations modify the original.
– FIXME: protect 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 UUID (default) to avoid clashes with other files in the directory.
43 44 45 46 47 48 49 50 51 |
# File 'lib/file_pipeline/versioned_file.rb', line 43 def initialize(file, target_suffix: SecureRandom.uuid) raise Errors::MissingVersionFileError, file: file unless File.exist? file @original = file @basename = File.basename(file, '.*') @history = {} @directory = nil @target_suffix = target_suffix end |
Instance Attribute Details
#basename ⇒ Object (readonly)
The basename of the versioned file.
9 10 11 |
# File 'lib/file_pipeline/versioned_file.rb', line 9 def basename @basename end |
#history ⇒ Object (readonly)
A hash with file paths as keys, information on the modifications applied to create the version as values (instances of FileOperations::Results).
13 14 15 |
# File 'lib/file_pipeline/versioned_file.rb', line 13 def history @history end |
#original ⇒ Object (readonly)
The path to the original file of self.
16 17 18 |
# File 'lib/file_pipeline/versioned_file.rb', line 16 def original @original end |
#target_suffix ⇒ Object (readonly)
A String that is appended to the file basename when the file written by #finalize is not replacing the original.
20 21 22 |
# File 'lib/file_pipeline/versioned_file.rb', line 20 def target_suffix @target_suffix end |
Class Method Details
.copy(src, dir, filename) ⇒ Object
Copies the file with path src to /dir/filename.
54 55 56 57 58 |
# File 'lib/file_pipeline/versioned_file.rb', line 54 def self.copy(src, dir, filename) dest = FilePipeline.path(dir, filename) FileUtils.cp src, dest dest end |
.move(src, dir, filename) ⇒ Object
Moves the file with path src to /dir/filename.
61 62 63 64 65 |
# File 'lib/file_pipeline/versioned_file.rb', line 61 def self.move(src, dir, filename) dest = FilePipeline.path(dir, filename) FileUtils.mv src, dest dest end |
Instance Method Details
#<<(version_info) ⇒ Object
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 move the file to #directory if it is in another directory.
73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/file_pipeline/versioned_file.rb', line 73 def <<(version_info) file, info = version_info raise Errors::FailedModificationError, info: info if info&.failure version = validate(file) @history[version] = info self rescue StandardError => e reset raise e end |
#captured_data ⇒ Object
Returns a two-dimesnional array, where each nested array has two items; the file operation object and data captured by the operartion (if any).
[[description_object, data_or_nil], ...]
89 90 91 |
# File 'lib/file_pipeline/versioned_file.rb', line 89 def captured_data filter_history :data end |
#captured_data_for(operation_name, **options) ⇒ Object
Returns any data captured by operation_name.
If multiple instances of one operation class have modified the file, pass any options the specific instance of the operation was initialized with as the optional second argument.
98 99 100 101 102 103 104 |
# File 'lib/file_pipeline/versioned_file.rb', line 98 def captured_data_for(operation_name, **) raw_data = captured_data.filter do |operation, _| operation.name == operation_name && .all? { |k, v| operation.[k] == v } end raw_data.map(&:last) end |
#captured_data_with(tag) ⇒ Object
Returns an array with all data captured by operations with tag has.
Tags are defined in FileOperations::CapturedDataTags
109 110 111 112 113 114 115 116 117 |
# File 'lib/file_pipeline/versioned_file.rb', line 109 def captured_data_with(tag) return unless changed? captured_data.map do |operation, results| next unless operation.captured_data_tag == tag results end end |
#changed? ⇒ Boolean
Returns true if there are #versions (file has been modified).
Warning: It will also return true if the file has been cloned.
122 123 124 |
# File 'lib/file_pipeline/versioned_file.rb', line 122 def changed? current != original end |
#clone ⇒ Object Also known as: touch
Creates a new identical version of #current. Will only add the path of the file to history, but no FileOperations::Results.
128 129 130 131 132 |
# File 'lib/file_pipeline/versioned_file.rb', line 128 def clone filename = FilePipeline.new_basename + current_extension clone_file = VersionedFile.copy(current, directory, filename) self << clone_file end |
#current ⇒ Object
Returns the path to the current file or the #original if no versions have been created.
136 137 138 |
# File 'lib/file_pipeline/versioned_file.rb', line 136 def current versions.last || original end |
#current_extension ⇒ Object
Returns the file extension for the #current file.
141 142 143 |
# File 'lib/file_pipeline/versioned_file.rb', line 141 def current_extension File.extname current end |
#directory ⇒ Object
Returns the path to the directory where the versioned of self are stored. Creates the directory if it does not exist.
147 148 149 |
# File 'lib/file_pipeline/versioned_file.rb', line 147 def directory @directory ||= workdir end |
#finalize(overwrite: false) ⇒ Object
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.
Options
-
overwrite-trueorfalse-
false(default) - The #target_suffix will be appended to the #basename and the #original will be preserved. -
true- The finalized version will replace the #original.
-
162 163 164 165 166 167 168 |
# File 'lib/file_pipeline/versioned_file.rb', line 162 def finalize(overwrite: false) filename = overwrite ? replacing_trarget : preserving_taget FileUtils.rm original if overwrite @original = VersionedFile.copy(current, original_dir, filename) ensure reset end |
#log ⇒ Object
Returns an array of triplets (arryas with three items each): the name of the file operation class (a string), options (a hash), and the actual log (an array).
173 174 175 176 |
# File 'lib/file_pipeline/versioned_file.rb', line 173 def log filter_history(:log) .map { |operation, info| [operation.name, operation., info] } end |
#metadata(for_version: :current) ⇒ Object
Returns the Exif metadata
Options
-
:for_version-currentororiginal-
current(default) - Metadata for the #current file will be returned. -
original- Metadata for the #original file will be returned.
-
– TODO: when file is not an image file, this should return other metadata than exif. TODO: implement the option to return metadata for a specif version index ++
192 193 194 195 |
# File 'lib/file_pipeline/versioned_file.rb', line 192 def (for_version: :current) file = public_send for_version read_exif(file).first end |
#modify ⇒ Object
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).
210 211 212 |
# File 'lib/file_pipeline/versioned_file.rb', line 210 def modify self << yield(current, directory, original) end |
#original_dir ⇒ Object
Returns the directory where #original is stored.
215 216 217 |
# File 'lib/file_pipeline/versioned_file.rb', line 215 def original_dir File.dirname original end |
#recovered_metadata ⇒ Object
Returns a hash into which all captured data from file operations with the FileOperations::CapturedDataTags::DROPPED_EXIF_DATA has been merged.
221 222 223 224 |
# File 'lib/file_pipeline/versioned_file.rb', line 221 def captured_data_with(FileOperations::CapturedDataTags::DROPPED_EXIF_DATA) &.reduce({}) { |recovered, data| recovered.merge data } end |
#versions ⇒ Object
Returns an array with paths to the version files of self (excluding #original).
228 229 230 |
# File 'lib/file_pipeline/versioned_file.rb', line 228 def versions history.keys end |