File Coverage

blib/lib/Digest/TransformPath.pm
Criterion Covered Total %
statement 29 29 100.0
branch 10 12 83.3
condition 6 6 100.0
subroutine 9 9 100.0
pod 4 4 100.0
total 58 60 96.6


line stmt bran cond sub pod time code
1             package Digest::TransformPath;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Digest::TransformPath - Implements the TransformPath concept
8              
9             =head1 ACKNOWLEDGEMENTS
10              
11             A big thank you goes out to "coraline" (Richard Soderburg) for bringing the
12             caching mechanism of ccache to my attention, which sparked the idea, and upon
13             which this module is loosely (very) and conceptually (just barely) based.
14              
15             =head1 SYNOPSIS
16              
17             # Pull the original image from the database
18             my $Image = Database->get('Image', 423);
19             my $Path = Digest::TransformPath->new('Image.423');
20            
21             # Resize the image if bigger than 800x600
22             Image::Munge->constrain( $Image, 800, 600 );
23             $Path->add('constrain(800x600)');
24            
25             # Save the file
26             my $filename = File::Spec->catfile( 'cropped', $Path->digest(15), $Image->type );
27             File::Slurp::write_file( $filename, $Image->data );
28              
29             =head1 DESCRIPTION
30              
31             A TransformPath is a complex higher-order key that is designed for use with
32             chains of functions that sequentially transform a piece of data.
33              
34             The concept starts with a sizable chunk of data, for example an image, for
35             which we can determine a unique identifier, and for which we can cheaply
36             determine if and when the source material has changed.
37              
38             A series of resource-intensive transforms might be applied to this original
39             data to produce another piece of data. In the image example, we might
40             auto-level, crop, scale, rotate, colour-balance and then thumbnail the
41             image. This transformed data would be put into a cache.
42              
43             If at some future point we wish to obtain the same image, but would
44             preferably like to use the cached version, we would have to take the original
45             image, reapply the transforms, and then compare to the result the first time
46             around.
47              
48             Alternatives to this general checking mechanism revolve around storing the
49             identifier in parellel to the data file, in a database or data file, or
50             similar schemes the involve similar amounts of complexity.
51              
52             In the TransformPath concept, a structure is created which contains the
53             original source identifier, and a short, ordered and unique description of
54             all of the transformations in the sequence.
55              
56             This description structure is then serialised and hashed to get a unique and
57             generally cryptographically secure identifier for the transformed image. This
58             identifier would typically be used as part of the file name/path for the
59             transformed image.
60              
61             To check that the file is unchanged, we merely confirm that the original has
62             not changed, and then rebuilt the TransformPath digest. If the TransformPath
63             digest is unchanged, then the transformed image is unchanged, and we can use
64             the version in the cache, saving ourselves the high expense of running the
65             transforms again.
66              
67             If we cannot cheaply tell that the source image has changed, there is a
68             clean fallback position. By including a digest of the original data inside
69             the TransformPath object, the final digest changes automatically whenever the
70             data inside the source file changes.
71              
72             While this still costs us a digest run each time, this is relatively
73             affordable compared to doing the transforms as well.
74              
75             This can be done by either using the initial digest as the source id, or by
76             adding it as the first transform step. The latter is recommended for most
77             situations, as this ensures that the source id is static, and won't change.
78              
79             In many uses of Digest::TransformPath, this is likely to be highly preferable.
80              
81             =cut
82              
83 2     2   22039 use 5.005;
  2         8  
  2         70  
84 2     2   10 use strict;
  2         2  
  2         85  
85 2     2   19 use Digest::MD5 ();
  2         4  
  2         33  
86              
87 2     2   8 use vars qw{$VERSION};
  2         2  
  2         144  
88             BEGIN {
89 2     2   7418 $VERSION = '1.00';
90             }
91              
92             =pod
93              
94             =head1 METHODS
95              
96             =head2 new $id [, $string, ... ]
97              
98             The C constructor creates a new Digest::TransformPath object.
99              
100             Returns a new Digest::TransformPath object, or C if not given a plain
101             string for the identifier.
102              
103             =cut
104              
105             sub new {
106 8 50   8 1 34 my $class = ref $_[0] ? ref shift : shift;
107 8         19 my $self = bless [ ], $class;
108              
109             # Add the id
110 8 100       21 $self->add(shift) or return undef;
111              
112             # Add any extra transforms
113 3         11 while ( @_ ) {
114 2 50       10 $self->add(shift) or return undef;
115             }
116              
117 3         14 $self;
118             }
119              
120             =pod
121              
122             =head2 add $string
123              
124             The C method adds a transform description, in the form of a string, to
125             the TransformPath object.
126              
127             Returns true, or C if not passed a string.
128              
129             =cut
130              
131             sub add {
132 15     15 1 506 my $self = shift;
133 15 100 100     98 my $step = (defined $_[0] and ! ref $_[0]) ? shift : return undef;
134 7         17 push @$self, $step;
135 7         25 1;
136             }
137              
138             =pod
139              
140             =head2 source_id
141              
142             Returns the original source identifier
143              
144             =cut
145              
146 1     1 1 5 sub source_id { $_[0]->[0] }
147              
148             =pod
149              
150             =head2 digest [ $chars ]
151              
152             The C method generates an MD5 digest for the object. If passed the
153             optional $chars integer value, it will trim the 32 byte digest (it uses hex)
154             down to a shorter length.
155              
156             =cut
157              
158             sub digest {
159 12     12 1 1250 my $self = shift;
160 12         26 my $joined = join "\n", @$self;
161 12         45 my $digest = Digest::MD5::md5_hex($joined);
162 12 100       33 my $chars = @_ ? shift : return $digest;
163 9 100 100     92 (defined $chars and ! ref $chars and $chars > 0 and $chars <= 32)
164             ? substr( $digest, 0, $chars )
165             : undef;
166             }
167              
168             1;
169              
170             =pod
171              
172             =head1 SUPPORT
173              
174             All bugs should be filed via the bug tracker at
175              
176             L
177              
178             For other issues, or commercial enhancement or support, contact the author.
179              
180             =head1 AUTHORS
181              
182             Adam Kennedy Ecpan@ali.asE, L
183              
184             Thank you to Phase N (L) for permitting
185             the open sourcing and release of this distribution.
186              
187             =head1 COPYRIGHT
188              
189             Copyright (c) 2004 Adam Kennedy. All rights reserved.
190             This program is free software; you can redistribute
191             it and/or modify it under the same terms as Perl itself.
192              
193             The full text of the license can be found in the
194             LICENSE file included with this module.
195              
196             =cut