file_pipeline 0.0.7 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.rdoc +40 -19
- data/lib/file_pipeline.rb +6 -5
- data/lib/file_pipeline/errors.rb +1 -0
- data/lib/file_pipeline/errors/failed_modification_error.rb +6 -6
- data/lib/file_pipeline/errors/misplaced_version_file_error.rb +30 -0
- data/lib/file_pipeline/file_operations/default_operations/exif_recovery.rb +53 -0
- data/lib/file_pipeline/file_operations/default_operations/exif_restoration.rb +9 -9
- data/lib/file_pipeline/file_operations/default_operations/ptiff_conversion.rb +1 -1
- data/lib/file_pipeline/file_operations/exif_manipulable.rb +5 -1
- data/lib/file_pipeline/file_operations/file_operation.rb +11 -2
- data/lib/file_pipeline/pipeline.rb +12 -5
- data/lib/file_pipeline/versioned_file.rb +18 -28
- data/lib/file_pipeline/versions/validator.rb +85 -0
- metadata +14 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac388f4340b5d6327f4f83318e151654a1c1c15d9500a8fa1d2e9fcd1509c186
|
4
|
+
data.tar.gz: 2375865b7dc7d58334a81cbbb9df11f1b521480d5e803a2d550d04d7ef3f69d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5a85862095d53802662177be37c0c147cf93a7861d6e3c2abb9eb080697f94fd63f3a605685da546b58bef781eb0e2322aa7efd61a9d9dbe70ccbeab47e659
|
7
|
+
data.tar.gz: f0380bd511501e8460526d54fd7cafe00d29d7404da642d721284eb3fde797e2e46077ec071a477848c8f992f1bbf9d9f49dcacee3dff52146fef6251d0a51a8
|
data/README.rdoc
CHANGED
@@ -248,8 +248,7 @@ The <tt>#initialize</tt> method _must_ take an +options+ argument (a hash
|
|
248
248
|
with a default value, or a <em>double splat</em>) and _must_ be exposed
|
249
249
|
through an <tt>#options</tt> getter method.
|
250
250
|
|
251
|
-
The options passed can be any
|
252
|
-
a specific instance of a method.
|
251
|
+
The options passed can be any to properly configure an instance of the class.
|
253
252
|
|
254
253
|
This requirement is imposed by the
|
255
254
|
{#define_operation}[rdoc-ref:FilePipeline::Pipeline#define_operation] instance
|
@@ -296,20 +295,26 @@ The three arguments required for implementations of <tt>#run</tt> are:
|
|
296
295
|
succession of modified versions has been created.
|
297
296
|
|
298
297
|
The <em>original file</em> will only be used by file operations that require
|
299
|
-
it for reference, e.g. to restore file metadata that was compromised
|
300
|
-
other file operations.
|
298
|
+
it for reference, e.g. to restore or recover file metadata that was compromised
|
299
|
+
by other file operations.
|
301
300
|
|
302
301
|
===== Return value
|
303
302
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
303
|
+
If the operation modifies the file (i.e. creates a new version), the +run+
|
304
|
+
method _must_ return the path to the file that was created (perferrably in the
|
305
|
+
_directory_). If it does not modify and no results are returned, it _must_
|
306
|
+
return +nil+.
|
307
|
+
|
308
|
+
The method _may_ return a
|
309
|
+
{Results}[rdoc-ref:FilePipeline::FileOperations::Results] object along with the
|
310
|
+
path or +nil+. The results object should contain the operation itself, a
|
311
|
+
_success_ flag (+true+ or +false+), and any logs or data returned by the
|
312
|
+
operation.
|
309
313
|
|
310
314
|
If results are returned with the path to the created file, both values must
|
311
315
|
be wrapped in an array, with the path as the first element, the results as
|
312
|
-
the second.
|
316
|
+
the second. If the operation does not modify and therefore not return a path,
|
317
|
+
the first element of the array must be +nil+.
|
313
318
|
|
314
319
|
===== Example
|
315
320
|
|
@@ -367,15 +372,21 @@ logic to perform the actual file operation, but will call an
|
|
367
372
|
{#operation method}[rdoc-label:label-The+operation+method] that _must_ be
|
368
373
|
defined in the subclass unless the subclass overrides the <tt>#run</tt> method.
|
369
374
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
extension. The default behavior is to
|
374
|
-
same as for the file that was passed in as
|
375
|
-
version will be created. If the operation will
|
376
|
-
type, the subclass _should_ define a
|
377
|
-
returns the appropriate file extension
|
378
|
-
{Target file extensions}[rdoc-label:label-Target+file+extensions]).
|
375
|
+
If the operation is modifying (creates a new version), the <tt>#run</tt> method
|
376
|
+
will generate the new path that is passed to the <tt>#operation</tt> method,
|
377
|
+
and to which the latter will write the new version of the file. The new file
|
378
|
+
path will need an appropriate file type extension. The default behavior is to
|
379
|
+
assume that the extension will be the same as for the file that was passed in as
|
380
|
+
the basis from which the new version will be created. If the operation will
|
381
|
+
result in a different file type, the subclass _should_ define a
|
382
|
+
<tt>#target_extension</tt> method that returns the appropriate file extension
|
383
|
+
(see {Target file extensions}[rdoc-label:label-Target+file+extensions]).
|
384
|
+
|
385
|
+
Subclasses of FileOperation are by default modifying. If the operation is not
|
386
|
+
modifying (does not create a new version of the file), the subclass _must_
|
387
|
+
override the <tt>#modiies?</tt> method or override the <tt>#run</tt> method to
|
388
|
+
ensure it does not return a file path (see
|
389
|
+
{Non-modifying operations}[rdoc-label:label-Non-modifying+operations]).
|
379
390
|
|
380
391
|
==== Initializer
|
381
392
|
|
@@ -490,6 +501,16 @@ return the appropriate
|
|
490
501
|
return
|
491
502
|
end
|
492
503
|
|
504
|
+
==== Non-modifying operations
|
505
|
+
|
506
|
+
If the operation will not create a new version, the class _must_ redefine the
|
507
|
+
<tt>#modifies?</tt> method to return +false+:
|
508
|
+
|
509
|
+
# non-modiyfing operation
|
510
|
+
def modifies?
|
511
|
+
false
|
512
|
+
end
|
513
|
+
|
493
514
|
==== Target file extensions
|
494
515
|
|
495
516
|
If the file that the operation creates is of a different type than the file
|
data/lib/file_pipeline.rb
CHANGED
@@ -23,7 +23,7 @@ module FilePipeline
|
|
23
23
|
return source_directories if source_directories.include? directory_path
|
24
24
|
|
25
25
|
no_dir = !File.directory?(directory_path)
|
26
|
-
raise Errors::SourceDirectoryError
|
26
|
+
raise Errors::SourceDirectoryError.new dir: directory if no_dir
|
27
27
|
|
28
28
|
@src_directories.prepend directory_path
|
29
29
|
end
|
@@ -33,7 +33,7 @@ module FilePipeline
|
|
33
33
|
def self.load(file_operation)
|
34
34
|
const = file_operation.split('_').map(&:capitalize).join
|
35
35
|
FilePipeline.load_file(file_operation) unless const_defined? const
|
36
|
-
const_get
|
36
|
+
const_get "FileOperations::#{const}"
|
37
37
|
rescue NameError
|
38
38
|
# TODO: implement autogenerating module names from file_operation src path
|
39
39
|
const_get const
|
@@ -45,9 +45,10 @@ module FilePipeline
|
|
45
45
|
src_file += '.rb' unless src_file.end_with? '.rb'
|
46
46
|
src_path = FilePipeline.source_path src_file
|
47
47
|
if src_path.nil?
|
48
|
-
raise Errors::SourceFileError
|
49
|
-
|
50
|
-
|
48
|
+
raise Errors::SourceFileError.new(
|
49
|
+
file: src_file,
|
50
|
+
directories: FilePipeline.source_directories
|
51
|
+
)
|
51
52
|
end
|
52
53
|
require src_path
|
53
54
|
end
|
data/lib/file_pipeline/errors.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'errors/failed_modification_error'
|
4
|
+
require_relative 'errors/misplaced_version_file_error'
|
4
5
|
require_relative 'errors/missing_version_file_error'
|
5
6
|
require_relative 'errors/source_directory_error'
|
6
7
|
require_relative 'errors/source_file_error'
|
@@ -12,7 +12,7 @@ module FilePipeline
|
|
12
12
|
#
|
13
13
|
# ===== Arguments
|
14
14
|
#
|
15
|
-
# * +msg+ - error message for the exception. If none provided, the
|
15
|
+
# * +msg+ - error message for the exception. If none provided, the
|
16
16
|
# instance will be initialized with the #default_message.
|
17
17
|
#
|
18
18
|
# ===== Options
|
@@ -38,15 +38,15 @@ module FilePipeline
|
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
# Appends the backtrace of the error that caused the exception to the
|
41
|
+
# Appends the backtrace of the error that caused the exception to the
|
42
42
|
# #default_message.
|
43
43
|
def append_backtrace(str)
|
44
|
-
return str
|
44
|
+
return "#{str}\n" unless original_backtrace
|
45
45
|
|
46
|
-
str
|
46
|
+
"#{str} Backtrace:\n#{original_backtrace}"
|
47
47
|
end
|
48
48
|
|
49
|
-
# Appends the message of the error that caused the exception to the
|
49
|
+
# Appends the message of the error that caused the exception to the
|
50
50
|
# #default_message.
|
51
51
|
def append_error(str)
|
52
52
|
return str unless original_error
|
@@ -58,7 +58,7 @@ module FilePipeline
|
|
58
58
|
|
59
59
|
# Returns a String with the #message for +self+.
|
60
60
|
def default_message
|
61
|
-
if info
|
61
|
+
if info.respond_to?(:operation) && info.respond_to?(:log)
|
62
62
|
msg = "#{info.operation&.name} with options"\
|
63
63
|
" #{info.operation&.options} failed on #{@file}."
|
64
64
|
append_error msg
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FilePipeline
|
4
|
+
module Errors
|
5
|
+
# Error class for exceptions that are raised when a new version is added,
|
6
|
+
# but the file is not in the VersionedFile's working directory.
|
7
|
+
class MisplacedVersionFileError < StandardError
|
8
|
+
# Path for of the misplaced file for the version.
|
9
|
+
attr_reader :file
|
10
|
+
|
11
|
+
# Path for the directory where the file should have been (the
|
12
|
+
# VersionedFile's working directory).
|
13
|
+
attr_reader :directory
|
14
|
+
|
15
|
+
def initialize(msg = nil, file: nil, directory: nil)
|
16
|
+
@file = file
|
17
|
+
@directory = directory
|
18
|
+
msg ||= default_message
|
19
|
+
super msg
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def default_message
|
25
|
+
"File #{File.basename @file} was expected in #{@directory},"\
|
26
|
+
" but was in #{File.dirname @file}."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FilePipeline
|
4
|
+
module FileOperations
|
5
|
+
# A non-modifying FileOperation that compares a file's _Exif_ Metadata with
|
6
|
+
# that of a reference file and returns tags missing in the working file as
|
7
|
+
# captured data.
|
8
|
+
#
|
9
|
+
# Used to recover _Exif_ tags that were not preserved during e.g. a file
|
10
|
+
# conversion.
|
11
|
+
class ExifRecovery < FileOperation
|
12
|
+
include ExifManipulable
|
13
|
+
|
14
|
+
# :args: options
|
15
|
+
#
|
16
|
+
# Returns a new instance.
|
17
|
+
#
|
18
|
+
# ===== Options
|
19
|
+
#
|
20
|
+
# * <tt>skip_tags</tt> - _Exif_ tags to be ignored during comparison.
|
21
|
+
#
|
22
|
+
# The ExifManipulable mixin defines a set of _Exif_
|
23
|
+
# {tags}[rdoc-ref:FilePipeline::FileOperations::ExifManipulable.file_tags]
|
24
|
+
# that will always be ignored.
|
25
|
+
def initialize(**opts)
|
26
|
+
defaults = { skip_tags: [] }
|
27
|
+
super(opts, defaults)
|
28
|
+
@options[:skip_tags] += ExifManipulable.file_tags
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the DROPPED_EXIF_DATA tag defined in CapturedDataTags.
|
32
|
+
#
|
33
|
+
# Instances of ExifRecovery will capture any _Exif_ tags and their values
|
34
|
+
# that are present in the reference file but missing in the working file.
|
35
|
+
def captured_data_tag
|
36
|
+
CapturedDataTags::DROPPED_EXIF_DATA
|
37
|
+
end
|
38
|
+
|
39
|
+
# Instances of ExifRecovery do not modify the working file.
|
40
|
+
def modifies?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
# Compares the _Exif_ metadata of <tt>src_file</tt> with that of
|
45
|
+
# +original+ and returns all tags that are present in +original+ but
|
46
|
+
# missing in <tt>src_file</tt>.
|
47
|
+
def operation(src_file, _, original)
|
48
|
+
original_exif, src_file_exif = read_exif original, src_file
|
49
|
+
missing_exif_fields(src_file_exif, original_exif)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
module FilePipeline
|
4
4
|
module FileOperations
|
5
|
-
# A FileOperation that compares Exif Metadata
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# A modifying FileOperation that compares a file's Exif Metadata with that
|
6
|
+
# of a reference file and attempts to copy tags missing in the working file
|
7
|
+
# from the reference file.
|
8
|
+
#
|
9
|
+
# Used to restore Exif tags that were not preserved during e.g. a file
|
10
|
+
# conversion.
|
8
11
|
#
|
9
12
|
# *Caveat:* if this operation is applied to a file together with
|
10
13
|
# ExifRedaction, it should be applied _before_ the latter, to avoid
|
@@ -20,10 +23,9 @@ module FilePipeline
|
|
20
23
|
#
|
21
24
|
# * <tt>skip_tags</tt> - _Exif_ tags to be ignored during restoration.
|
22
25
|
#
|
23
|
-
# The ExifManipulable mixin defines a set of _Exif_
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# such as file format conversions.
|
26
|
+
# The ExifManipulable mixin defines a set of _Exif_
|
27
|
+
# {tags}[rdoc-ref:FilePipeline::FileOperations::ExifManipulable.file_tags]
|
28
|
+
# that will always be ignored.
|
27
29
|
def initialize(**opts)
|
28
30
|
defaults = { skip_tags: [] }
|
29
31
|
super(opts, defaults)
|
@@ -38,8 +40,6 @@ module FilePipeline
|
|
38
40
|
CapturedDataTags::DROPPED_EXIF_DATA
|
39
41
|
end
|
40
42
|
|
41
|
-
# :args: src_file, out_file
|
42
|
-
#
|
43
43
|
# Writes a new version of <tt>src_file</tt> to <tt>out_file</tt> with all
|
44
44
|
# writable _Exif_ tags from +original+ restored.
|
45
45
|
#
|
@@ -9,6 +9,10 @@ module FilePipeline
|
|
9
9
|
# Returns an Array of tags to be ignored during comparison. These can
|
10
10
|
# be merged with an ExifManipulable including FileOperation's options
|
11
11
|
# to skip tags (e.g. the <tt>skip_tags</tt> option in ExifRestoration).
|
12
|
+
#
|
13
|
+
# The included tags relate to the file properties (e.g. filesize,
|
14
|
+
# MIME-type) that will have been altered by any prior operation, such as
|
15
|
+
# file format conversions.
|
12
16
|
def self.file_tags
|
13
17
|
%w[FileSize FileModifyDate FileAccessDate FileInodeChangeDate
|
14
18
|
FilePermissions FileType FileTypeExtension MIMEType]
|
@@ -20,7 +24,7 @@ module FilePipeline
|
|
20
24
|
end
|
21
25
|
|
22
26
|
def self.strip_path(str) # :nodoc:
|
23
|
-
str.sub(%r{ -
|
27
|
+
str.sub(%r{ - /?(/|[-:.]+|\w+)+\.\w+$}, '')
|
24
28
|
end
|
25
29
|
|
26
30
|
# Redacts (deletes) all <tt>tags_to_delete</tt> in <tt>out_file</tt>.
|
@@ -18,6 +18,9 @@ module FilePipeline
|
|
18
18
|
# that is passed to #run or #operation as <tt>src_file</tt>, the subclass
|
19
19
|
# must have a #target_extension method that returns the appropriate
|
20
20
|
# extension.
|
21
|
+
#
|
22
|
+
# If the operation is non-modifying, the subclass must redefine the
|
23
|
+
# #modifies? methods to return +false+.
|
21
24
|
class FileOperation
|
22
25
|
# A Hash; any options used when performing #operation.
|
23
26
|
attr_reader :options
|
@@ -62,6 +65,12 @@ module FilePipeline
|
|
62
65
|
results false, log_data
|
63
66
|
end
|
64
67
|
|
68
|
+
# Returns +true+ if the FIleOperation will create a new version.
|
69
|
+
# _Default:_ +true+.
|
70
|
+
def modifies?
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
65
74
|
# Returns the class name (string) of +self+ _without_ the names of the
|
66
75
|
# modules that the class is nested in.
|
67
76
|
def name
|
@@ -133,11 +142,11 @@ module FilePipeline
|
|
133
142
|
# e.g. when exif metadata tags missing in the <tt>src_file</tt> are to
|
134
143
|
# be copied over from another file.
|
135
144
|
def run(src_file, directory, original = nil)
|
136
|
-
out_file = target directory, extension(src_file)
|
145
|
+
out_file = target directory, extension(src_file) if modifies?
|
137
146
|
log_data = operation src_file, out_file, original
|
138
147
|
[out_file, success(log_data)]
|
139
148
|
rescue StandardError => e
|
140
|
-
FileUtils.rm out_file if File.exist?
|
149
|
+
FileUtils.rm out_file if out_file && File.exist?(out_file)
|
141
150
|
[out_file, failure(e)]
|
142
151
|
end
|
143
152
|
|
@@ -41,10 +41,7 @@ module FilePipeline
|
|
41
41
|
# Adds a file operation object #file_operations. The object must implement
|
42
42
|
# a _run_ method (see FileOperations::FileOperation#run for details).
|
43
43
|
def <<(file_operation_instance)
|
44
|
-
|
45
|
-
raise TypeError, 'File operations must implement a #run method'
|
46
|
-
end
|
47
|
-
|
44
|
+
validate file_operation_instance
|
48
45
|
@file_operations << file_operation_instance
|
49
46
|
end
|
50
47
|
|
@@ -88,7 +85,7 @@ module FilePipeline
|
|
88
85
|
#
|
89
86
|
def define_operation(file_operation, options = {})
|
90
87
|
operation = FilePipeline.load file_operation
|
91
|
-
self << operation.new(options)
|
88
|
+
self << operation.new(**options)
|
92
89
|
self
|
93
90
|
end
|
94
91
|
|
@@ -106,5 +103,15 @@ module FilePipeline
|
|
106
103
|
operation.run version, directory, original
|
107
104
|
end
|
108
105
|
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Raises TypeError if <tt>file_operation_instance</tt> does not implement a
|
110
|
+
# #run method.
|
111
|
+
def validate(file_operation_instance)
|
112
|
+
return file_operation_instance if file_operation_instance.respond_to? :run
|
113
|
+
|
114
|
+
raise TypeError, 'File operations must implement a #run method'
|
115
|
+
end
|
109
116
|
end
|
110
117
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'versions/validator'
|
3
4
|
module FilePipeline
|
4
5
|
# VersionedFile creates a directory where it stores any versions of _file_.
|
5
6
|
class VersionedFile
|
@@ -81,14 +82,10 @@ module FilePipeline
|
|
81
82
|
# <tt>version_info</tt> must be a path to an existing file or an array with
|
82
83
|
# the path and optionally a FileOperations::Results instance:
|
83
84
|
# <tt>['path/to/file', results_object]</tt>.
|
84
|
-
# Will
|
85
|
+
# Will raise MisplacedVersionFileError if it is in another directory.
|
85
86
|
def <<(version_info)
|
86
|
-
|
87
|
-
|
88
|
-
raise Errors::FailedModificationError, info: info, file: original
|
89
|
-
end
|
90
|
-
|
91
|
-
version = validate(file)
|
87
|
+
version, info = Versions::Validator[version_info, self]
|
88
|
+
version ||= @current
|
92
89
|
@history[version] = info
|
93
90
|
self
|
94
91
|
rescue StandardError => e
|
@@ -144,7 +141,9 @@ module FilePipeline
|
|
144
141
|
# * +true+ - The finalized version will replace the #original.
|
145
142
|
def finalize(overwrite: false)
|
146
143
|
yield(self) if block_given?
|
147
|
-
|
144
|
+
return original unless changed?
|
145
|
+
|
146
|
+
filename = overwrite ? replacing_target : preserving_target
|
148
147
|
FileUtils.rm original if overwrite
|
149
148
|
@original = Versions.copy(current, original_dir, filename)
|
150
149
|
ensure
|
@@ -165,10 +164,12 @@ module FilePipeline
|
|
165
164
|
# than exif.
|
166
165
|
#++
|
167
166
|
def metadata(for_version: :current)
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
167
|
+
file = case for_version
|
168
|
+
when :current, :original
|
169
|
+
public_send for_version
|
170
|
+
else
|
171
|
+
for_version
|
172
|
+
end
|
172
173
|
read_exif(file).first
|
173
174
|
end
|
174
175
|
|
@@ -198,6 +199,7 @@ module FilePipeline
|
|
198
199
|
# FileOperations::CapturedDataTags::DROPPED_EXIF_DATA has been merged.
|
199
200
|
def recovered_metadata
|
200
201
|
return unless changed?
|
202
|
+
|
201
203
|
captured_data_with(FileOperations::CapturedDataTags::DROPPED_EXIF_DATA)
|
202
204
|
&.reduce({}) { |recovered, data| recovered.merge data }
|
203
205
|
end
|
@@ -208,13 +210,13 @@ module FilePipeline
|
|
208
210
|
|
209
211
|
# Returns the filename for a target file that will not overwrite the
|
210
212
|
# original.
|
211
|
-
def
|
212
|
-
basename
|
213
|
+
def preserving_target
|
214
|
+
"#{basename}_#{target_suffix}#{current_extension}"
|
213
215
|
end
|
214
216
|
|
215
217
|
# Returns the filename for a target file that will overwrite the
|
216
218
|
# original.
|
217
|
-
def
|
219
|
+
def replacing_target
|
218
220
|
basename + current_extension
|
219
221
|
end
|
220
222
|
|
@@ -224,25 +226,13 @@ module FilePipeline
|
|
224
226
|
history.clear!
|
225
227
|
end
|
226
228
|
|
227
|
-
# Validates if file exists and has been stored in #directory.
|
228
|
-
def validate(file)
|
229
|
-
return current unless file
|
230
|
-
|
231
|
-
raise Errors::MissingVersionFileError, file: file unless File.exist? file
|
232
|
-
|
233
|
-
return file if File.dirname(file) == directory
|
234
|
-
|
235
|
-
Versions.move file, directory, File.basename(file)
|
236
|
-
end
|
237
|
-
|
238
229
|
# Creates the directory containing all version files. Directory name is
|
239
230
|
# composed of the basename plus '_version'.
|
240
231
|
#
|
241
232
|
# Raises SystemCallError if the directory already exists.
|
242
233
|
def workdir
|
243
|
-
subdir = basename + '_versions'
|
244
234
|
filedir = File.dirname(original)
|
245
|
-
dirname = File.join filedir,
|
235
|
+
dirname = File.join filedir, "#{basename}_versions"
|
246
236
|
FileUtils.mkdir(dirname)
|
247
237
|
File.path dirname
|
248
238
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FilePipeline
|
4
|
+
module Versions
|
5
|
+
# Validator objects verify the version file and results returned by a
|
6
|
+
# FileOperation.
|
7
|
+
#
|
8
|
+
# They will validate:
|
9
|
+
# - that the version file existst
|
10
|
+
# - that it is in the correct directory
|
11
|
+
# - that the file operation has not returned any failures
|
12
|
+
class Validator
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
# File for the version that resulted from a FileOperation.
|
16
|
+
attr_reader :file
|
17
|
+
|
18
|
+
# FileOperation::Results object.
|
19
|
+
attr_reader :info
|
20
|
+
|
21
|
+
# Returns a new instance.
|
22
|
+
#
|
23
|
+
# ===== Arguments
|
24
|
+
#
|
25
|
+
# * <tt>version_info</tt> - path to an existing file or an array with the
|
26
|
+
# path and optionally a FileOperations::Results instance.
|
27
|
+
# * +directory+ - directory where the file is expected (the working
|
28
|
+
# directory of a VersionedFile).
|
29
|
+
# * +filename+ - name of the file to be returned if the file operation was
|
30
|
+
# was non-modifying (usually the VersionedFile#original).
|
31
|
+
def initialize(version_info, directory, filename)
|
32
|
+
@file, @info = [version_info].flatten
|
33
|
+
@directory = directory
|
34
|
+
@filename = filename
|
35
|
+
end
|
36
|
+
|
37
|
+
# Validates file, directory, and info for <tt>version_info</tt> in the
|
38
|
+
# context of <tt>versioned_file</tt>.
|
39
|
+
#
|
40
|
+
# ===== Arguments
|
41
|
+
#
|
42
|
+
# * <tt>version_info</tt> - path to an existing file or an array with the
|
43
|
+
# path and optionally a FileOperations::Results instance.
|
44
|
+
# * <tt>versioned_file</tt> - an object that responds to #original and
|
45
|
+
# returns a file path, and #directory and returns a directory path.
|
46
|
+
def self.[](version_info, versioned_file)
|
47
|
+
new(version_info, versioned_file.directory, versioned_file.original)
|
48
|
+
.validate_info
|
49
|
+
.validate_file
|
50
|
+
.validate_directory
|
51
|
+
.then { |validator| [validator.file, validator.info] }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns +true+ when there is no file for the version (result of a
|
55
|
+
# non-modifying file operation), +false+ otherwise.
|
56
|
+
def unmodified?
|
57
|
+
@file.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Raises MisplacedVersionFileError if #file is not in #directory.
|
61
|
+
def validate_directory
|
62
|
+
return self if unmodified? || File.dirname(@file) == @directory
|
63
|
+
|
64
|
+
raise Errors::MisplacedVersionFileError.new file: @file,
|
65
|
+
directory: @directory
|
66
|
+
end
|
67
|
+
|
68
|
+
# Raises MissingVersionFileError if #file does not exist on the file
|
69
|
+
# system.
|
70
|
+
def validate_file
|
71
|
+
return self if unmodified? || File.exist?(@file)
|
72
|
+
|
73
|
+
raise Errors::MissingVersionFileError.new file: @file
|
74
|
+
end
|
75
|
+
|
76
|
+
# Raises FailedModificationError if the file operation generatint the
|
77
|
+
# #info failed.
|
78
|
+
def validate_info
|
79
|
+
return self unless @info&.failure
|
80
|
+
|
81
|
+
raise Errors::FailedModificationError.new info: @info, file: @filename
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_pipeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Stein
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_exiftool
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.16.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.16.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: ruby-vips
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
33
|
+
version: '2.1'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.
|
40
|
+
version: '2.1'
|
41
41
|
description: The file_pipeline gem provides a framework for nondestructive application
|
42
42
|
of file operation batches to files.
|
43
43
|
email: loveablelobster@fastmail.fm
|
@@ -50,11 +50,13 @@ files:
|
|
50
50
|
- lib/file_pipeline.rb
|
51
51
|
- lib/file_pipeline/errors.rb
|
52
52
|
- lib/file_pipeline/errors/failed_modification_error.rb
|
53
|
+
- lib/file_pipeline/errors/misplaced_version_file_error.rb
|
53
54
|
- lib/file_pipeline/errors/missing_version_file_error.rb
|
54
55
|
- lib/file_pipeline/errors/source_directory_error.rb
|
55
56
|
- lib/file_pipeline/errors/source_file_error.rb
|
56
57
|
- lib/file_pipeline/file_operations.rb
|
57
58
|
- lib/file_pipeline/file_operations/captured_data_tags.rb
|
59
|
+
- lib/file_pipeline/file_operations/default_operations/exif_recovery.rb
|
58
60
|
- lib/file_pipeline/file_operations/default_operations/exif_redaction.rb
|
59
61
|
- lib/file_pipeline/file_operations/default_operations/exif_restoration.rb
|
60
62
|
- lib/file_pipeline/file_operations/default_operations/ptiff_conversion.rb
|
@@ -67,11 +69,12 @@ files:
|
|
67
69
|
- lib/file_pipeline/versioned_file.rb
|
68
70
|
- lib/file_pipeline/versions.rb
|
69
71
|
- lib/file_pipeline/versions/history.rb
|
72
|
+
- lib/file_pipeline/versions/validator.rb
|
70
73
|
homepage: https://github.com/loveablelobster/file_pipeline
|
71
74
|
licenses:
|
72
75
|
- MIT
|
73
76
|
metadata: {}
|
74
|
-
post_install_message:
|
77
|
+
post_install_message:
|
75
78
|
rdoc_options: []
|
76
79
|
require_paths:
|
77
80
|
- lib
|
@@ -79,15 +82,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
82
|
requirements:
|
80
83
|
- - ">="
|
81
84
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
85
|
+
version: '3.0'
|
83
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
87
|
requirements:
|
85
88
|
- - ">="
|
86
89
|
- !ruby/object:Gem::Version
|
87
90
|
version: '0'
|
88
91
|
requirements: []
|
89
|
-
rubygems_version: 3.
|
90
|
-
signing_key:
|
92
|
+
rubygems_version: 3.2.15
|
93
|
+
signing_key:
|
91
94
|
specification_version: 4
|
92
95
|
summary: Nondestructive file processing with a defined batch
|
93
96
|
test_files: []
|