file_pipeline 0.0.1 → 0.0.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 +513 -0
- metadata +4 -216
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cda1036fc126999807374257bd750430e152b18e401af8a0acb8087a47e92951
|
4
|
+
data.tar.gz: b9acc95e3f3a4cdbdd70d61ebbb1a9bd8c41614cd390455f12ecc18156ceb586
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bb410c9ed6ec2d7a1d9eaa57012d1f48521311f930ab3ffd96c2ce249498d0e7cc164a9676fd0d6f66b3060d89fec7a1347680eaf1107c9433ea899ad5615e5
|
7
|
+
data.tar.gz: 41bd9a73cfc94b6c378a21d2eee38cf6badbd59ab29c55faa2a3668a43db01dd0cf5e31e9cd431d2492bc1d854900140844e77014ed0fbfd5bd92bc7d4848100
|
data/README.rdoc
ADDED
@@ -0,0 +1,513 @@
|
|
1
|
+
= file_pipeline
|
2
|
+
|
3
|
+
The <tt>file_pipeline</tt> gem provides a framework for nondestructive
|
4
|
+
application of file operation batches to files.
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
gem install file_pipeline
|
9
|
+
|
10
|
+
== Dependencies
|
11
|
+
|
12
|
+
The file operations included in the gem require
|
13
|
+
{ruby-vips}[https://github.com/libvips/ruby-vips] for image manipulation and
|
14
|
+
{multi_exiftool}[https://github.com/janfri/multi_exiftool] for image file
|
15
|
+
metdata extraction and manipulation.
|
16
|
+
|
17
|
+
While these dependencies should be installed automatically with the gem,
|
18
|
+
ruby-vips depends on {libvips}[https://libvips.github.io/libvips/], and
|
19
|
+
multi_exiftool depends on
|
20
|
+
{Exiftool}[https://www.sno.phy.queensu.ca/~phil/exiftool/], which will not be
|
21
|
+
installed automatically.
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
The basic usage is to create a new FilePipeline::Pipeline object and define any
|
26
|
+
file operations that are to be performed, apply it to a
|
27
|
+
FilePipeline::VersionedFile object initialized with the image to be processed,
|
28
|
+
then finalize the versioned file.
|
29
|
+
|
30
|
+
require 'file_pipeline'
|
31
|
+
|
32
|
+
# create a new instance of Pipeline
|
33
|
+
my_pipeline = FilePipeline::Pipeline.new
|
34
|
+
|
35
|
+
# configure an operation to scale an image to 1280 x 960 pixels
|
36
|
+
my_pipeline.define_operation('scale', :width => 1280, :height => 960)
|
37
|
+
|
38
|
+
# create an instance of VersionedFile for the file '~/image.jpg'
|
39
|
+
image = FilePipeline::VersionedFile.new('~/image.jpg')
|
40
|
+
|
41
|
+
# apply the pipeline to the versioned file
|
42
|
+
my_pipeline.apply_to(image)
|
43
|
+
|
44
|
+
# finalize the versioned file, replacing the original
|
45
|
+
image.finalize(:overwrite => true)
|
46
|
+
|
47
|
+
=== Setting up a Pipeline
|
48
|
+
|
49
|
+
Pipeline objects can be set up to contain default file operations included in
|
50
|
+
the gem or with custom file operations (see
|
51
|
+
{Custom file operations}[rdoc-label:label-Custom+file+operations] for
|
52
|
+
instructions on how to create custom operations).
|
53
|
+
|
54
|
+
==== Basic set up with default operations
|
55
|
+
|
56
|
+
To define an operation, pass the class name of the operation in underscore
|
57
|
+
notation and without the containing module name, and any options to
|
58
|
+
{#define_operation}[rdoc-ref:FilePipeline::Pipeline#define_operation].
|
59
|
+
|
60
|
+
The example below adds an instance of
|
61
|
+
PtiffConversion[rdoc-ref:FilePipeline::FileOperations::PtiffConversion] with
|
62
|
+
the <tt>:tile_width</tt> and <tt>:tile_height</tt> options each set to 64
|
63
|
+
pixels.
|
64
|
+
|
65
|
+
my_pipeline = FilePipeline::Pipeline.new
|
66
|
+
my_pipeline.define_operation('ptiff_conversion',
|
67
|
+
:tile_width => 64, :tile_height => 64)
|
68
|
+
|
69
|
+
Chaining is possible
|
70
|
+
|
71
|
+
my_pipeline = FilePipeline::Pipeline.new
|
72
|
+
my_pipeline.define_operation('scale', :width => 1280, :height => 1024)
|
73
|
+
.define_operation('exif_restoration')
|
74
|
+
|
75
|
+
Alternatively, operations can be defined during initialization by passing a
|
76
|
+
block to {#new}[rdoc-ref:FilePipeline::Pipeline.new].
|
77
|
+
|
78
|
+
my_pipeline = FilePipeline::Pipeline.new do |pipeline|
|
79
|
+
pipeline.define_operation('scale', :width => 1280, :height => 1024)
|
80
|
+
pipeline.define_operation('exif_restoration')
|
81
|
+
end
|
82
|
+
|
83
|
+
When using the default operations included in the gem, it is sufficient to
|
84
|
+
call <tt>#define_operation</tt> with the desired operations and options.
|
85
|
+
|
86
|
+
==== Using custom file operations
|
87
|
+
|
88
|
+
When file operations are to be used that are not included in the gem, place
|
89
|
+
the source files for the class definitions in one or more directories and
|
90
|
+
initialize the Pipeline object with the directory paths. The directories will
|
91
|
+
be added to the {source directories}[rdoc-ref:FilePipeline.source_directories].
|
92
|
+
|
93
|
+
Directories are added to the source directories in reverse order, so that
|
94
|
+
directories added later will have precedence when searching source files. The
|
95
|
+
default operations directory included in the gem will be searched last. This
|
96
|
+
allows overriding of operations without changing the code in existing classes.
|
97
|
+
|
98
|
+
If, for example, there are two directories with custom file operation classes,
|
99
|
+
<tt>'~/custom_operations'</tt> and <tt>'~/other_operations'</tt>, the new
|
100
|
+
instance of Pipeline can be set up to look for source files first in
|
101
|
+
<tt>'~/other_operations'</tt>, then in <tt>'~/custom_operations'</tt>, and
|
102
|
+
finally in the included default operations.
|
103
|
+
|
104
|
+
The basename for source files _must_ be the class name in underscore notation
|
105
|
+
without the containing module name. If, for example, the operation is
|
106
|
+
<tt>FileOperations::MyOperation</tt>, the source file basename should be
|
107
|
+
<tt>'my_operation.rb'</tt>
|
108
|
+
|
109
|
+
my_pipeline = FilePipeline::Pipeline.new('~/custom_operations',
|
110
|
+
'~/other_operations')
|
111
|
+
my_pipeline.define_operation('my_operation')
|
112
|
+
|
113
|
+
See {Custom file operations}[rdoc-label:label-Custom+file+operations] for
|
114
|
+
instructions for how to write file operations.
|
115
|
+
|
116
|
+
=== Nondestructive application to files
|
117
|
+
|
118
|
+
Pipelines[rdoc-ref:FilePipeline::Pipeline] work on
|
119
|
+
{versioned files}[rdoc-ref:FilePipeline::VersionedFile], which allow for
|
120
|
+
non-destructive application of all file operations.
|
121
|
+
|
122
|
+
To create a versioned file, initialize an instance with the path to the
|
123
|
+
original file:
|
124
|
+
|
125
|
+
# create an instance of VersionedFile for the file '~/image.jpg'
|
126
|
+
image = FilePipeline::VersionedFile.new('~/image.jpg')
|
127
|
+
|
128
|
+
As long as no operations have been applied, this will have no effect in the
|
129
|
+
file system. Only when the first operation is applied will VersionedFile
|
130
|
+
create a {working directory}[rdoc-ref:FilePipeline::VersionedFile#directory] in
|
131
|
+
the same directory as the original file. The working directory will have the
|
132
|
+
name of the file basename without extension and the suffix <tt>'_versions'</tt>
|
133
|
+
added.
|
134
|
+
|
135
|
+
Pipelines can be applied to a singe versioned file with the
|
136
|
+
the {#apply_to}[rdoc-ref:FilePipeline::Pipeline#apply_to] method of the pipeline
|
137
|
+
instance, or to an array of versioned files with the
|
138
|
+
{#batch_apply}[rdoc-ref:FilePipeline::Pipeline#batch_apply] method of the
|
139
|
+
pipeline instance.
|
140
|
+
|
141
|
+
=== Accessing file metadata and captured data.
|
142
|
+
|
143
|
+
*Limitations*: this currently only works for _Exif_ metadata of image files.
|
144
|
+
|
145
|
+
VersionedFile provides access to a files metadata via the
|
146
|
+
{#metadata}[rdoc-ref:FilePipeline::VersionedFile#metadata] method of the
|
147
|
+
versioned file instance.
|
148
|
+
|
149
|
+
Both the metadata for the original file and the current (latest) version can
|
150
|
+
be accessed:
|
151
|
+
|
152
|
+
image = FilePipeline::VersionedFile.new('~/image.jpg')
|
153
|
+
|
154
|
+
# access the metadata for the current version
|
155
|
+
image.metadata
|
156
|
+
|
157
|
+
Note that if no file operations have been applied by a pipeline object, this
|
158
|
+
will return the metadata for the original, which in that case is the current
|
159
|
+
(latest) version.
|
160
|
+
|
161
|
+
To explicitly get the metadata for the original file even if there are newer
|
162
|
+
versions available, pass the <tt>:for_version</tt> option with the symbol
|
163
|
+
<tt>:original</tt>:
|
164
|
+
|
165
|
+
# access the metadata for the original file
|
166
|
+
image.metadata(:for_version => :original)
|
167
|
+
|
168
|
+
Some file operations can comprise metadata; many image processing libraries
|
169
|
+
will not preserve all _Exif_ tags and their values when converting images to
|
170
|
+
a different format, but only write a small subset of tags to the file they
|
171
|
+
create. In these cases, the
|
172
|
+
{ExifRestoration}[rdoc-ref:FilePipeline::FileOperations::ExifRestoration]
|
173
|
+
operation can be used to try to restore the tags that have been discarded, but
|
174
|
+
it can not write all tags. It will store all tags and their values that it could
|
175
|
+
not write back to the file and return them as captured data.
|
176
|
+
|
177
|
+
Likewise, if the
|
178
|
+
{ExifRedaction}[rdoc-ref:FilePipeline::FileOperations::ExifRedaction] is applied
|
179
|
+
to delete sensitive tags (e.g. GPS location data), it will return all deleted
|
180
|
+
exif tags and their values as captured data.
|
181
|
+
|
182
|
+
The
|
183
|
+
{#recovered_metadata}[rdoc-ref:FilePipeline::VersionedFile#recovered_metadata]
|
184
|
+
of the versioned file instance will return a hash with all metadata that was
|
185
|
+
deleted or could not be restored by operations:
|
186
|
+
|
187
|
+
delete_tags = ['CreatorTool', 'Software']
|
188
|
+
|
189
|
+
my_pipeline = FilePipeline::Pipeline.new do |pipeline|
|
190
|
+
pipeline.define_operation('scale', width: 1280, height: 1024)
|
191
|
+
pipeline.define_operation('exif_restoration')
|
192
|
+
Pipeline.define_operation('exif_redaction', :redact_tags => delete_tags)
|
193
|
+
end
|
194
|
+
|
195
|
+
image = FilePipeline::VersionedFile.new('~/image.jpg')
|
196
|
+
my_pipeline.apply_to(image)
|
197
|
+
|
198
|
+
image.recovered_metadata
|
199
|
+
|
200
|
+
For information on retrieving other kinds of captured data, refer to
|
201
|
+
the versioned file instance methods
|
202
|
+
{#captured_data}[rdoc-ref:FilePipeline::VersionedFile#captured_data],
|
203
|
+
{#captured_data_for}[rdoc-ref:FilePipeline::VersionedFile#captured_data_for],
|
204
|
+
and
|
205
|
+
{#captured_data_with}[rdoc-ref:FilePipeline::VersionedFile#captured_data_with].
|
206
|
+
|
207
|
+
=== Finalizing files
|
208
|
+
|
209
|
+
Once all file operations of a pipeline object have been applied to a
|
210
|
+
versioned file object, it can be finalized by calling the
|
211
|
+
{#finalize}[rdoc-ref:FilePipeline::VersionedFile#finalize] method of the
|
212
|
+
instance.
|
213
|
+
|
214
|
+
Finalization will write the current version to the same directory that
|
215
|
+
contains the original. It will by default preserve the original by adding
|
216
|
+
a suffix to the basename of the final version. If the <tt>:overwrite</tt>
|
217
|
+
option for the method is passed with +true+, it will delete the original and
|
218
|
+
write the final version to the same basename as the original.
|
219
|
+
|
220
|
+
image = FilePipeline::VersionedFile.new('~/image.jpg')
|
221
|
+
|
222
|
+
# finalize the versioned file, preserving the original
|
223
|
+
image.finalize
|
224
|
+
|
225
|
+
# finalize the versioned file, replacing the original
|
226
|
+
image.finalize(:overwrite => true)
|
227
|
+
|
228
|
+
The work directory with all other versions will be deleted after the final
|
229
|
+
version has been written.
|
230
|
+
|
231
|
+
== Custom file operations
|
232
|
+
|
233
|
+
=== Module nesting
|
234
|
+
|
235
|
+
File operation classes _must_ be defined in the FilePipeline::FileOperations
|
236
|
+
module for {automatic requiring}[rdoc-ref:FilePipeline.load] of source files to
|
237
|
+
work.
|
238
|
+
|
239
|
+
=== Implementing from scratch
|
240
|
+
|
241
|
+
==== Initializer
|
242
|
+
|
243
|
+
The <tt>#initialize</tt> method _must_ take an +options+ argument (a hash
|
244
|
+
with a default value, or a <em>double splat</em>) and _must_ be exposed
|
245
|
+
through an <tt>#options</tt> getter method.
|
246
|
+
|
247
|
+
The options passed can be any for the file operation to properly configure
|
248
|
+
a specific instance of a method.
|
249
|
+
|
250
|
+
This requirement is imposed by the
|
251
|
+
{#define_operation}[rdoc-ref:FilePipeline::Pipeline#define_operation] instance
|
252
|
+
method of Pipeline, which will automatically load and initialize an instance of
|
253
|
+
the file operation with any options provided as a hash.
|
254
|
+
|
255
|
+
===== Examples
|
256
|
+
|
257
|
+
class MyOperation
|
258
|
+
attr_reader :options
|
259
|
+
|
260
|
+
# initializer with a default
|
261
|
+
def initialize(options = {})
|
262
|
+
@options = options
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class MyOperation
|
267
|
+
attr_reader :options
|
268
|
+
|
269
|
+
# initializer with a double splat
|
270
|
+
def initialize(**options)
|
271
|
+
@options = options
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
Consider a file operation +CopyrightNotice+ that whill add copyright
|
276
|
+
information to an image file's _Exif_ metadata, the value for the copyright
|
277
|
+
tag could be passed as an option.
|
278
|
+
|
279
|
+
copyright_notice = CopyrightNotice.new(:copyright => 'The Photographer')
|
280
|
+
|
281
|
+
==== The <tt>run</tt> method
|
282
|
+
|
283
|
+
File operations _must_ implement a <tt>#run</tt> method that takes three
|
284
|
+
arguments (or a _splat_) in order to be used in a Pipeline.
|
285
|
+
|
286
|
+
===== Arguments
|
287
|
+
|
288
|
+
The three arguments required for implementations of <tt>#run</tt> are:
|
289
|
+
* the path to the <em>file to be modified</em>
|
290
|
+
* the path to the _directory_ to which new files will be saved.
|
291
|
+
* the path to the <em>original file</em>, from which the first version in a
|
292
|
+
succession of modified versions has been created.
|
293
|
+
|
294
|
+
The <em>original file</em> will only be used by file operations that require
|
295
|
+
it for reference, e.g. to restore file metadata that was compromised by
|
296
|
+
other file operations.
|
297
|
+
|
298
|
+
===== Return value
|
299
|
+
|
300
|
+
The method _must_ return the path to the file that was created by the
|
301
|
+
operation (perferrably in the _directory_). It _may_ also return a
|
302
|
+
{Results}[rdoc-ref:FilePipeline::FileOperations::Results] object, containing the
|
303
|
+
operation itself, a _success_ flag (+true+ or +false+), and any logs or data
|
304
|
+
returned by the operation.
|
305
|
+
|
306
|
+
If results are returned with the path to the created file, both values must
|
307
|
+
be wrapped in an array, with the path as the first element, the results as
|
308
|
+
the second.
|
309
|
+
|
310
|
+
===== Example
|
311
|
+
|
312
|
+
def run(src_file, directory, original)
|
313
|
+
# make a path to which the created file will be written
|
314
|
+
out_file = File.join(directory, 'new_file_name.extension')
|
315
|
+
|
316
|
+
# create a Results object reporting success with no logs or data
|
317
|
+
results = Results.new(self, true, nil)
|
318
|
+
|
319
|
+
# create a new out_file based on src_file in directory
|
320
|
+
# ...
|
321
|
+
|
322
|
+
# return the path to the new file and the results object
|
323
|
+
[out_file, results]
|
324
|
+
end
|
325
|
+
|
326
|
+
==== Captured data tags
|
327
|
+
|
328
|
+
Captured data tags can be used to
|
329
|
+
{filter captured data}[rdoc-ref:FilePipeline::VersionedFile#captured_data_with]
|
330
|
+
accumulated during successive file operations.
|
331
|
+
|
332
|
+
Operations that return data as part of the results _should_ respond to
|
333
|
+
<tt>:captured_data_tag</tt> and return one of the
|
334
|
+
{tag constants}[rdoc-ref:FilePipeline::FileOperations::CapturedDataTags].
|
335
|
+
|
336
|
+
===== Example
|
337
|
+
|
338
|
+
# returns NO_DATA
|
339
|
+
def captured_data_tag
|
340
|
+
CapturedDataTags::NO_DATA
|
341
|
+
end
|
342
|
+
|
343
|
+
=== Subclassing FileOperation
|
344
|
+
|
345
|
+
The {FileOperation}[rdoc-ref:FilePipeline::FileOperations::FileOperation] class
|
346
|
+
is an abstract superclass that provides a scaffold to facilitate the creation of
|
347
|
+
file operations that conform to the requirements.
|
348
|
+
|
349
|
+
It implements a
|
350
|
+
{#run}[rdoc-ref:FilePipeline::FileOperations::FileOperation#run] method, that
|
351
|
+
takes the required three arguments and returns the path to the newly created
|
352
|
+
file and a Results object.
|
353
|
+
|
354
|
+
When the operation was successful,
|
355
|
+
{success}[rdoc-ref:FilePipeline::FileOperations::Results#success] will be
|
356
|
+
+true+. When an exception was raised, that exeption will be rescued and returned
|
357
|
+
as the {log}[rdoc-ref:FilePipeline::FileOperations::Results#log], and
|
358
|
+
{success}[rdoc-ref:FilePipeline::FileOperations::Results#success] will be
|
359
|
+
+false+.
|
360
|
+
|
361
|
+
The standard <tt>#run</tt> method of the FileOperation class does not contain
|
362
|
+
logic to perform the actual file operation, but will call an
|
363
|
+
{#operation method}[rdoc-label:label-The+operation+method] that _must_ be
|
364
|
+
defined in the subclass unless the subclass overrides the <tt>#run</tt> method.
|
365
|
+
|
366
|
+
The <tt>#run</tt> method will generate the new path that is passed to the
|
367
|
+
<tt>#operation</tt> method, and to which the latter will write the new
|
368
|
+
version of the file. The new file path will need an appropriate file type
|
369
|
+
extension. The default behavior is to assume that the extension will be the
|
370
|
+
same as for the file that was passed in as the basis from which the new
|
371
|
+
version will be created. If the operation will result in a different file
|
372
|
+
type, the subclass _should_ define a <tt>#target_extension</tt> method that
|
373
|
+
returns the appropriate file extension (see
|
374
|
+
{Target file extensions}[rdoc-label:label-Target+file+extensions]).
|
375
|
+
|
376
|
+
==== Initializer
|
377
|
+
|
378
|
+
The +initialize+ method _must_ take an +options+ argument (a hash with a
|
379
|
+
default value or a <em>double splat</em>).
|
380
|
+
|
381
|
+
===== Options and defaults
|
382
|
+
|
383
|
+
The initializer can call +super+ and pass the +options+ hash and any
|
384
|
+
defaults (a hash with default options). This will update the defaults with
|
385
|
+
the actual options passed to +initialize+ and assign them to the
|
386
|
+
{#options}[rdoc-ref:FilePipeline::FileOperations::FileOperation#options]
|
387
|
+
attribute.
|
388
|
+
|
389
|
+
If the initializer does not call +super+, it _must_ assign the options to
|
390
|
+
the <tt>@options</tt> instance variable or expose them through an
|
391
|
+
<tt>#options</tt> getter method.
|
392
|
+
|
393
|
+
If it calls +super+ but must ensure some options are always set to a
|
394
|
+
specific value, those should be set after the call to +super+.
|
395
|
+
|
396
|
+
===== Examples
|
397
|
+
|
398
|
+
# initializer without defaults callings super
|
399
|
+
def initialize(**options)
|
400
|
+
super(options)
|
401
|
+
end
|
402
|
+
|
403
|
+
# initializer with defaults calling super
|
404
|
+
def initialize(**options)
|
405
|
+
defaults = { :option_a => true, :option_b => false }
|
406
|
+
super(options, defaults)
|
407
|
+
end
|
408
|
+
|
409
|
+
# initializer with defaults calling super, ensures :option_c => true
|
410
|
+
def initialize(**options)
|
411
|
+
defaults = { :option_a => true, :option_b => false }
|
412
|
+
super(options, defaults)
|
413
|
+
@options[:option_c] = true
|
414
|
+
end
|
415
|
+
|
416
|
+
# initilizer that does not call super
|
417
|
+
def initialize(**options)
|
418
|
+
@options = options
|
419
|
+
end
|
420
|
+
|
421
|
+
==== The <tt>operation</tt> method
|
422
|
+
|
423
|
+
The <tt>#operation</tt> method contains the logic specific to a given
|
424
|
+
subclass of FileOperation and must be defined in that subclass unless the
|
425
|
+
<tt>#run</tt> method is overwritten.
|
426
|
+
|
427
|
+
===== Arguments
|
428
|
+
|
429
|
+
The <tt>#operation</tt> method must accept three arguments:
|
430
|
+
|
431
|
+
* the path to the <em>file to be modified</em>
|
432
|
+
* the path for the <em>file to be created</em> by the operation.
|
433
|
+
* the path to the <em>original file</em>, from which the first version in a
|
434
|
+
succession of modified versions has been created.
|
435
|
+
|
436
|
+
The <em>original file</em> will only be used by file operations that require
|
437
|
+
it for reference, e.g. to restore file metadata that was compromised by
|
438
|
+
other file operations.
|
439
|
+
|
440
|
+
===== Return Value
|
441
|
+
|
442
|
+
The method _can_ return anything that can be interpreted by
|
443
|
+
{LogDataParser}[rdoc-ref:FilePipeline::FileOperations::LogDataParser],
|
444
|
+
including nothing.
|
445
|
+
|
446
|
+
It will usually return any log outpout that the logic of <tt>#operation</tt>
|
447
|
+
has generated, and/or data captured. If data is captured that is to be used
|
448
|
+
later, the subclass should override the <tt>#captured_data_tag</tt> method to
|
449
|
+
return the appropriate
|
450
|
+
{tag constant}[rdoc-ref:FilePipeline::FileOperations::CapturedDataTags].
|
451
|
+
|
452
|
+
===== Examples
|
453
|
+
|
454
|
+
# creates out_file based on src_file, captures metadata differences
|
455
|
+
# between out_file and original, returns log messages and captured data
|
456
|
+
def operation(src_file, out_file, original)
|
457
|
+
captured_data = {}
|
458
|
+
log_messages = []
|
459
|
+
|
460
|
+
# write the new version based on src_file to out_file
|
461
|
+
# compare metadata of out_file with original, store any differences
|
462
|
+
# in captures_data and append any log messages to log_messages
|
463
|
+
|
464
|
+
[log_messages, captured_data]
|
465
|
+
end
|
466
|
+
|
467
|
+
# takes the third argument for the original file but does not use it
|
468
|
+
# creates out_file based on src_file, returns log messages
|
469
|
+
def operation(src_file, out_file, _)
|
470
|
+
src_file, out_file = args
|
471
|
+
log_messages = []
|
472
|
+
|
473
|
+
# write the new version based on src_file to out_file
|
474
|
+
|
475
|
+
log_messages
|
476
|
+
end
|
477
|
+
|
478
|
+
# takes arguments as a splat and destructures them to avoid having the
|
479
|
+
# unused thirs argumen
|
480
|
+
# creates out_file based on src_file, returns nothing
|
481
|
+
def operation(*args)
|
482
|
+
src_file, out_file = args
|
483
|
+
|
484
|
+
# write the new version based on src_file to out_file
|
485
|
+
|
486
|
+
return
|
487
|
+
end
|
488
|
+
|
489
|
+
==== Target file extensions
|
490
|
+
|
491
|
+
If the file that the operation creates is of a different type than the file
|
492
|
+
the version is based upon, the class _must_ define the
|
493
|
+
<tt>#target_extension</tt> method that returns the appropriate file type
|
494
|
+
extension.
|
495
|
+
|
496
|
+
In most cases, the resulting file type will be predictable (static), and in
|
497
|
+
such cases, the method can just return a string with the extension.
|
498
|
+
|
499
|
+
An alternative would be to provide the expected extension as an #option
|
500
|
+
to the initializer.
|
501
|
+
|
502
|
+
===== Examples
|
503
|
+
|
504
|
+
# returns always '.tiff.
|
505
|
+
def target_extension
|
506
|
+
'.tiff'
|
507
|
+
end
|
508
|
+
|
509
|
+
# returns the extension specified in #options +:extension+
|
510
|
+
# my_operation = MyOperation(:extension => '.dng')
|
511
|
+
def target_extension
|
512
|
+
options[:extension]
|
513
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_pipeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Stein
|
@@ -38,227 +38,15 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 2.0.14
|
41
|
-
description:
|
42
|
-
|
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"
|
41
|
+
description: The <tt>file_pipeline</tt> gem provides a framework fornondestructive
|
42
|
+
application of file operation batches to files.
|
256
43
|
email: loveablelobster@fastmail.fm
|
257
44
|
executables: []
|
258
45
|
extensions: []
|
259
46
|
extra_rdoc_files: []
|
260
47
|
files:
|
261
48
|
- LICENSE
|
49
|
+
- README.rdoc
|
262
50
|
- lib/file_pipeline.rb
|
263
51
|
- lib/file_pipeline/errors.rb
|
264
52
|
- lib/file_pipeline/errors/failed_modification_error.rb
|