file_pipeline 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|