File Coverage

blib/lib/relative.pm
Criterion Covered Total %
statement 56 62 90.3
branch 28 34 82.3
condition n/a
subroutine 6 6 100.0
pod n/a
total 90 102 88.2


line stmt bran cond sub pod time code
1             package relative;
2 5     5   35376 use strict;
  5         14  
  5         283  
3 5     5   30 use Carp;
  5         16  
  5         413  
4 5     5   5766 use UNIVERSAL::require;
  5         14779  
  5         148  
5              
6             {
7 5     5   173 no strict "vars";
  5         11  
  5         4119  
8             $VERSION = '0.04';
9             }
10              
11             =head1 NAME
12              
13             relative - Load modules with relative names
14              
15             =head1 VERSION
16              
17             Version 0.04
18              
19             =cut
20              
21             sub import {
22 21 100   21   37271 return if @_ <= 1; # called with no args
23 17         55 my ($package, @args) = @_;
24 17         68 my ($caller) = caller();
25 17         67 my @loaded = ();
26              
27             # read the optional parameters
28 17         37 my %param = ();
29              
30 17 50       202 if (ref $args[0] eq 'HASH') {
    50          
31 0         0 %param = %{shift @args}
  0         0  
32             }
33             elsif (ref $args[0] eq 'ARRAY') {
34 0         0 %param = @{shift @args}
  0         0  
35             }
36              
37             # go through the args list, looking to parameters with the dash syntax,
38             # and module names and optional arguments
39 17         33 my %args_for = (); # modules arguments
40 17         24 my @modules = (); # will be filled with only the module names
41 17         24 my $prev = "";
42              
43 17         41 for my $item (@args) {
44             # if $prev is true, the previous thing (parameter or module name)
45             # is expecting a value
46 47 100       92 if ($prev) {
47             # this is a parameter
48 19 100       64 if (index($prev, "-") == 0) {
49 5         22 $param{substr($prev, 1)} = $item;
50 5         14 $prev = "";
51             }
52             # this is a module name
53             else {
54 14         27 push @modules, $prev;
55              
56             # this isn't a ref, so the previous module is just stored
57             # and the current item becomes the new $prev
58 14 100       42 if (not ref $item) {
    50          
59 10         67 $prev = $item;
60             }
61             # this is an arrayref, which will be used as the import list
62             # for the module name in $prev
63             elsif (ref $item eq "ARRAY") {
64 4         10 $args_for{$prev} = $item;
65 4         10 $prev = "";
66             }
67             else {
68 0         0 my $that = "a ".lc(ref $item)."ref";
69 0         0 croak "error: Don't know how to deal with $that (after '$prev')";
70             }
71             }
72             }
73             else {
74 28 100       70 if ($item eq "-aliased") {
75             # -aliased is a flag, so it doesn't expect a value
76 2         9 $param{aliased} = 1
77             }
78             else {
79 26         70 $prev = $item
80             }
81             }
82             }
83              
84 17 50       136 push @modules, $prev if $prev;
85              
86             # determine the base name
87 17 100       59 my $base = exists $param{to} ? $param{to} : $caller;
88              
89             # load the modules
90 17         35 for my $relname (@modules) {
91             # resolve the module relative name to absolute name
92 31         73 my $module = "$base\::$relname";
93 31         252 1 while $module =~ s/::\w+::(?:\.\.)?::/::/g;
94 31         50 $module =~ s/^:://;
95              
96             # load the module, die if it failed
97 31 100       209 $module->require or croak _clean($@);
98              
99             # import the symbols from the loaded module into the caller module
100 30 100       1098 if (exists $args_for{$relname}) {
101 4         71 my $args = $args_for{$relname};
102              
103             # an arguments list has been defined, but only call import if
104             # there are some arguments
105 4 100       14 if (@$args) {
106 2         5 my $args_str = join ", ", map {"q/\Q$_\E/"} @$args;
  2         9  
107 2 50       238 eval qq{ package $caller; $module->import($args_str); 1 }
108             or croak _clean($@);
109             }
110             }
111             else {
112             # use the default import method
113 26 50       2840 eval qq{ package $caller; $module->import; 1 } or croak _clean($@);
114             }
115              
116             # define alias if asked to
117 30 100       114 if ($param{aliased}) {
118 4         34 my ($alias) = $module =~ /\b(\w+)$/;
119 4         206 eval qq{ package $caller; sub $alias () { q/$module/ } };
120             }
121              
122             # keep a list of the loaded modules
123 30         100 push @loaded, $module;
124             }
125              
126 16 100       157 return wantarray ? @loaded : $loaded[-1]
127             }
128              
129              
130             sub _clean {
131 1     1   16 my ($msg) = @_;
132 1         10 $msg =~ s/ at .*relative.pm line \d+\.\s*$//s;
133 1         198 return $msg
134             }
135              
136              
137             =head1 SYNOPSIS
138              
139             package BigApp::Report;
140              
141             use relative qw(Create Publish);
142             # loads BigApp::Report::Create, BigApp::Report::Publish
143              
144             use relative qw(..::Utils);
145             # loads BigApp::Utils
146              
147             use relative -to => "Enterprise::Framework" => qw(Base Factory);
148             # loads Enterprise::Framework::Base, Enterprise::Framework::Factory
149              
150              
151             =head1 DESCRIPTION
152              
153             This module allows you to load modules using only parts of their name,
154             relatively to the current module or to a given module. Module names are
155             by default searched below the current module, but can be searched upper
156             in the hierarchy using the C<..::> syntax.
157              
158             In order to further loosen the namespace coupling, C returns
159             the full names of the loaded modules, making object-oriented code easier
160             to write:
161              
162             use relative;
163              
164             my ($Maker, $Publisher) = import relative qw(Create Publish);
165             my $report = $Maker->new;
166             my $publisher = $Publisher->new;
167              
168             my ($Base, $Factory) = import relative -to => "Enterprise::Framework"
169             => qw(Base Factory);
170             my $thing = $Factory->new;
171              
172             This can also be written using aliases:
173              
174             use relative -aliased => qw(Create Publish);
175             my $report = Create->new;
176             my $publisher = Publisher->new;
177              
178             use relative -to => "Enterprise::Framework", -aliased => qw(Base Factory);
179             my $thing = Factory->new;
180              
181              
182             =head1 IMPORT OPTIONS
183              
184             Import options can be given as an hashref or an arrayref as the first
185             argument:
186              
187             # options as a hashref
188             import relative { param => value, ... }, qw(Name ...);
189              
190             # options as an arrayref
191             import relative [ param => value, ... ], qw(Name ...);
192              
193             In order to simplyfing syntax, options can also be given as dash-prefixed
194             params:
195              
196             import relative -param => value, qw(name ...);
197              
198             Available options:
199              
200             =over
201              
202             =item *
203              
204             C can be used to indicate another hierarchy to search modules inside.
205              
206             B
207              
208             # in a hashref:
209             import relative { to => "Some::Other::Namespace" }, qw(Other Modules);
210              
211             # as dash-param:
212             import relative -to => "Some::Other::Namespace", qw(Other Modules);
213              
214             =item *
215              
216             C will create constants, named with the last component of each
217             loaded module, returning its corresponding full name. Yes, this feature
218             is very similar to what C does as it was added per Ovid request C<:-)>
219              
220             B
221              
222             # in a hashref:
223             import relative { aliased => 1 }, qw(Whack Zlonk);
224             my $frob = Whack->fizzle;
225              
226             # as dash-param:
227             import relative -aliased, qw(Whack Zlonk);
228             my $frob = Whack->fizzle;
229              
230             =back
231              
232             C will C as soon as a module can't be loaded.
233              
234             C returns the full names of the loaded modules when called in
235             list context, or the last one when called in scalar context.
236              
237              
238             =head1 AUTHOR
239              
240             SEbastien Aperghis-Tramoni, C<< >>
241              
242             =head1 BUGS
243              
244             Please report any bugs or feature requests to
245             C, or through the web interface at
246             L.
247             I will be notified, and then you'll automatically be notified of progress
248             on your bug as I make changes.
249              
250             =head1 SUPPORT
251              
252             You can find documentation for this module with the perldoc command.
253              
254             perldoc relative
255              
256             You can also look for information at:
257              
258             =over 4
259              
260             =item * AnnoCPAN: Annotated CPAN documentation
261              
262             L
263              
264             =item * CPAN Ratings
265              
266             L
267              
268             =item * RT: CPAN's request tracker
269              
270             L
271              
272             =item * Search CPAN
273              
274             L
275              
276             =back
277              
278              
279             =head1 ACKNOWLEDGEMENTS
280              
281             Thanks to Aristotle Pagaltzis, Andy Armstrong, Ken Williams
282             and Curtis Poe for their suggestions and ideas.
283              
284              
285             =head1 COPYRIGHT & LICENSE
286              
287             Copyright 2007 SEbastien Aperghis-Tramoni, all rights reserved.
288              
289             This program is free software; you can redistribute it and/or modify it
290             under the same terms as Perl itself.
291              
292             =cut
293              
294             "evitaler fo dnE" # "End of relative"