File Coverage

blib/lib/File/ShareDir/PAR.pm
Criterion Covered Total %
statement 133 134 99.2
branch 25 30 83.3
condition 3 6 50.0
subroutine 24 24 100.0
pod 0 5 0.0
total 185 199 92.9


line stmt bran cond sub pod time code
1             package File::ShareDir::PAR;
2              
3             =pod
4              
5             =head1 NAME
6              
7             File::ShareDir::PAR - File::ShareDir with PAR support
8              
9             =head1 SYNOPSIS
10              
11             use File::SharedDir::PAR ':ALL';
12             # exact same interface as the normal File::ShareDir:
13            
14             # Where are distribution-level shared data files kept
15             $dir = dist_dir('File-ShareDir');
16            
17             # Where are module-level shared data files kept
18             $dir = module_dir('File::ShareDir');
19            
20             # Find a specific file in our dist/module shared dir
21             $file = dist_file( 'File-ShareDir', 'file/name.txt');
22             $file = module_file('File::ShareDir', 'file/name.txt');
23            
24             # Like module_file, but search up the inheritance tree
25             $file = class_file( 'Foo::Bar', 'file/name.txt' );
26              
27             You may choose to install the C
28             functions into C so that they become available
29             globally. In that case, you must do the following before
30             anybody can import functions from C:
31              
32             use File::ShareDir::PAR 'global';
33              
34             =head1 WARNING
35              
36             This module contains I code. If you want
37             to load modules from C<.par> files using PAR
38             and then access their shared directory using C,
39             you probably have no choice but to use it. But beware,
40             here be dragons.
41              
42             =head1 DESCRIPTION
43              
44             C provides the same functionality
45             as L but tries hard to be compatible with
46             L packaged applications.
47              
48             The problem is, that the concept of having a distribution or
49             module specific I directory becomes a little hazy
50             when you're loading everything from a single file.
51             L uses an C<@INC> hook to intercept any attempt to load
52             a module. L uses the directory structure that
53             is typically found in the directories that are listed in C<@INC>
54             for storing the shared data. In a C enviroment, this is
55             not necessarily possible.
56              
57             When you call one of the functions that this module provides,
58             it will take care to search in any of the currently loaded
59             C<.par> files before scanning C<@INC>. This is the same
60             order of preference you get for loading modules when PAR is
61             in effect. If the path or file you are asking for is found
62             in one of the loaded C<.par> files, that containing
63             C<.par> file is extracted and the path returned will
64             point to the extracted copy on disk.
65              
66             Depending on how you're using PAR, the files that are extracted
67             this way are either cleaned up after program termination
68             or cached for further executions. Either way, you're safe if
69             you use the shared data as read-only data. If you write to it,
70             your changes may be lost after the program ends.
71              
72             For any further usage information, including the list of exportable
73             functions, please refer to the documentation of L.
74              
75             =cut
76              
77 5     5   1893767 use 5.005;
  5         23  
  5         217  
78 5     5   30 use strict;
  5         13  
  5         217  
79 5     5   48 use base 'Exporter';
  5         11  
  5         665  
80 5     5   33 use Carp 'croak';
  5         16  
  5         387  
81 5     5   2762 use File::ShareDir ();
  5         49795  
  5         114  
82 5     5   55 use File::Spec ();
  5         9  
  5         74  
83 5     5   27 use Class::Inspector ();
  5         11  
  5         74  
84 5     5   28 use Config ();
  5         10  
  5         73  
85 5     5   25 use File::Path ();
  5         11  
  5         97  
86              
87 5     5   75 use vars qw{$VERSION @EXPORT_OK %EXPORT_TAGS %CLEANUP_DIRS};
  5         10  
  5         499  
88             BEGIN {
89 5     5   12 $VERSION = '0.06';
90 5         18 @EXPORT_OK = qw{dist_dir dist_file module_dir module_file class_file};
91 5         134 %EXPORT_TAGS = (
92             ALL => [ @EXPORT_OK ],
93             );
94             }
95              
96 5     5   27 use constant IS_MACOS => !!($^O eq 'MacOS');
  5         9  
  5         12990  
