File Coverage

blib/lib/Win32/TieRegistry/PMVersionInfo.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Win32::TieRegistry::PMVersionInfo;
2 1     1   5056 use strict;
  1         2  
  1         56  
3             our $VERSION = 0.2;
4             our $CHAT;
5            
6             =head1 NAME
7            
8             Win32::TieRegistry::PMVersionInfo - store in Win32 Registry PM $VERSION info
9            
10             =head1 SYNOPSIS
11            
12             use Win32::TieRegistry::PMVersionInfo 0.2;
13            
14             my $reg = new Win32::TieRegistry::PMVersionInfo (
15             file_root => "D:/src/pl/spc2xml/version5/",
16             ignore_dirs => ["Commercial/bin/",
17             "Commercial/SPC/XSLT/SourceForge",
18             "Commercial/SPC/XSLT/CSS",
19             "Commercial/SPC/XSLT/imgs",],
20             reg_root => 'LMachine/Software/LittleBits/',
21             strip_path => $strip_path,
22             chat=>1,
23             );
24             $reg->get;
25             $reg->store;
26            
27             exit;
28            
29             =head1 DESCRIPTION
30            
31             This module mirrors to the Win32 registry version information from a perl module's heirachy.
32            
33             It offers no support for reading the information - for that use the C module
34             on which this module is based.
35            
36             Version information is ascertained using the same method as in C version 5.45.
37             To quote that module's manpage:
38            
39             The first line in the file that contains the regular expression
40            
41             /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/
42            
43             will be evaluated with eval() and the value of the named variable
44             after the eval() will be assigned to the VERSION attribute of the
45             MakeMaker object. The following lines will be parsed o.k.:
46            
47             $VERSION = '1.00';
48             *VERSION = \'1.01';
49             ( $VERSION ) = '$Revision: 1.222 $ ' =~ /\$Revision:\s+([^\s]+)/;
50             $FOO::VERSION = '1.10';
51             *FOO::VERSION = \'1.11';
52             our $VERSION = 1.2.3; # new for perl5.6.0
53            
54             but these will fail:
55            
56             my $VERSION = '1.01';
57             local $VERSION = '1.02';
58             local $FOO::VERSION = '1.30';
59            
60             (Putting "my" or "local" on the preceding line will work o.k.)
61            
62             =head1 DEPENDENCIES
63            
64             Win32::TieRegistry.
65            
66             =cut
67            
68 1     1   1351 use Win32::TieRegistry ( Delimiter=>"/" );
  0            
  0            
