File Coverage

blib/lib/MAD/Loader.pm
Criterion Covered Total %
statement 71 71 100.0
branch 12 12 100.0
condition 6 6 100.0
subroutine 12 12 100.0
pod 7 7 100.0
total 108 108 100.0


line stmt bran cond sub pod time code
1             package MAD::Loader;
2             $MAD::Loader::VERSION = '3.001001_001';
3 22     22   757451 use Moo;
  22         164925  
  22         194  
4             extends 'Exporter';
5              
6 20     20   24038 use Carp;
  20         4449  
  20         2929  
7 20     20   8591 use Const::Fast;
  20         16262  
  20         168  
8              
9             const our $MODULE_NAME_REGEX => qr{^[_[:upper:]]\w*(::\w+)*$};
10              
11             our @EXPORT_OK = qw{
12             fqn
13             load_module
14             build_object
15             load_and_new
16             };
17              
18             has 'prefix' => (
19             is => 'ro',
20             isa => sub {
21             Carp::croak "Invalid prefix '$_[0]'"
22             unless '' eq $_[0] || $_[0] =~ $MODULE_NAME_REGEX;
23             },
24             default => sub {
25             return '';
26             },
27             );
28              
29             has 'builder' => (
30             is => 'ro',
31             default => sub {
32             return 'new';
33             },
34             );
35              
36             has 'set_inc' => (
37             is => 'ro',
38             isa => sub {
39             Carp::croak 'set_inc must be an ArrayRef or "undef"'
40             if defined $_[0] && 'ARRAY' ne ref $_[0];
41             },
42             default => sub {
43             return;
44             },
45             );
46              
47             has 'add_inc' => (
48             is => 'ro',
49             isa => sub {
50             Carp::croak 'add_inc must be an ArrayRef or "undef"'
51             if defined $_[0] && ref $_[0] ne 'ARRAY';
52             },
53             default => sub {
54             return;
55             },
56             );
57              
58             has 'inc' => (
59             is => 'ro',
60             isa => sub {
61             Carp::croak 'inc must be an ArrayRef'
62             unless 'ARRAY' eq ref $_[0];
63             },
64             lazy => 1,
65             builder => 1,
66             );
67              
68             has 'args' => (
69             is => 'ro',
70             isa => sub {
71             Carp::croak 'options must be an ArrayRef'
72             unless 'ARRAY' eq ref $_[0];
73             },
74             default => sub {
75             return [];
76             },
77             );
78              
79             has 'on_error' => (
80             is => 'ro',
81             isa => sub {
82             Carp::croak 'on_error must be an CodeRef'
83             unless 'CODE' eq ref $_[0];
84             },
85             default => sub {
86             return \&Carp::croak;
87             },
88             );
89              
90             sub load {
91 7     7 1 1529 my ( $self, @modules ) = @_;
92              
93 7         11 my %result;
94 7         11 foreach my $module (@modules) {
95 25         487 $result{$module} = load_module(
96             module => $module,
97             prefix => $self->prefix,
98             on_error => $self->on_error,
99             inc => $self->inc,
100             );
101             }
102              
103 6         22 return \%result;
104             }
105              
106             sub build {
107 4     4 1 33 my ( $self, @modules ) = @_;
108              
109 4         4 my %result;
110 4         7 foreach my $module (@modules) {
111 16         61 $result{$module} = build_object(
112             module => $module,
113             builder => $self->builder,
114             args => $self->args,
115             );
116             }
117              
118 4         12 return \%result;
119             }
120              
121             sub load_and_build {
122 2     2 1 32 my ( $self, @modules ) = @_;
123              
124 2         6 my $loaded = $self->load(@modules);
125 2         4 my $built = $self->build( @{$loaded}{@modules} );
  2         10  
126              
127 2         9 return $built;
128             }
129              
130             sub fqn {
131 70   100 70 1 16898 my $module = shift || '';
132 70         81 my $prefix = shift;
133              
134 70 100       167 $module = $prefix . q{::} . $module
135             if $prefix;
136              
137 70 100       580 return $module =~ $MODULE_NAME_REGEX ? $module : '';
138             }
139              
140             sub load_module {
141 32     32 1 2205 my (%args) = @_;
142              
143 32         38 local @INC = @{ $args{inc} };
  32         102  
144              
145 32         92 my $module = fqn( $args{module}, $args{prefix} );
146 32   100     123 my $on_error = $args{on_error} || \&Carp::croak;
147 32         37 my $error;
148             {
149             ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
150 32         28 local $@ = '';
  32         40  
151 32     12   2003 eval "use $module;";
  12         3999  
  11         19274  
  11         372  
152 32         74 $error = $@;
153             ## use critic
154             }
155              
156 32 100       131 if ($error) {
157 1         2 $module = '';
158 1         22 $on_error->($error);
159             }
160              
161 31         152 return $module;
162             }
163              
164             sub build_object {
165 19     19 1 91 my (%args) = @_;
166              
167 19         76 my $module = $args{module};
168 19         22 my $builder = $args{builder};
169 19         18 my @args = @{ $args{args} };
  19         32  
170 19   100     77 my $on_error = $args{on_error} || \&Carp::croak;
171              
172 19         19 my ( $instance, $error );
173             {
174             ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
175 19         17 local $@ = '';
  19         25  
176 19         25 eval { $instance = $module->$builder(@args); };
  19         137  
177 19         14331 $error = $@;
178             ## use critic
179             }
180              
181 19 100       49 if ($error) {
182 1         19 $on_error->($error);
183             }
184              
185 18         65 return $instance;
186             }
187              
188             sub load_and_new {
189 1     1 1 17 my (%args) = @_;
190              
191 1         9 return build_object(
192             module => load_module(
193             module => $args{module},
194             prefix => $args{prefix},
195             inc => [@INC],
196             ),
197             builder => 'new',
198             args => $args{args},
199             );
200             }
201              
202             sub _build_inc {
203 8     8   4890 my ($self) = @_;
204              
205 8         16 my @inc = ();
206 8 100       44 if ( defined $self->set_inc ) {
    100          
207 5         6 push @inc, @{ $self->set_inc };
  5         19  
208             }
209             elsif ( defined $self->add_inc ) {
210 1         1 push @inc, @{ $self->add_inc }, @INC;
  1         9  
211             }
212             else {
213 2         9 push @inc, @INC;
214             }
215              
216 8         130 return \@inc;
217             }
218              
219             1;
220              
221             #ABSTRACT: A tiny module loader
222              
223             __END__
224              
225             =pod
226              
227             =encoding UTF-8
228              
229             =head1 NAME
230              
231             MAD::Loader - A tiny module loader
232              
233             =head1 VERSION
234              
235             version 3.001001_001
236              
237             =head1 SYNOPSIS
238              
239             MAD::loader is a module loader and object builder for situations when you
240             want several modules being loaded dynamically.
241              
242             For each module loaded this way an builder method may be called with
243             or without arguments. You may also control where the loader will search for
244             modules, you may prefix the module names with a custom namespace and you
245             may change how it will behave on getting errors.
246              
247             ## Procedural interface, for handling one module each time
248             use MAD::Loader qw{ fqn load_module build_object };
249            
250             my $fqn = fqn( 'My::Module', 'My::Prefix' );
251             # $fqn is 'My::Prefix::My::Module'
252            
253             my $module = load_module(
254             module => 'Bar',
255             prefix => 'Foo',
256             inc => [ 'my/local/lib' ],
257             on_error => \&error_handler,
258             );
259             # $module is 'Foo::Bar' if Foo::Bar was successfully loaded
260             # error_handler() will be called in case of error
261            
262             my $object = build_object(
263             module => 'Foo::Bar',
264             builder => 'new',
265             args => [ 123, 456 ],
266             on_error => \&error_handler,
267             );
268             # Foo::Bar must be already loaded
269             # $object = Foo::Bar->new( 123, 456 );
270            
271             ## OO interface, for handling many modules each time
272             use MAD::Loader;
273              
274             my $loader = MAD::Loader->new(
275             prefix => 'Foo',
276             set_inc => [ 'my/module/dir' ],
277             builder => 'new',
278             args => [ 123, 456 ],
279             on_error => \&error_handler,
280             );
281            
282             my $loaded = $loader->load( qw{ Bar Etc 123 } );
283             # Same as:
284             use Foo::Bar;
285             use Foo::Etc;
286             use Foo::123;
287            
288             my $built = $loader->build( qw{ Foo::Bar Foo::Etc Foo::123 } );
289             # Same as:
290             my $built = {
291             Foo::Bar => Foo::Bar->new( 123, 456 ),
292             Foo::Etc => Foo::Etc->new( 123, 456 ),
293             Foo::123 => Foo::123->new( 123, 456 ),
294             }
295            
296             my $built = $loader->load_and_build( qw{ Bar Etc 123 } );
297             # Same as:
298             use Foo::Bar;
299             use Foo::Etc;
300             use Foo::123;
301            
302             my $built = {
303             Foo::Bar => Foo::Bar->new( 123, 456 ),
304             Foo::Etc => Foo::Etc->new( 123, 456 ),
305             Foo::123 => Foo::123->new( 123, 456 ),
306             }
307              
308             =head1 FUNCTIONS
309              
310             =head2 fqn( $module [, $prefix] )
311              
312             This method is used to validate the full name of a C<$module>. If an optional
313             C<$prefix> is given, it will be prepended to the C<$module> before being
314             validated.
315              
316             The fqn is validated against the regular expression in C<$MODULE_NAME_REGEX>
317             which is C<qr{^[_[:lower:]]\w*(::\w+)*$}i>.
318              
319             If a valid fqn can not be found then an empty string is returned.
320              
321             Note that only the non-ascii characters recognized by C<[:upper:]> and C<\w>
322             can be part of the module name or prefix.
323              
324             Numbers are valid except for the B<first character> of the fqn.
325              
326             =head2 load_module( %args )
327              
328             Tries to load a single module.
329              
330             Receives as argument a hash containing the following keys:
331              
332             =head3 module (Mandatory)
333              
334             The module name.
335              
336             =head3 inc (Mandatory)
337              
338             An ArrayRef with the list of directories where to look for the module. This
339             replaces locally the array @INC.
340              
341             =head3 prefix (Optional)
342              
343             A namespace to prefix the module name. Defaults to C<''>.
344              
345             =head3 on_error (Optional)
346              
347             An error handler to be executed when found errors. Defaults to
348             C<\&Carp::croak>.
349              
350             =head2 build_object( %args )
351              
352             Tries to build an object from a loaded module.
353              
354             Receives as argument a hash containing the following keys:
355              
356             =head3 module (Mandatory)
357              
358             The module name.
359              
360             =head3 builder (Mandatory)
361              
362             The name of method used to build the object.
363              
364             =head3 args (Optional)
365              
366             An ArrayRef of parameters to be passed to the builder method.
367              
368             =head3 on_error (Optional)
369              
370             An error handler to be executed when found errors. Defaults to
371             C<\&Carp::croak>.
372              
373             =head2 load_and_new( %args )
374              
375             A shortcut for C<load_module> then C<build_object> with some predefined
376             args.
377              
378             C<inc> is set to C<@INC> and c<builder> to C<'new'>. It is expected to deal
379             only with module, prefix and builder args.
380              
381             =head1 METHODS
382              
383             =head2 new( %params )
384              
385             Creates a loader object.
386              
387             You may provide any optional arguments: B<prefix>, B<builder>,
388             B<args>, B<add_inc>, B<set_inc> and B<on_error>.
389              
390             =head3 prefix
391              
392             The namespace that will be prepended to the module names.
393              
394             The default value is '' (empty string) meaning that no prefix will be used.
395              
396             my $loader = MAD::Loader->new( prefix => 'Foo' );
397             $loader->load(qw{ Bar Etc 123 });
398            
399             ## This will load the modules:
400             ## * Foo::Bar
401             ## * Foo::Etc
402             ## * Foo::123
403              
404             =head3 builder
405              
406             The name of the method used to create a new object or to initialize the
407             module.
408              
409             The default value is C<''> (empty string).
410              
411             When an C<builder> is defined the loader will try to call it like as a
412             constructor passing the array C<args> as argument.
413              
414             The code below:
415              
416             my $loader = MAD::Loader->new(
417             builder => 'init',
418             args => [ 1, 2, 3 ],
419             );
420             $loader->load( 'Foo' );
421             $loader->build( 'Foo' );
422              
423             Will cause something like this to occur:
424              
425             use Foo;
426             Foo->init( 1, 2, 3 );
427              
428             =head3 args
429              
430             An ArrayRef with the arguments provided to all builders.
431              
432             Note that although C<args> is an ArrayRef, it will be passed as an B<array>
433             to C<builder>.
434              
435             When several modules are loaded together, the same C<args> will be passed
436             to their builders.
437              
438             =head3 add_inc
439              
440             An ArrayRef with directories to be prepended to C<@INC>.
441              
442             The array C<@INC> will be localized before the loader add these directories,
443             so the original state of C<@INC> will be preserved out of the loader.
444              
445             The default value is C<undef> meaning that original value of C<@INC> will be
446             used.
447              
448             =head3 set_inc
449              
450             An ArrayRef of directories used to override C<@INC>.
451              
452             This option has priority over C<add_inc>, that is, if C<set_inc>
453             is defined the value of C<add_inc> will be ignored.
454              
455             Again, C<@INC> will be localized internally so his original values will be
456             left untouched.
457              
458             =head3 on_error
459              
460             An error handler called when a module fails to load or build an object. His
461             only argument will be the exception thrown.
462              
463             This is a coderef and the default value is C<\&Carp::croak>.
464              
465             =head2 load( @modules )
466              
467             Takes a list of module names and tries to load all of them in order.
468              
469             For each module that fails to load, the error handler C<on_error> will be
470             called. Note that the default error handler is an alias to C<Carp::croak> so
471             in this case at the first fail, an exception will be thrown.
472              
473             All module names will be prefixed with the provided C<prefix> and the loader
474             will try to make sure that they all are valid before try to load them. All
475             modules marked as "invalid" will not be loaded.
476              
477             The term "invalid" is subject of discussion ahead.
478              
479             The loader will search for modules into directories pointed by C<@INC> which
480             may be changed by attributes C<add_inc> and C<set_inc>.
481              
482             In the end, if no exception was thrown, the method C<load> will return a
483             HashRef which the keys are the module names passed to it (without prefix)
484             and the values are the fqn (with prefix) of the module if it was loaded or an
485             empty string if it was not loaded.
486              
487             =head2 build( @modules )
488              
489             Takes a list of modules (fqn) already loaded and for each one, tries to
490             build an object calling the method indicated by C<builder>, passing to it the
491             arguments in C<args>.
492              
493             Returns a HashRef which the keys are the names of the modules and the
494             values are the objects.
495              
496             =head2 load_and_build( @modules )
497              
498             A mix of C<load> and C<build>. Receives a list of modules, tries to prepend
499             them with C<prefix>, load all and finally build an object for each one.
500              
501             Returns the same as C<build>.
502              
503             =head2 prefix
504              
505             Returns the namespace C<prefix> as described above.
506              
507             =head2 builder
508              
509             Returns the name of the C<builder> as described above.
510              
511             =head2 args
512              
513             Returns an ArrayRef with the C<args> provided to all builders.
514              
515             =head2 add_inc
516              
517             Returns the ArrayRef of directories prepended to C<@INC>.
518              
519             =head2 set_inc
520              
521             Returns the ArrayRef of directories used to override C<@INC>.
522              
523             =head2 inc
524              
525             Returns the ArrayRef of directories that represents the content of C<@INC>
526             internally into the loader.
527              
528             =head2 on_error
529              
530             Returns the CodeRef of the error handler.
531              
532             =head1 LIMITATIONS
533              
534             =head2 Valid Module Names
535              
536             This module tries to define what is a valid module name. Arbitrarily we
537             consider a valid module name whatever module that matches with the regular
538             expression C<qr{^[_[:upper:]]\w*(::\w+)*$}i>.
539              
540             This validation is to avoid injection of arbitrarily code as fake module
541             names and the regular expression above should be changed in future versions
542             or a better approach may be considered.
543              
544             Therefore some valid module names are considered invalid within
545             C<MAD::Loader> as names with some UTF-8 characters for example.
546             These modules cannot be loaded by C<MAD::Loader> yet. For now this B<IS>
547             intentional.
548              
549             The old package delimiter C<'> (single quote) is also intentionally ignored
550             in favor of C<::> (double colon). Modules with single quote as package
551             delimiter cannot be loaded by C<MAD::Loader>.
552              
553             =head1 CAVEATS
554              
555             The options C<add_inc> and C<set_inc> are used to isolate the environment
556             where the search by modules is made, allowing you precisely control where
557             MAD::Loader will look for modules.
558              
559             You may use this features when your application must load plugins and you
560             must assure that only modules within specific directories can be valid
561             plugins for example.
562              
563             A collateral effect is that when a module loaded by MAD::Loader tries to
564             dynamically load another module, this module will be searched only within
565             the directories known by MAD::Laoder.
566              
567             If you use the option C<set_inc> to limitate MAD::Loader to search only
568             within the directory C</my/plugins> for example, and some plugin tries to
569             load a module placed out of this path, your plugin will fail like this:
570              
571             Can't locate SomeModule.pm in @INC (@INC contains: /my/plugins) at
572             /my/plugins/Myplugin.pm line 42.
573              
574             Note that actually this is a feature, not a bug. If you isolate the search
575             path with MAD::Loader you will be sure that no module will bypass your
576             limitation, except if it know the search path of his sub-modules by itself
577             (in this case, there is little to do :) ).
578              
579             See L<https://github.com/blabos/MAD-Loader/issues/1> for an example.
580              
581             =head1 AUTHOR
582              
583             Blabos de Blebe <blabos@cpan.org>
584              
585             =head1 COPYRIGHT AND LICENSE
586              
587             This software is copyright (c) 2014 by Blabos de Blebe.
588              
589             This is free software; you can redistribute it and/or modify it under
590             the same terms as the Perl 5 programming language system itself.
591              
592             =cut