97              
98             # cleanup temporary extraction dirs.
99             # should be handled by PAR, but since we're
100             # abusing it to extract full .par's to inc/,
101             # we'd better take care!
102             END {
103 5     5   6464 foreach my $directory (keys %CLEANUP_DIRS) {
104 2 50       18042 File::Path::rmtree($directory) if -d $directory;
105             }
106             }
107              
108             # This isn't nice: Breaking PAR encapsulation.
109             # finds the specified file in the loaded .par's
110             # and returns the zip member, zip file, and zip handle
111             # on success
112             {
113             my $ver = $Config::Config{version};
114             my $arch = $Config::Config{archname};
115             sub _par_find_zip_member {
116 17     17   19 my $files = shift;
117 17 50       40 $files = [$files] if not ref $files;
118              
119 17         88 require PAR;
120              
121 17         98 s/\/+$// for @$files;
122              
123 114         118 my @files =
124 114         167 map {s{\\}{/}g; $_}
  19         23  
125             map {
126 17         36 my $file = $_;
127 19         109 ( $file, "lib/$file", "arch/$file", "$arch/$file", "$ver/$file", "$ver/$arch/$file" )
128             }
129             @$files;
130              
131 17         40 my $files_regexp = '^(?:' . join(')|(?:', map {quotemeta($_)} @files) . ')/?';
  114         232  
132 17         60 foreach my $zipkey (keys %PAR::LibCache) {
133 17         27 my $zip = $PAR::LibCache{$zipkey};
134 17 100       53 my $member = PAR::_first_member_matching($zip, $files_regexp) or next;
135 10         1417 return($member, $zipkey, $zip);
136             }
137              
138 7         1159 return;
139             }
140             }
141              
142             sub _par_in_use {
143 34 100   34   133 return() unless exists $INC{"PAR.pm"};
144 17 50       51 return() unless @PAR::LibCache;
145 17         48 return 1;
146             }
147              
148             sub _search_and_unpar {
149 17     17   24 my $zippaths = shift;
150 17 100       53 $zippaths = [$zippaths] if not ref $zippaths;
151              
152 17         38 my ($member, $zipkey, $zip) = _par_find_zip_member($zippaths);
153 17 100       44 if ($member) {
154 10 100 66     56 if (exists $PAR::ArchivesExtracted{$zip->fileName()} or $PAR::ArchivesExtracted{$zipkey}) {
155 8         64 my $inc = $PAR::ArchivesExtracted{$zip->fileName()};
156 8         40 return $inc;
157             }
158             else {
159             # watch out: breaking PAR encapsulation
160 2 50       133 my $inc_existed = -d "$PAR::SetupTemp::PARTemp/inc" ? 1 : 0;
161 2         13 my $inc = PAR::_extract_inc($zip, 'force');
162 2         245743 $PAR::ArchivesExtracted{$zip->fileName()} = $inc;
163 2 50 33     33 if (defined $inc and not $inc_existed) {
164 2         10 $CLEANUP_DIRS{$inc} = 1;
165 2         12 return $inc;
166             }
167 0         0 return();
168             }
169             }
170 7         13 return();
171             }
172              
173              
174             #####################################################################
175             # Interface Functions
176              
177             my $orig_dist_dir = \&File::ShareDir::dist_dir; # save original
178             sub dist_dir {
179 4     4 0 2138 my @args = @_;
180 4 100       25 if (_par_in_use()) {
181 2         10 my $dist = File::ShareDir::_DIST(shift);
182              
183             # Create the subpath
184 2         33 my $zip_paths = [
185             join (
186             '/',
187             'auto', split( /-/, $dist )
188             ),
189             join (
190             '/',
191             'auto', 'share', 'dist', split( /-/, $dist )
192             )
193             ];
194              
195 2         8 _search_and_unpar($zip_paths);
196             }
197              
198             # hide from croak
199 4         15 @_ = @args;
200 4         22 goto &$orig_dist_dir;
201             }
202              
203              
204             my $orig_module_dir = \&File::ShareDir::module_dir; # save original
205             sub module_dir {
206 38     38 0 19481 my @args = @_;
207 38         114 my $module = File::ShareDir::_MODULE(shift);
208              
209 26         856 my $short = Class::Inspector->filename($module);
210 26 100       680 if (_par_in_use()) {
211 13         27 my $inc = _search_and_unpar($short);
212 13 100       37 if (defined $inc) {
213             # Holy shit, I'm so evil. Somebody will find out I did this.
214 8         42 $INC{$short} = Class::Inspector->resolved_filename($module);
215             }
216             }
217              
218             # hide from croak
219 26         536 @_ = @args;
220 26         104 goto &$orig_module_dir;
221             }
222              
223              
224             my $orig_dist_file = \&File::ShareDir::dist_file; # save original
225             sub dist_file {
226 4     4 0 4764 my @args = @_;
227 4         19 my $dist = File::ShareDir::_DIST(shift);
228 4         48 my $file = File::ShareDir::_FILE(shift);
229              
230             # Create the subpath
231 4         106 my $zippath = join (
232             '/',
233             'auto', split( /-/, $dist ), File::Spec->splitdir($file)
234             );
235              
236 4 100       21 _search_and_unpar($zippath) if _par_in_use();
237              
238             # hide from croak
239 4         13 @_ = @args;
240 4         23 goto &$orig_dist_file;
241             }
242              
243              
244             my $orig_module_file = \&File::ShareDir::module_file; # save original
245             sub module_file {
246 4     4 0 4761 my @args = @_;
247 4         27 my $module = File::ShareDir::_MODULE($_[0]);
248 4         143 my $dir = module_dir($module);
249 4         496 @_ = @args;
250 4         22 goto &$orig_module_file;
251             }
252              
253              
254             my $orig_class_file = \&File::ShareDir::class_file; # save original
255             sub class_file {
256 4     4 0 7154 my @args = @_;
257 4         22 my $module = File::ShareDir::_MODULE(shift);
258              
259             # This had to be copied from File::ShareDir.
260             ### BEGIN VERBATIM COPY ###
261             # Get the super path ( not including UNIVERSAL )
262             # Rather than using Class::ISA, we'll use an inlined version
263             # that implements the same basic algorithm.
264 4         144 my @path = ();
265 4         12 my @queue = ( $module );
266 4         13 my %seen = ( $module => 1 );
267 4         20 while ( my $cl = shift @queue ) {
268 12         18 push @path, $cl;
269 5     5   38 no strict 'refs';
  5         28  
  5         1029  
270 8         49 unshift @queue, grep { ! $seen{$_}++ }
  8         15  
271 8         13 map { s/^::/main::/; s/\'/::/g; $_ }
  8         19  
  12         72  
272 12         17 ( @{"${cl}::ISA"} );
273             }
274             ### END VERBATIM COPY ###
275              
276 4         9 foreach my $class ( @path ) {
277 12         3114 eval { module_dir($class); };
  12         28  
278             }
279              
280             # hide from croak
281 4         2070 @_ = @args;
282 4         26 goto &$orig_class_file;
283             }
284              
285             sub import {
286 13     13   11596 my $class = shift;
287 13         32 my @opt = grep { $_ ne 'global' } @_;
  14         43  
288 13 100       60 if (@opt < @_) { # included 'global' option
289 5     5   31 no warnings 'redefine';
  5         7  
  5         946  
290 2         7 *File::ShareDir::class_file = \&class_file;
291 2         5 *File::ShareDir::module_file = \&module_file;
292 2         4 *File::ShareDir::dist_file = \&dist_file;
293 2         5 *File::ShareDir::module_dir = \&module_dir;
294 2         4 *File::ShareDir::dist_dir = \&dist_dir;
295             }
296 13         13934 $class->export_to_level(1, $class, @opt);
297             }
298              
299             1;
300              
301             =pod
302              
303             =head1 SUPPORT
304              
305             Bugs should always be submitted via the CPAN bug tracker
306              
307             L
308              
309             For other issues, contact the PAR mailing list: Epar@perl.orgE
310              
311             =head1 AUTHOR
312              
313             Steffen Mueller Esmueller@cpan.orgE
314              
315             The code was adapted from Adam Kennedy's work on C
316              
317             =head1 SEE ALSO
318              
319             L, L, L, L
320              
321             =head1 COPYRIGHT AND LICENSE
322              
323             Copyright (c) 2008-2010 Steffen Mueller
324             This program is free software; you can redistribute
325             it and/or modify it under the same terms as Perl itself.
326              
327             The portions of code that were copied from C are:
328              
329             Copyright (c) 2005, 2006 Adam Kennedy.
330             This program is free software; you can redistribute
331             it and/or modify it under the same terms as Perl itself.
332              
333             The full text of the license can be found in the
334             LICENSE file included with this module.
335              
336             =cut