File Coverage

blib/lib/Filter/Include.pm
Criterion Covered Total %
statement 53 60 88.3
branch 7 14 50.0
condition 2 9 22.2
subroutine 14 15 93.3
pod 0 2 0.0
total 76 100 76.0


line stmt bran cond sub pod time code
1             {
2             package Filter::Include;
3              
4             $VERSION = '1.6';
5              
6 5     5   329733 use strict;
  5         13  
  5         196  
7             # XXX - this is dropped for the sake of pre-5.6 perls
8             # use warnings;
9              
10 5     5   31 use Carp 'croak';
  5         8  
  5         281  
11 5     5   29 use Scalar::Util 'reftype';
  5         13  
  5         569  
12 5     5   7610 use File::Spec::Functions 'catfile';
  5         7278  
  5         625  
13 5     5   23274 use Module::Locate Global => 1, 'get_source';
  5         82774  
  5         42  
14              
15 5     5   340 use vars '$MATCH_RE';
  5         10  
  5         760  
16             $MATCH_RE = qr{ ^ \043 ? \s* include \s+ (.+?) ;? $ }xm;
17              
18             sub install_handler {
19 4     4 0 6 my($name, $handler) = @_;
20              
21 4 50 0     26 croak "The $name handler must be a CODE reference, was given: " .
      33        
22             ref($handler) || $handler
23             if !ref $handler or reftype $handler ne 'CODE';
24              
25 5     5   31 no strict 'refs';
  5         10  
  5         911  
26 4         5 *{$name . '_handler'} = $handler;
  4         28  
27             }
28              
29             sub import {
30             my( $called_by, %args ) = @_;
31              
32             install_handler $_ => delete $args{$_}
33             for grep exists $args{$_}, qw/ before after pre post /;
34             }
35              
36             ## There's probably a nice module to do this somewhere ...
37             sub handler {
38 6     6 0 22 my $name = shift(@_) . '_handler';
39 6         34 my $handler = \&$name;
40              
41 6 100       38 goto &$handler
42             if defined &$name;
43             }
44              
45 5     5   32 use vars '$LINE';
  5         10  
  5         6573  
46             sub _filter {
47 5     5   13 local $_ = shift;
48              
49 5         68 s{$MATCH_RE}{
50 4         16 my $include = $1;
51            
52             ## Only do this the first time.
53 4 50       34 $LINE = _find_initial_lineno($_, $&)
54             unless defined $LINE;
55            
56 4         19 _source($include);
57             }ge;
58              
59 1         7 return $_
60             . "\n#line " . ( $LINE += tr[\n][\n] + 1 ) . "\n";
61             }
62              
63             ## work magic to find the first line number so #line declarations are correct
64             sub _find_initial_lineno {
65 4     4   13 my($src, $match) = @_;
66            
67             ## Find the number of lines before the $match in $src.
68 4         45 my $include_at = () = substr($src, 0, index($src, $match)) =~ /^(.?)/mg;
69              
70 4         13 my($i, $called_from) = 0;
71 4         84 $called_from = ( caller $i++ )[2]
72             while caller $i;
73              
74             ## We need the caller's line num in addition to the number of lines before
75             ## the match substring as Filter::Simple only filters after it is called.
76 4         19 return $include_at + $called_from;
77             }
78            
79             sub _source {
80 4     4   9 local $_ = shift;
81              
82 4 50       146 return ''
83             unless defined;
84              
85 4         8 my $include;
86 4         7 my $data = do {
87 4         7 local $@;
88 4 50       296 $include = $_ =~ $Module::Locate::PkgRe ? $_ : eval $_;
89 4 50 33     87 croak("Filter::Include - couldn't get a meaningful filename from: '$_'")
90             if not defined $include or $@;
91            
92 4         25 get_source( $include );
93             };
94              
95 0           $data = _expand_source($include, $data);
96              
97 0           return $data;
98             }
99              
100             sub _expand_source {
101 0     0     my($include, $data) = @_;
102              
103 0           handler pre => $include, $data;
104              
105 0 0         $data = _filter($data)
106             if $data =~ $MATCH_RE;
107              
108 0           handler post => $include, $data;
109              
110 0           return $data;
111             }
112            
113 5     5   295099 use Filter::Simple;
  5         210433  
  5         37  
114             FILTER {
115             ## You are crazy Filter::Simple, quite simply mad.
116             return
117             if /\A\s*\z/s;
118              
119             handler before => $_;
120             $_ = _filter($_);
121             handler after => $_;
122             };
123             }
124              
125             q. The End.;
126              
127             =pod
128              
129             =head1 NAME
130              
131             Filter::Include - Emulate the behaviour of the C<#include> directive
132              
133             =head1 SYNOPSIS
134              
135             use Filter::Include;
136            
137             include Foo::Bar;
138             include "somefile.pl";
139              
140             ## or the C preprocessor directive style:
141              
142             #include Some::Class
143             #include "little/library.pl"
144              
145             =head1 DESCRIPTION
146              
147             Take the C<#include> preproccesor directive from C, stir in some C
148             semantics and we have this module. Only one keyword is used, C, which
149             is really just a processor directive for the filter, which indicates the file to
150             be included. The argument supplied to C will be handled like it would
151             by C and C so C<@INC> is searched accordingly and C<%INC> is
152             populated.
153              
154             =head1 #include
155              
156             For those who have not come across C's C<#include> preprocessor directive
157             this section shall explain briefly what it does.
158              
159             When the C preprocessor sees the C<#include> directive, it will include the
160             given file straight into the source. The file is dumped directly to where
161             C<#include> previously stood, so becomes part of the source of the given file
162             when it is compiled. This is used primarily for C's header files so function
163             and data predeclarations can be nicely separated out.
164              
165             So given a small script like this:
166              
167             ## conf.pl
168             my $conf = { lots => 'of', configuration => 'info' };
169              
170             We can pull this file I in to the source of the following script
171             using C
172              
173             use Filter::Include;
174              
175             include 'conf.pl';
176             print join(' ', map { $_, $conf->{$_} } reverse sort keys %$conf), "\n";
177              
178             Once the filter is applied to the file above the source will look like this:
179              
180             ## conf.pl
181             my $conf = { lots => 'of', configuration => 'info' };
182              
183             print join(' ', map { $_, $conf->{$_} } reverse sort keys %$conf), "\n";
184              
185             So unlike C's native file include functions C pulls the
186             source of the file to be included I into the caller's source without
187             any code evaluation.
188              
189             =head2 Why not to use C<-P>
190              
191             To quote directly from L:
192              
193             NOTE: Use of -P is strongly discouraged because of its inherent problems,
194             including poor portability.
195              
196             So while you can use the C<#include> natively in C it comes with the
197             baggage of the C preprocessor.
198              
199             =head1 HANDLERS
200              
201             C has a facility to install handlers at various points of the
202             filtering process. These handlers can be installed by passing in the name of the
203             handler and an associated subroutine e.g
204              
205             use Filter::Include pre => sub {
206             my $include = shift;
207             print "Including $inc\n";
208             },
209             after => sub {
210             my $code = shift;
211             print "The resulting source looks like:\n$code\n";
212             };
213              
214             This will install the C
 and C handlers (documented below). 
215              
216             These handlers are going to be most suited for debugging purposes but could also
217             be useful for tracking module usage.
218              
219             =over 4
220              
221             =item pre/post
222              
223             Both handlers take two positional arguments - the current include e.g
224             C or C, and the source of the include which in the
225             case of the C
 handler is the source before it is parsed and in the case of 
226             the C handler it is the source after it has been parsed and updated as
227             appropriate.
228              
229             =item before/after
230              
231             Both handlers take a single argument - a string representing the relevant
232             source code. The C handler is called I any filtering is
233             performed so it will get the pre-filtered source as its first argument. The
234             C handler is called I the filtering has been performed so will
235             get the source post-filtered as its first argument.
236              
237             =back
238              
239             =head1 AUTHOR
240              
241             Dan Brook C<< >>
242              
243             =head1 SEE ALSO
244              
245             C, -P in L, L, L
246              
247             =cut