file_pipeline 0.0.1
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 +7 -0
- data/LICENSE +21 -0
- data/lib/file_pipeline/errors/failed_modification_error.rb +23 -0
- data/lib/file_pipeline/errors/missing_version_file_error.rb +18 -0
- data/lib/file_pipeline/errors/source_directory_error.rb +18 -0
- data/lib/file_pipeline/errors/source_file_error.rb +26 -0
- data/lib/file_pipeline/errors.rb +12 -0
- data/lib/file_pipeline/file_operations/captured_data_tags.rb +16 -0
- data/lib/file_pipeline/file_operations/default_operations/exif_redaction.rb +52 -0
- data/lib/file_pipeline/file_operations/default_operations/exif_restoration.rb +56 -0
- data/lib/file_pipeline/file_operations/default_operations/ptiff_conversion.rb +48 -0
- data/lib/file_pipeline/file_operations/default_operations/scale.rb +70 -0
- data/lib/file_pipeline/file_operations/exif_manipulable.rb +90 -0
- data/lib/file_pipeline/file_operations/file_operation.rb +188 -0
- data/lib/file_pipeline/file_operations/log_data_parser.rb +140 -0
- data/lib/file_pipeline/file_operations/results.rb +109 -0
- data/lib/file_pipeline/file_operations.rb +17 -0
- data/lib/file_pipeline/pipeline.rb +104 -0
- data/lib/file_pipeline/versioned_file.rb +284 -0
- data/lib/file_pipeline.rb +92 -0
- metadata +303 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
require_relative 'file_pipeline/errors'
|
6
|
+
require_relative 'file_pipeline/file_operations'
|
7
|
+
require_relative 'file_pipeline/versioned_file'
|
8
|
+
require_relative 'file_pipeline/pipeline'
|
9
|
+
|
10
|
+
# Module that contains classes to build a file processing pipeline that applies
|
11
|
+
# a defined batch of file operations non-destructively to a VersionedFile.
|
12
|
+
module FilePipeline
|
13
|
+
# Constant for the defualt directory for file operations.
|
14
|
+
DEFAULT_DIR = 'file_pipeline/file_operations/default_operations'
|
15
|
+
|
16
|
+
# Adds _directory_ to the Array of #source_directories which will be
|
17
|
+
# searched for source files when loading file operations.
|
18
|
+
# _directory_ will be prepended. Therefore, directories will be searcherd
|
19
|
+
# in reverse order of them being added.
|
20
|
+
def self.<<(directory)
|
21
|
+
directory_path = File.expand_path directory
|
22
|
+
return source_directories if source_directories.include? directory_path
|
23
|
+
|
24
|
+
no_dir = !File.directory?(directory_path)
|
25
|
+
raise Errors::SourceDirectoryError, dir: directory if no_dir
|
26
|
+
|
27
|
+
@src_directories.prepend directory_path
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the constant for the <em>file_operation</em> class. If the
|
31
|
+
# constant is not defined, will try to require the source file.
|
32
|
+
def self.load(file_operation)
|
33
|
+
const = file_operation.split('_').map(&:capitalize).join
|
34
|
+
FilePipeline.load_file(file_operation) unless const_defined? const
|
35
|
+
const_get 'FileOperations::' + const
|
36
|
+
rescue NameError
|
37
|
+
# TODO: implement autogenerating module names from file_operation src path
|
38
|
+
const_get const
|
39
|
+
end
|
40
|
+
|
41
|
+
# Will search for <em>src_file</em> in .source_directories and require the
|
42
|
+
# file if.
|
43
|
+
def self.load_file(src_file)
|
44
|
+
src_file += '.rb' unless src_file.end_with? '.rb'
|
45
|
+
src_path = FilePipeline.source_path src_file
|
46
|
+
if src_path.nil?
|
47
|
+
raise Errors::SourceFileError,
|
48
|
+
file: src_file,
|
49
|
+
directories: FilePipeline.source_directories
|
50
|
+
end
|
51
|
+
require src_path
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a file basename consisting of either a timestamp or a UUID,
|
55
|
+
# depending on the _kind_ argument (+:timestamp+ or +:random+; default:
|
56
|
+
# +:timestamp+)
|
57
|
+
def self.new_basename(kind = :timestamp)
|
58
|
+
case kind
|
59
|
+
when :random
|
60
|
+
SecureRandom.uuid
|
61
|
+
when :timestamp
|
62
|
+
Time.now.strftime('%Y-%m-%dT%H:%M:%S.%N')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a String with the <em>/directory/filename</em>.
|
67
|
+
def self.path(dir, filename)
|
68
|
+
File.join dir, filename
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns an array of directory paths that may contain source files for
|
72
|
+
# file operation classes.
|
73
|
+
def self.source_directories
|
74
|
+
return @src_directories if @src_directories
|
75
|
+
|
76
|
+
@src_directories = [FilePipeline.path(__dir__, DEFAULT_DIR)]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Searches .source_directories and for _file_, and returns the full path
|
80
|
+
# (directory and filename) for the first match or nil if the file is
|
81
|
+
# nowhere found. Since directories are added in reverse order (see .<<)
|
82
|
+
# this will give redefinitions of file operations in custom directories
|
83
|
+
# precedence over the default directory, thus allowing overriding of file
|
84
|
+
# operation definitions.
|
85
|
+
def self.source_path(file)
|
86
|
+
FilePipeline.source_directories.each do |dir|
|
87
|
+
full_path = FilePipeline.path(dir, file)
|
88
|
+
return full_path if File.exist? full_path
|
89
|
+
end
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: file_pipeline
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Stein
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_exiftool
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.11.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.11.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-vips
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.14
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.0.14
|
41
|
+
description: "= file_pipeline\n\nThe <tt>file_pipeline</tt> gem provides a framework
|
42
|
+
for nondestructive \napplication of file operation batches to files.\n\n== Installation\n\n
|
43
|
+
\ gem install file_pipeline\n\n== Dependencies\n\nThe file operations included in
|
44
|
+
the gem require \n{ruby-vips}[https://github.com/libvips/ruby-vips] for image manipulation
|
45
|
+
and\n{multi_exiftool}[https://github.com/janfri/multi_exiftool] for image file\nmetdata
|
46
|
+
extraction and manipulation.\n\nWhile these dependencies should be installed automatically
|
47
|
+
with the gem, \nruby-vips depends on {libvips}[https://libvips.github.io/libvips/],
|
48
|
+
and\nmulti_exiftool depends on\n{Exiftool}[https://www.sno.phy.queensu.ca/~phil/exiftool/],
|
49
|
+
which will not be\ninstalled automatically.\n\n== Usage\n\nThe basic usage is to
|
50
|
+
create a new FilePipeline::Pipeline object and define any\nfile operations that
|
51
|
+
are to be performed, apply it to a \nFilePipeline::VersionedFile object initialized
|
52
|
+
with the image to be processed,\nthen finalize the versioned file.\n\n require
|
53
|
+
'file_pipeline'\n\n # create a new instance of Pipeline\n my_pipeline = FilePipeline::Pipeline.new\n\n
|
54
|
+
\ # configure an operation to scale an image to 1280 x 960 pixels\n my_pipeline.define_operation('scale',
|
55
|
+
:width => 1280, :height => 960)\n\n # create an instance of VersionedFile for the
|
56
|
+
file '~/image.jpg'\n image = FilePipeline::VersionedFile.new('~/image.jpg')\n\n
|
57
|
+
\ # apply the pipeline to the versioned file\n my_pipeline.apply_to(image)\n\n
|
58
|
+
\ # finalize the versioned file, replacing the original\n image.finalize(:overwrite
|
59
|
+
=> true)\n\n=== Setting up a Pipeline\n\nPipeline objects can be set up to contain
|
60
|
+
default file operations included in\nthe gem or with custom file operations (see\n{Custom
|
61
|
+
file operations}[rdoc-label:label-Custom+file+operations] for\ninstructions on how
|
62
|
+
to create custom operations).\n\n==== Basic set up with default operations\n\nTo
|
63
|
+
define an operation, pass the class name of the operation in underscore\nnotation
|
64
|
+
and without the containing module name, and any options to\n{#define_operation}[rdoc-ref:FilePipeline::Pipeline#define_operation].\n\nThe
|
65
|
+
example below adds an instance of \nPtiffConversion[rdoc-ref:FilePipeline::FileOperations::PtiffConversion]
|
66
|
+
with\nthe <tt>:tile_width</tt> and <tt>:tile_height</tt> options each set to 64\npixels.\n\n
|
67
|
+
\ my_pipeline = FilePipeline::Pipeline.new\n my_pipeline.define_operation('ptiff_conversion',\n
|
68
|
+
\ :tile_width => 64, :tile_height => 64)\n\nChaining
|
69
|
+
is possible\n\n my_pipeline = FilePipeline::Pipeline.new\n my_pipeline.define_operation('scale',
|
70
|
+
:width => 1280, :height => 1024)\n .define_operation('exif_restoration')\n\nAlternatively,
|
71
|
+
operations can be defined during initialization by passing a\nblock to {#new}[rdoc-ref:FilePipeline::Pipeline.new].\n\n
|
72
|
+
\ my_pipeline = FilePipeline::Pipeline.new do |pipeline|\n pipeline.define_operation('scale',
|
73
|
+
:width => 1280, :height => 1024)\n pipeline.define_operation('exif_restoration')\n
|
74
|
+
\ end\n\nWhen using the default operations included in the gem, it is sufficient
|
75
|
+
to\ncall <tt>#define_operation</tt> with the desired operations and options.\n\n====
|
76
|
+
Using custom file operations\n\nWhen file operations are to be used that are not
|
77
|
+
included in the gem, place\nthe source files for the class definitions in one or
|
78
|
+
more directories and\ninitialize the Pipeline object with the directory paths. The
|
79
|
+
directories will\nbe added to the {source directories}[rdoc-ref:FilePipeline.source_directories].\n\nDirectories
|
80
|
+
are added to the source directories in reverse order, so that\ndirectories added
|
81
|
+
later will have precedence when searching source files. The\ndefault operations
|
82
|
+
directory included in the gem will be searched last. This\nallows overriding of
|
83
|
+
operations without changing the code in existing classes.\n\nIf, for example, there
|
84
|
+
are two directories with custom file operation classes,\n<tt>'~/custom_operations'</tt>
|
85
|
+
and <tt>'~/other_operations'</tt>, the new\ninstance of Pipeline can be set up to
|
86
|
+
look for source files first in\n<tt>'~/other_operations'</tt>, then in <tt>'~/custom_operations'</tt>,
|
87
|
+
and\nfinally in the included default operations.\n\nThe basename for source files
|
88
|
+
_must_ be the class name in underscore notation\nwithout the containing module name.
|
89
|
+
If, for example, the operation is\n<tt>FileOperations::MyOperation</tt>, the source
|
90
|
+
file basename should be\n<tt>'my_operation.rb'</tt>\n\n my_pipeline = FilePipeline::Pipeline.new('~/custom_operations',\n
|
91
|
+
\ '~/other_operations')\n my_pipeline.define_operation('my_operation')\n\nSee
|
92
|
+
{Custom file operations}[rdoc-label:label-Custom+file+operations] for \ninstructions
|
93
|
+
for how to write file operations.\n\n=== Nondestructive application to files\n\nPipelines[rdoc-ref:FilePipeline::Pipeline]
|
94
|
+
work on \n{versioned files}[rdoc-ref:FilePipeline::VersionedFile], which allow for\nnon-destructive
|
95
|
+
application of all file operations.\n\nTo create a versioned file, initialize an
|
96
|
+
instance with the path to the \noriginal file:\n\n # create an instance of VersionedFile
|
97
|
+
for the file '~/image.jpg'\n image = FilePipeline::VersionedFile.new('~/image.jpg')\n\nAs
|
98
|
+
long as no operations have been applied, this will have no effect in the\nfile system.
|
99
|
+
Only when the first operation is applied will VersionedFile\ncreate a {working directory}[rdoc-ref:FilePipeline::VersionedFile#directory]
|
100
|
+
in\nthe same directory as the original file. The working directory will have the\nname
|
101
|
+
of the file basename without extension and the suffix <tt>'_versions'</tt>\nadded.\n\nPipelines
|
102
|
+
can be applied to a singe versioned file with the\nthe {#apply_to}[rdoc-ref:FilePipeline::Pipeline#apply_to]
|
103
|
+
method of the pipeline\ninstance, or to an array of versioned files with the\n{#batch_apply}[rdoc-ref:FilePipeline::Pipeline#batch_apply]
|
104
|
+
method of the \npipeline instance.\n\n=== Accessing file metadata and captured data.\n\n*Limitations*:
|
105
|
+
this currently only works for _Exif_ metadata of image files.\n\nVersionedFile provides
|
106
|
+
access to a files metadata via the\n{#metadata}[rdoc-ref:FilePipeline::VersionedFile#metadata]
|
107
|
+
method of the \nversioned file instance.\n\nBoth the metadata for the original file
|
108
|
+
and the current (latest) version can\nbe accessed:\n\n image = FilePipeline::VersionedFile.new('~/image.jpg')\n\n
|
109
|
+
\ # access the metadata for the current version\n image.metadata\n\nNote that if
|
110
|
+
no file operations have been applied by a pipeline object, this\nwill return the
|
111
|
+
metadata for the original, which in that case is the current\n(latest) version.\n\nTo
|
112
|
+
explicitly get the metadata for the original file even if there are newer\nversions
|
113
|
+
available, pass the <tt>:for_version</tt> option with the symbol\n<tt>:original</tt>:\n\n
|
114
|
+
\ # access the metadata for the original file\n image.metadata(:for_version =>
|
115
|
+
:original)\n\nSome file operations can comprise metadata; many image processing
|
116
|
+
libraries\nwill not preserve all _Exif_ tags and their values when converting images
|
117
|
+
to\na different format, but only write a small subset of tags to the file they\ncreate.
|
118
|
+
In these cases, the \n{ExifRestoration}[rdoc-ref:FilePipeline::FileOperations::ExifRestoration]\noperation
|
119
|
+
can be used to try to restore the tags that have been discarded, but\nit can not
|
120
|
+
write all tags. It will store all tags and their values that it could \nnot write
|
121
|
+
back to the file and return them as captured data.\n\nLikewise, if the \n{ExifRedaction}[rdoc-ref:FilePipeline::FileOperations::ExifRedaction]
|
122
|
+
is applied\nto delete sensitive tags (e.g. GPS location data), it will return all
|
123
|
+
deleted \nexif tags and their values as captured data.\n\nThe\n{#recovered_metadata}[rdoc-ref:FilePipeline::VersionedFile#recovered_metadata]\nof
|
124
|
+
the versioned file instance will return a hash with all metadata that was\ndeleted
|
125
|
+
or could not be restored by operations:\n\n delete_tags = ['CreatorTool', 'Software']\n\n
|
126
|
+
\ my_pipeline = FilePipeline::Pipeline.new do |pipeline|\n pipeline.define_operation('scale',
|
127
|
+
width: 1280, height: 1024)\n pipeline.define_operation('exif_restoration')\n
|
128
|
+
\ Pipeline.define_operation('exif_redaction', :redact_tags => delete_tags)\n end\n\n
|
129
|
+
\ image = FilePipeline::VersionedFile.new('~/image.jpg')\n my_pipeline.apply_to(image)\n\n
|
130
|
+
\ image.recovered_metadata\n\nFor information on retrieving other kinds of captured
|
131
|
+
data, refer to\nthe versioned file instance methods\n{#captured_data}[rdoc-ref:FilePipeline::VersionedFile#captured_data],\n{#captured_data_for}[rdoc-ref:FilePipeline::VersionedFile#captured_data_for],
|
132
|
+
\nand\n{#captured_data_with}[rdoc-ref:FilePipeline::VersionedFile#captured_data_with].\n\n===
|
133
|
+
Finalizing files\n\nOnce all file operations of a pipeline object have been applied
|
134
|
+
to a\nversioned file object, it can be finalized by calling the\n{#finalize}[rdoc-ref:FilePipeline::VersionedFile#finalize]
|
135
|
+
method of the\ninstance.\n\nFinalization will write the current version to the same
|
136
|
+
directory that\ncontains the original. It will by default preserve the original
|
137
|
+
by adding\na suffix to the basename of the final version. If the <tt>:overwrite</tt>\noption
|
138
|
+
for the method is passed with +true+, it will delete the original and\nwrite the
|
139
|
+
final version to the same basename as the original.\n\n image = FilePipeline::VersionedFile.new('~/image.jpg')\n\n
|
140
|
+
\ # finalize the versioned file, preserving the original\n image.finalize\n\n #
|
141
|
+
finalize the versioned file, replacing the original\n image.finalize(:overwrite
|
142
|
+
=> true)\n\nThe work directory with all other versions will be deleted after the
|
143
|
+
final\nversion has been written.\n\n== Custom file operations\n\n=== Module nesting\n\nFile
|
144
|
+
operation classes _must_ be defined in the FilePipeline::FileOperations\nmodule
|
145
|
+
for {automatic requiring}[rdoc-ref:FilePipeline.load] of source files to\nwork.\n\n===
|
146
|
+
Implementing from scratch\n\n==== Initializer\n\nThe <tt>#initialize</tt> method
|
147
|
+
_must_ take an +options+ argument (a hash\nwith a default value, or a <em>double
|
148
|
+
splat</em>) and _must_ be exposed\nthrough an <tt>#options</tt> getter method.\n\nThe
|
149
|
+
options passed can be any for the file operation to properly configure\na specific
|
150
|
+
instance of a method.\n\nThis requirement is imposed by the \n{#define_operation}[rdoc-ref:FilePipeline::Pipeline#define_operation]
|
151
|
+
instance\nmethod of Pipeline, which will automatically load and initialize an instance
|
152
|
+
of\nthe file operation with any options provided as a hash.\n\n===== Examples\n\n
|
153
|
+
\ class MyOperation\n attr_reader :options\n\n # initializer with a default\n
|
154
|
+
\ def initialize(options = {})\n @options = options\n end\n end\n\n class
|
155
|
+
MyOperation\n attr_reader :options\n\n # initializer with a double splat\n
|
156
|
+
\ def initialize(**options)\n @options = options\n end\n end\n\nConsider
|
157
|
+
a file operation +CopyrightNotice+ that whill add copyright\ninformation to an image
|
158
|
+
file's _Exif_ metadata, the value for the copyright\ntag could be passed as an option.\n\n
|
159
|
+
copyright_notice = CopyrightNotice.new(:copyright => 'The Photographer')\n\n====
|
160
|
+
The <tt>run</tt> method\n\nFile operations _must_ implement a <tt>#run</tt> method
|
161
|
+
that takes three\narguments (or a _splat_) in order to be used in a Pipeline.\n\n=====
|
162
|
+
Arguments\n\nThe three arguments required for implementations of <tt>#run</tt> are:\n*
|
163
|
+
the path to the <em>file to be modified</em>\n* the path to the _directory_ to which
|
164
|
+
new files will be saved.\n* the path to the <em>original file</em>, from which the
|
165
|
+
first version in a\n succession of modified versions has been created.\n\nThe <em>original
|
166
|
+
file</em> will only be used by file operations that require\nit for reference, e.g.
|
167
|
+
to restore file metadata that was compromised by\nother file operations.\n\n=====
|
168
|
+
Return value\n\nThe method _must_ return the path to the file that was created by
|
169
|
+
the\noperation (perferrably in the _directory_). It _may_ also return a \n{Results}[rdoc-ref:FilePipeline::FileOperations::Results]
|
170
|
+
object, containing the\noperation itself, a _success_ flag (+true+ or +false+),
|
171
|
+
and any logs or data\nreturned by the operation.\n\nIf results are returned with
|
172
|
+
the path to the created file, both values must\nbe wrapped in an array, with the
|
173
|
+
path as the first element, the results as\nthe second.\n\n===== Example\n\n def
|
174
|
+
run(src_file, directory, original)\n # make a path to which the created file
|
175
|
+
will be written\n out_file = File.join(directory, 'new_file_name.extension')\n\n
|
176
|
+
\ # create a Results object reporting success with no logs or data\n results
|
177
|
+
= Results.new(self, true, nil)\n\n # create a new out_file based on src_file
|
178
|
+
in directory\n # ...\n\n # return the path to the new file and the results
|
179
|
+
object\n [out_file, results]\n end\n\n==== Captured data tags\n\nCaptured data
|
180
|
+
tags can be used to\n{filter captured data}[rdoc-ref:FilePipeline::VersionedFile#captured_data_with]
|
181
|
+
\naccumulated during successive file operations.\n\nOperations that return data
|
182
|
+
as part of the results _should_ respond to\n<tt>:captured_data_tag</tt> and return
|
183
|
+
one of the \n{tag constants}[rdoc-ref:FilePipeline::FileOperations::CapturedDataTags].\n\n=====
|
184
|
+
Example\n\n # returns NO_DATA\n def captured_data_tag\n CapturedDataTags::NO_DATA\n
|
185
|
+
\ end\n\n=== Subclassing FileOperation\n\nThe {FileOperation}[rdoc-ref:FilePipeline::FileOperations::FileOperation]
|
186
|
+
class\nis an abstract superclass that provides a scaffold to facilitate the creation
|
187
|
+
of\nfile operations that conform to the requirements.\n\nIt implements a \n{#run}[rdoc-ref:FilePipeline::FileOperations::FileOperation#run]
|
188
|
+
method, that\ntakes the required three arguments and returns the path to the newly
|
189
|
+
created\nfile and a Results object.\n\nWhen the operation was successful,\n{success}[rdoc-ref:FilePipeline::FileOperations::Results#success]
|
190
|
+
will be\n+true+. When an exception was raised, that exeption will be rescued and
|
191
|
+
returned\nas the {log}[rdoc-ref:FilePipeline::FileOperations::Results#log], and\n{success}[rdoc-ref:FilePipeline::FileOperations::Results#success]
|
192
|
+
will be \n+false+.\n\nThe standard <tt>#run</tt> method of the FileOperation class
|
193
|
+
does not contain\nlogic to perform the actual file operation, but will call an \n{#operation
|
194
|
+
method}[rdoc-label:label-The+operation+method] that _must_ be\ndefined in the subclass
|
195
|
+
unless the subclass overrides the <tt>#run</tt> method.\n\nThe <tt>#run</tt> method
|
196
|
+
will generate the new path that is passed to the\n<tt>#operation</tt> method, and
|
197
|
+
to which the latter will write the new\nversion of the file. The new file path will
|
198
|
+
need an appropriate file type\nextension. The default behavior is to assume that
|
199
|
+
the extension will be the\nsame as for the file that was passed in as the basis
|
200
|
+
from which the new\nversion will be created. If the operation will result in a different
|
201
|
+
file\ntype, the subclass _should_ define a <tt>#target_extension</tt> method that\nreturns
|
202
|
+
the appropriate file extension (see\n{Target file extensions}[rdoc-label:label-Target+file+extensions]).\n\n====
|
203
|
+
Initializer\n\nThe +initialize+ method _must_ take an +options+ argument (a hash
|
204
|
+
with a\ndefault value or a <em>double splat</em>).\n\n===== Options and defaults\n\nThe
|
205
|
+
initializer can call +super+ and pass the +options+ hash and any\ndefaults (a hash
|
206
|
+
with default options). This will update the defaults with\nthe actual options passed
|
207
|
+
to +initialize+ and assign them to the\n{#options}[rdoc-ref:FilePipeline::FileOperations::FileOperation#options]
|
208
|
+
\nattribute.\n\nIf the initializer does not call +super+, it _must_ assign the options
|
209
|
+
to\nthe <tt>@options</tt> instance variable or expose them through an\n<tt>#options</tt>
|
210
|
+
getter method.\n\nIf it calls +super+ but must ensure some options are always set
|
211
|
+
to a\nspecific value, those should be set after the call to +super+.\n\n===== Examples\n\n
|
212
|
+
\ # initializer without defaults callings super\n def initialize(**options)\n super(options)\n
|
213
|
+
\ end\n\n # initializer with defaults calling super\n def initialize(**options)\n
|
214
|
+
\ defaults = { :option_a => true, :option_b => false }\n super(options, defaults)\n
|
215
|
+
\ end\n\n # initializer with defaults calling super, ensures :option_c => true\n
|
216
|
+
\ def initialize(**options)\n defaults = { :option_a => true, :option_b => false
|
217
|
+
}\n super(options, defaults)\n @options[:option_c] = true\n end\n\n # initilizer
|
218
|
+
that does not call super\n def initialize(**options)\n @options = options\n
|
219
|
+
\ end\n\n==== The <tt>operation</tt> method\n\nThe <tt>#operation</tt> method contains
|
220
|
+
the logic specific to a given\nsubclass of FileOperation and must be defined in
|
221
|
+
that subclass unless the\n<tt>#run</tt> method is overwritten.\n\n===== Arguments\n\nThe
|
222
|
+
<tt>#operation</tt> method must accept three arguments:\n\n* the path to the <em>file
|
223
|
+
to be modified</em>\n* the path for the <em>file to be created</em> by the operation.\n*
|
224
|
+
the path to the <em>original file</em>, from which the first version in a\n succession
|
225
|
+
of modified versions has been created.\n\nThe <em>original file</em> will only be
|
226
|
+
used by file operations that require\nit for reference, e.g. to restore file metadata
|
227
|
+
that was compromised by\nother file operations.\n\n===== Return Value\n\nThe method
|
228
|
+
_can_ return anything that can be interpreted by\n{LogDataParser}[rdoc-ref:FilePipeline::FileOperations::LogDataParser],\nincluding
|
229
|
+
nothing.\n\nIt will usually return any log outpout that the logic of <tt>#operation</tt>\nhas
|
230
|
+
generated, and/or data captured. If data is captured that is to be used\nlater,
|
231
|
+
the subclass should override the <tt>#captured_data_tag</tt> method to\nreturn the
|
232
|
+
appropriate \n{tag constant}[rdoc-ref:FilePipeline::FileOperations::CapturedDataTags].\n\n=====
|
233
|
+
Examples\n\n # creates out_file based on src_file, captures metadata differences\n
|
234
|
+
\ # between out_file and original, returns log messages and captured data\n def
|
235
|
+
operation(src_file, out_file, original)\n captured_data = {}\n log_messages
|
236
|
+
= []\n\n # write the new version based on src_file to out_file\n # compare
|
237
|
+
metadata of out_file with original, store any differences\n # in captures_data
|
238
|
+
and append any log messages to log_messages\n\n [log_messages, captured_data]\n
|
239
|
+
\ end\n\n # takes the third argument for the original file but does not use it\n
|
240
|
+
\ # creates out_file based on src_file, returns log messages\n def operation(src_file,
|
241
|
+
out_file, _)\n src_file, out_file = args\n log_messages = []\n\n # write
|
242
|
+
the new version based on src_file to out_file\n\n log_messages\n end\n\n #
|
243
|
+
takes arguments as a splat and destructures them to avoid having the\n # unused
|
244
|
+
thirs argumen\n # creates out_file based on src_file, returns nothing\n def operation(*args)\n
|
245
|
+
\ src_file, out_file = args\n\n # write the new version based on src_file to
|
246
|
+
out_file\n\n return\n end\n\n==== Target file extensions\n\nIf the file that
|
247
|
+
the operation creates is of a different type than the file\nthe version is based
|
248
|
+
upon, the class _must_ define the\n<tt>#target_extension</tt> method that returns
|
249
|
+
the appropriate file type\nextension.\n\nIn most cases, the resulting file type
|
250
|
+
will be predictable (static), and in\nsuch cases, the method can just return a string
|
251
|
+
with the extension.\n\nAn alternative would be to provide the expected extension
|
252
|
+
as an #option\nto the initializer.\n\n===== Examples\n\n # returns always '.tiff.\n
|
253
|
+
\ def target_extension\n '.tiff'\n end\n\n # returns the extension specified
|
254
|
+
in #options +:extension+\n # my_operation = MyOperation(:extension => '.dng')\n
|
255
|
+
\ def target_extension\n options[:extension]\n end\n"
|
256
|
+
email: loveablelobster@fastmail.fm
|
257
|
+
executables: []
|
258
|
+
extensions: []
|
259
|
+
extra_rdoc_files: []
|
260
|
+
files:
|
261
|
+
- LICENSE
|
262
|
+
- lib/file_pipeline.rb
|
263
|
+
- lib/file_pipeline/errors.rb
|
264
|
+
- lib/file_pipeline/errors/failed_modification_error.rb
|
265
|
+
- lib/file_pipeline/errors/missing_version_file_error.rb
|
266
|
+
- lib/file_pipeline/errors/source_directory_error.rb
|
267
|
+
- lib/file_pipeline/errors/source_file_error.rb
|
268
|
+
- lib/file_pipeline/file_operations.rb
|
269
|
+
- lib/file_pipeline/file_operations/captured_data_tags.rb
|
270
|
+
- lib/file_pipeline/file_operations/default_operations/exif_redaction.rb
|
271
|
+
- lib/file_pipeline/file_operations/default_operations/exif_restoration.rb
|
272
|
+
- lib/file_pipeline/file_operations/default_operations/ptiff_conversion.rb
|
273
|
+
- lib/file_pipeline/file_operations/default_operations/scale.rb
|
274
|
+
- lib/file_pipeline/file_operations/exif_manipulable.rb
|
275
|
+
- lib/file_pipeline/file_operations/file_operation.rb
|
276
|
+
- lib/file_pipeline/file_operations/log_data_parser.rb
|
277
|
+
- lib/file_pipeline/file_operations/results.rb
|
278
|
+
- lib/file_pipeline/pipeline.rb
|
279
|
+
- lib/file_pipeline/versioned_file.rb
|
280
|
+
homepage: https://github.com/loveablelobster/file_pipeline
|
281
|
+
licenses:
|
282
|
+
- MIT
|
283
|
+
metadata: {}
|
284
|
+
post_install_message:
|
285
|
+
rdoc_options: []
|
286
|
+
require_paths:
|
287
|
+
- lib
|
288
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - ">="
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0'
|
293
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
294
|
+
requirements:
|
295
|
+
- - ">="
|
296
|
+
- !ruby/object:Gem::Version
|
297
|
+
version: '0'
|
298
|
+
requirements: []
|
299
|
+
rubygems_version: 3.0.3
|
300
|
+
signing_key:
|
301
|
+
specification_version: 4
|
302
|
+
summary: Nondestructive file processing with a defined batch
|
303
|
+
test_files: []
|