69             use Carp;
70            
71             =head1 CONSTRUCTOR
72            
73             Expects a class name, and optionally a list of arguments in a hash-like structure, a hash or pointer to a hash.
74             Options are keys in a the blessed hash reference that is the object, and as such may be directly accessed anytime.
75            
76             Options are:
77            
78             =over 4
79            
80             =item file_root
81            
82             The root at which to be begin parsing files.
83            
84             =item ignore_dirs
85            
86             An array of directories above the C not to process.
87             If any directory encountered matches at the beginning of one of
88             these strings, it will not be processed.
89            
90             =item strip_path
91            
92             The text to strip from left-hand side of paths when storing in the registry.
93            
94             =item reg_root
95            
96             The branch at which to root the mirror of the directory structure.
97            
98             =item dirname_pattern
99            
100             A positve regular expressions used when reading a directory, which the module
101             encloses within the bracket 'grouping' operator and anchors to the begining and
102             end of the string being matched. The C<.> and C<..> directories are excluded.
103            
104             =item filename_pattern
105            
106             As C above, but applies to filenames, and defaults to C<.*>.
107            
108             =item extension
109            
110             Set to anything to retain the file extension when mapping to the registry (the default);
111             expilcitly set to C to strip from the filename everything after the last full-stop.
112            
113             =back
114            
115             =cut
116            
117             sub new { my ($class) = (shift);
118             unless (defined $class) {
119             warn "Usage: $class->new( {key=>value} )";
120             return undef;
121             }
122             my %args;
123             # Take parameters and place in object slots/set as instance variables
124             if (ref $_[0] eq 'HASH'){ %args = %{$_[0]} }
125             elsif (not ref $_[0]){ %args = @_ }
126             else {
127             warn "Usage: $class->new( { key=>values, } )";
128             return undef;
129             }
130             my $self = bless {},$class;
131             # Set default options that may be over-ridden
132             $self->{tree} = ();
133             $self->{file_root} = '';
134             $self->{ignore_dirs} = [];
135             $self->{reg_root} = '';
136             $self->{strip_path} = '';
137             $self->{filename_pattern} = '.*';
138             $self->{dirname_pattern} = '.*';
139             $self->{extension} = 1;
140             # Set/overwrite public slots with user's values
141             foreach (keys %args) { $self->{lc $_} = $args{$_} }
142             if (exists $self->{chat} and defined $self->{chat}){
143             $CHAT = 1;
144             }
145             if (ref $self->{ignore_dirs} ne 'ARRAY'){
146             carp "Not an array ref";
147             }
148             # Try to create the root key if it doesn't exist
149             $_ = $Registry->{ $self->{reg_root} };
150             $Registry->{ $self->{reg_root} } = {} if not defined $_;
151             $_ = $Registry->{ $self->{reg_root} };
152             return $self;
153             }
154            
155             =head2 METHOD get
156            
157             Accepts an object reference, and optionally a directory to parse. Stores the names of all the files
158             in the passed directory (or the calling object's C slot),
159             and recurses (calls itself) on all sub-directories. Incidentally returns the path to the
160             directory operated upon.
161            
162             Will return without reiterating if the directory passed matches at the beginning of
163             any string in the C list (ie. the value in the object's C
164             plus C<@{$self->{ignore_dirs}}> slot).
165            
166             See L for details of how to effect exclusion of file and directory names.
167            
168             See also L above for details of how the version is ascertained.
169            
170             =cut
171            
172             sub get { my ($self,$dir) = (shift,shift);
173             local *DIR;
174             $dir = $self->{file_root} if not defined $dir;
175             croak "No \$self->{file_root} or passed dir to parse in method 'get'" if not defined $dir or $dir eq '';
176            
177             # See if our dir, $dir, is in the ignore list, @{$self->{ignore_dirs}}
178             foreach (@{$self->{ignore_dirs}}){
179             warn "Ignoring $_\n" and return undef if $dir =~ /$self->{file_root}\/?$_/;
180             }
181            
182             opendir DIR,$dir
183             or croak("Method get couldn't open process dir to get a file: <$dir>:\n $!.")
184             and return undef;
185             foreach my $fn (grep !-d && /^$self->{filename_pattern}$/,readdir DIR){
186             push @{$self->{tree}}, {
187             path => $dir.$fn,
188             version => &version_from($dir.$fn)
189             };
190             }
191             closedir DIR;
192            
193             chdir $dir or $self->croak("Method get couldn't cd to dir <$dir>: $!") and return undef;
194             opendir DIR,$dir or $self->croak("Method get couldn't open dir <$dir>: $!") and return undef;
195             foreach my $next_dir (grep {-d && !/^\.\.?$/ && /^($self->{dirname_pattern})$/ } readdir DIR){
196             $self->get($dir.$next_dir.'/');
197             }
198             closedir DIR;
199             return $dir;
200             }
201            
202             #
203             # PRIVATE SUBROUTINE version_from
204             # accepts a path, returns the version of that file or undef.
205             # Evals each line in the file until finding /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/ and evaluating
206             #
207             sub version_from { my $path = shift;
208             croak "version_from called without path argument" if not defined $path;
209             local *IN;
210             my $version = undef;
211             open IN, $path;
212             while (){
213             my $VERSION;
214             next if !/([\$*])(([\w\:\']*)\bVERSION)\b.*\=/;
215             s/^\s*(local|our)\s+//;
216             $_ = eval ("$_"); # Escape scoping?
217             $version = $VERSION;
218             last;
219             }
220             close IN;
221             warn "$version in $path\n" if $CHAT and defined $version;
222             return $version;
223             }
224            
225             =head2 METHOD get_from_MANIFEST
226            
227             As the C method, but only gets information from files listed
228             in a C file, the path to which should be passed as the first argument.
229            
230             Additionally, the name of a C file may be passed as a further argument,
231             in which case no information will be garthered from files listed therein.
232            
233             =cut
234            
235             sub get_from_MANIFEST { my ($self,$manifest,$manifest_skip) = (@_);
236             croak "No manifest file passed as argument" if not defined $manifest;
237             croak "No such manifest file as $manifest" if not -e $manifest;
238             local *IN;
239             my %skip;
240             if (defined $manifest_skip){
241             croak "No such MANIFEST.SKIP file as $manifest_skip" if not -e $manifest_skip;
242             open IN, $manifest_skip;
243             while (){
244             chomp;
245             $skip{$_} = 1;
246             }
247             close IN;
248             }
249             open MANIFEST,$manifest or croak "Could not open $manifest";
250             while (){
251             chomp;
252             next if exists $skip{$_};
253             push @{$self->{tree}}, {
254             path => $_,
255             version => &version_from($_)
256             };
257             }
258             close IN;
259             return 1;
260             }
261            
262             =head2 METHOD store
263            
264             Accepts an object-reference and optionally a registry path to act as a root at which to secure
265             the C<$VERSION> info from every file in the object's C slot. If no 'root' is supplied,
266             the calling object's C slot is used. Incidentally returns the root used after making
267             changes to the registry.
268            
269             =cut
270            
271             sub store { my ($self,$root) = (shift,shift);
272             $root = $self->{reg_root} if not defined $root;
273             foreach my $file (sort @{$self->{tree}}){
274             if (exists $file->{version} and $file->{version} ne ''){
275             # warn $file->{path},"\t",$file->{version},"\n";
276             $file->{path} =~ s/^\Q$self->{strip_path}\E//i;
277             $file->{path} =~ s/\.[^.]*$// if defined $self->{extension};
278             $file->{path} =~ s|\\|/|g;
279             # Build the heirachy
280             my $path = $root;
281             foreach my $part (split m|/|,$file->{path}){
282             $path .= $part.'/';
283             $_ = $Registry->{ $path };
284             $Registry->{ $path } = {} if not defined $_;
285             }
286             # Make the keys from all the values in %{$file}, except $path
287             foreach (keys %{$file}){
288             next if $_ eq 'path';
289             $Registry->{ $root.$file->{path} } = {$_ => $file->{$_} };
290             }
291             } else {
292             warn "No version in file '$file->{path}'\n" if $CHAT;
293             }
294             }
295             return $root;
296             }
297            
298            
299            
300            
301             1; # Moduel must return a true value
302            
303             __END__