File Coverage

blib/lib/Toolkit.pm
Criterion Covered Total %
statement 50 95 52.6
branch 1 18 5.5
condition 1 2 50.0
subroutine 17 23 73.9
pod n/a
total 69 138 50.0


line stmt bran cond sub pod time code
1             package Toolkit;
2              
3 1     1   72359 use version; $VERSION = qv('0.0.2');
  1         14912  
  1         8  
4              
5 1     1   87 use warnings;
  1         2  
  1         34  
6 1     1   5 use strict;
  1         7  
  1         26  
7 1     1   6 use Carp;
  1         1  
  1         105  
8              
9 1     1   1072 use File::Spec::Functions qw( catfile splitpath );
  1         1085  
  1         99  
10 1     1   1292 use Filter::Simple;
  1         54724  
  1         7  
11              
12             sub _uniq {
13 2     2   4 my %seen;
14 2         3 return grep { !$seen{$_}++ } @_;
  2         10  
15             }
16              
17             # Read in an entire file given the filename...
18             sub _slurp {
19 0     0   0 my ($file_path) = @_;
20              
21 0 0       0 open my $fh, '<', $file_path
22             or croak "Toolkit couldn't open $file_path\n$!";
23              
24 0         0 local $/;
25 0         0 return <$fh>;
26             }
27              
28             # Cache of module names using this module
29             my @callers;
30              
31             my @MANDATORY_KITS = qw(ALWAYS);
32             my @DEFAULT_KITS = qw(ALWAYS DEFAULT);
33             my @ANY_KITS = ();
34              
35             # Handle the macro kits and prepare for run-time kits...
36             FILTER {
37             my ($class, @args) = @_;
38              
39             # Remember where we came in...
40             my ($caller, $file, $line) = caller(1);
41             $line++;
42             my $location = qq{\n#line $line "$file"\n};
43              
44             # Are we looking for something in particular?
45             my @only_these_macro_kits
46             = _uniq(@args ? (@MANDATORY_KITS, @args) : @DEFAULT_KITS);
47             ### @only_these_macro_kits
48             my @only_these_runtime_kits
49             = _uniq(@args ? (@MANDATORY_KITS, @args) : @ANY_KITS);
50              
51             # Cache the module that invoked this filter (for run-time kits)..
52             push @callers, $caller;
53              
54             # Get a list of run-time kits...
55             my @subs_runtime
56             = _kit_names( _find_all_kits(['Runtime'], \@only_these_runtime_kits) );
57              
58             # Load up all the compile-time macros (with line numbering)...
59             my $macros = q{};
60             for my $macro_path ( _find_all_kits(['Macros'], \@only_these_macro_kits) ) {
61             $macros .= qq{#line 1 "$macro_path"\n} . _slurp $macro_path;
62             }
63              
64             # Insert the macros and pre-declarations for run-time subs...
65             $_ = $macros . _declarations_for(@subs_runtime) . $location . $_;
66             ### Filtered source: $_
67             };
68              
69             # Strip directory paths from kits...
70             sub _kit_names {
71 0         0 return map {
72 1     1   3 my (undef, undef, $file_name) = splitpath($_);
73 0         0 $file_name;
74             } @_;
75             }
76              
77             # Locate all kits in the specified subdirectory by searching include path...
78             sub _find_all_kits {
79 2     2   3 my ($sub_path_ref, $only_these_kits_ref) = @_;
80             ### $sub_path_ref
81             ### $only_these_kits_ref
82 22         112 return map {
83 2         5 _glob_plain_files(
84 22         27 catfile($_, 'Toolkit', @{$sub_path_ref}),
85             $only_these_kits_ref,
86             )
87             } @INC;
88             }
89              
90             # Return full paths of all plain files under root directory...
91             sub _glob_plain_files {
92 22     22   27 my ($root_dir, $only_these_kits_ref) = @_;
93 22 50       1058 return () if ! -d $root_dir;
94              
95             ### Globbing: $root_dir
96 0         0 my @files;
97 1     1   701 use File::Find;
  1         3  
  1         415  
98 0         0 my @root_dirs = @{$only_these_kits_ref}
  0         0  
99 0 0       0 ? map { catfile $root_dir, $_ } @{$only_these_kits_ref}
  0         0  
100             : $root_dir;
101             ### Searching: @root_dirs
102             ROOT:
103 0         0 for my $root (@root_dirs) {
104 0 0       0 next ROOT if ! -e $root;
105             find(
106             sub {
107 0 0   0   0 return if -d; push @files, $File::Find::name
  0         0  
108             },
109 0         0 $root,
110             );
111             }
112             ### @files
113 0         0 return @files;
114             }
115              
116             # Create declarations for run-time kits...
117             sub _declarations_for {
118 1     1   7 return join q{}, map { "sub $_;" } @_;
  0            
119             }
120              
121             # Install a subroutine from a run-time kit...
122             sub _install_kit_for {
123 0     0     my ($full_sub_name) = @_;
124 0           my ($package, $sub_name) = ($full_sub_name =~ m/(.*) :: (.*)/xms);
125              
126             # Which kit (if any)?
127 0           my $file_path = _find_kit_for(qw(Toolkit Runtime), $sub_name);
128 0 0         return if !$file_path;
129              
130             # Install it...
131 0           return _install_kit_from($file_path, $full_sub_name, $sub_name);
132             }
133              
134             # Load and install a subroutine from the specified kit...
135             sub _install_kit_from {
136 0     0     my ($file_path, $full_sub_name, $sub_name) = @_;
137              
138             # First try to install it in the sandbox...
139             package Toolkit::Sandbox;
140 1     1   7 use Carp;
  1         2  
  1         66  
141 0           do $file_path;
142              
143             # Verify that it was installed...
144 1     1   7 no strict 'refs';
  1         7  
  1         254  
145 0 0         if (! *{$sub_name}{CODE} ) {
  0            
146 0           carp "Toolkit could not load &$sub_name\n",
147             "(running $file_path didn't install it)\n",
148             "Problem was detected";
149 0           return; # failure
150             }
151              
152             # Then move it to the requested location...
153 0           *{$full_sub_name} = \&{$sub_name};
  0            
  0            
154              
155 0           return 1; # success
156             }
157              
158             # Return the first kit matching the specified path...
159             sub _find_kit_for {
160 0     0     my (@path) = @_;
161 0           for my $dir (@INC) {
162 0           my $file_path = catfile($dir,@path);
163 0 0         return $file_path if -r $file_path;
164             }
165 0           return;
166             }
167              
168             # Install AUTOLOADs in every module that used this one...
169             CHECK {
170 1     1   766 for my $package (@callers) {
171 1         3 my $full_AUTOLOAD_name = $package.'::AUTOLOAD';
172              
173 1     1   6 no strict 'refs';
  1         2  
  1         106  
174 1         2 our $AUTOLOAD;
175              
176             # Save any existing AUTOLOAD in the same namespace...
177             my $orig_AUTOLOAD_ref
178             = *{$full_AUTOLOAD_name}{CODE}
179 1   50     2 || sub { croak "Undefined subroutine &$AUTOLOAD called" };
180              
181             # Install a replacement that defaults to the original...
182 1     1   6 no warnings 'redefine';
  1         2  
  1         179  
183 1         1158 *{$package.'::AUTOLOAD'} = sub {
184             # First try to load the requested subroutine from a kit...
185 0 0   0   0 if ( _install_kit_for($AUTOLOAD) ) {
186 0         0 goto &{$AUTOLOAD};
  0         0  
187             }
188              
189             # Otherwise default to the "real" AUTOLOAD...
190 0         0 ${$package.'::AUTOLOAD'} = $AUTOLOAD;
  0         0  
191 0         0 goto &{$orig_AUTOLOAD_ref};
  0         0  
192 1         5 };
193             }
194             }
195              
196              
197             1; # Magic true value required at end of module
198             __END__
199              
200             =head1 NAME
201              
202             Toolkit - Keep your handy modules organized
203              
204              
205             =head1 VERSION
206              
207             This document describes Toolkit version 0.0.2
208              
209              
210             =head1 SYNOPSIS
211              
212             use Toolkit;
213              
214             # All your favorites are now available
215              
216            
217             =head1 DESCRIPTION
218              
219             The Toolkit module provides a standard location to store modules that you use
220             all the time, and then loads them for you automatically. For example, instead
221             of always writing:
222              
223             use strict;
224             use warnings;
225             use Carp;
226             use Smart::Comments;
227              
228             in every program/module, you can just write:
229              
230             use Toolkit;
231              
232             and put all your favorite modules in a file:
233              
234             > cat $PERL5LIB/Toolkit/Macros/ALWAYS/Modules
235              
236             use strict;
237             use warnings;
238             use Carp;
239             use Smart::Comments;
240              
241             You can also specify load-on-demand subroutines:
242              
243             > cat $PERL5LIB/Toolkit/Runtime/prompt
244              
245             use IO::Prompt qw( prompt );
246              
247             > cat $PERL5LIB/Toolkit/Runtime/say
248              
249             sub say { print @_, "\n" }
250              
251             in which case Toolkit will install an C<AUTOLOAD> that installs these
252             subroutines the first time they're called.
253              
254              
255             =head1 INTERFACE
256              
257             Calling:
258              
259             use Toolkit;
260              
261             with no arguments loads any files in the directories:
262              
263             $PERL5LIB/Toolkit/Macros/ALWAYS/
264             $PERL5LIB/Toolkit/Macros/DEFAULT/
265              
266             or any of their subdirectories.
267              
268             Calling:
269              
270             use Toolkit qw(foo bar);
271              
272             any files in the directories:
273              
274             $PERL5LIB/Toolkit/Macros/ALWAYS/
275             $PERL5LIB/Toolkit/Macros/foo/
276             $PERL5LIB/Toolkit/Macros/bar/
277              
278             or any of their subdirectories.
279              
280             Using the Toolkit module in any way also installs an C<AUTOLOAD> subroutine
281             which looks in:
282              
283             $PERL5LIB/Toolkit/Runtime/
284              
285             for a file of the same name as the subroutine that is being autoloaded. That
286             is, if you write:
287              
288             use Toolkit;
289              
290             baz();
291              
292             Then the module looks for a file:
293              
294             $PERL5LIB/Toolkit/Runtime/baz
295              
296             and executes it in a special namespace. After the file executes, Toolkit
297             expects that the special namespace will now have a subroutine of the required
298             name, which it then calls.
299              
300             =head1 DIAGNOSTICS
301              
302             =over
303              
304             =item Toolkit couldn't open %s
305              
306             You specified a particular macro for Toolkit to load, but it wasn't able to
307             read the corresponding file. Usually a file permissions problem or a
308             non-existent macro.
309              
310              
311             =item Undefined subroutine %s called
312              
313             You used a subroutine that Toolkit couldn't autoload. Did you misspell
314             the subroutine name, or fail to install a file of the same name in your
315             C<$PERL5LIB/Toolkit/Runtime/> subdirectory.
316              
317             =item Toolkit could not load %s (running %s didn't install it).
318              
319             You used a subroutine that Toolkit tried to autoload. It found the
320             corresponding file in the C<$PERL5LIB/Toolkit/Runtime/> subdirectory, but
321             executing that file didn't produce a subroutine of the correct name.
322              
323             =back
324              
325              
326             =head1 CONFIGURATION AND ENVIRONMENT
327              
328             Toolkit uses the following directories and files to configure its behaviour:
329              
330             =over
331              
332             =item $PERL5LIB/Toolkit/Macros/ALWAYS/
333              
334             Files in this directory are prepended to your source code whenever Toolkit is
335             used
336              
337             =item $PERL5LIB/Toolkit/Macros/DEFAULT/
338              
339             Files in this directory are prepended to your source code whenever Toolkit is
340             used without arguments
341              
342             =item $PERL5LIB/Toolkit/Macros/I<any file name>
343              
344             These files are prepended to your source code whenever Toolkit is used and
345             their name is specified after the C<use Toolkit>.
346              
347             =item $PERL5LIB/Toolkit/Runtime/
348              
349             Files in this directory are executed to whenever Toolkit is used and a
350             subroutine of the same name is called. They are expected to define the
351             required subroutine.
352              
353             =back
354              
355             =head1 DEPENDENCIES
356              
357             Requires:
358              
359             =over
360              
361             =item *
362              
363             File::Spec::Functions
364              
365             =item *
366              
367             Filter::Simple
368              
369             =item *
370              
371             version
372              
373             =back
374              
375              
376             =head1 INCOMPATIBILITIES
377              
378             None reported.
379              
380              
381             =head1 BUGS AND LIMITATIONS
382              
383             No bugs have been reported.
384              
385             Please report any bugs or feature requests to
386             C<bug-tool-kit@rt.cpan.org>, or through the web interface at
387             L<http://rt.cpan.org>.
388              
389              
390             =head1 AUTHOR
391              
392             Damian Conway C<< <DCONWAY@cpan.org> >>
393              
394              
395             =head1 LICENCE AND COPYRIGHT
396              
397             Copyright (c) 2005, Damian Conway C<< <DCONWAY@cpan.org> >>. All rights reserved.
398              
399             This module is free software; you can redistribute it and/or
400             modify it under the same terms as Perl itself.
401              
402              
403             =head1 DISCLAIMER OF WARRANTY
404              
405             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
406             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
407             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
408             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
409             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
410             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
411             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
412             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
413             NECESSARY SERVICING, REPAIR, OR CORRECTION.
414              
415             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
416             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
417             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
418             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
419             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
420             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
421             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
422             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
423             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
424             SUCH DAMAGES.