File Coverage

blib/lib/Mojo/Loader.pm
Criterion Covered Total %
statement 69 69 100.0
branch 28 28 100.0
condition 8 10 80.0
subroutine 14 14 100.0
pod 6 6 100.0
total 125 127 98.4


line stmt bran cond sub pod time code
1             package Mojo::Loader;
2 64     64   1154 use Mojo::Base -strict;
  64         152  
  64         466  
3              
4 64     64   471 use Exporter qw(import);
  64         198  
  64         2170  
5 64     64   4602 use Mojo::Exception;
  64         158  
  64         2789  
6 64     64   1784 use Mojo::File qw(path);
  64         159  
  64         3542  
7 64     64   434 use Mojo::Util qw(b64_decode class_to_path);
  64         143  
  64         37068  
8              
9             our @EXPORT_OK = qw(data_section file_is_binary find_modules find_packages load_class load_classes);
10              
11             my (%BIN, %CACHE);
12              
13 631 100   631 1 9119 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 19 100   19 1 5974 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  19         59  
16              
17             sub find_modules {
18 100   100 100 1 3182 my ($ns, $options) = (shift, shift // {});
19              
20 100         535 my @ns = split /::/, $ns;
21 100         380 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  1044         13475  
  1044         2199  
22              
23 100         514 my %modules;
24 100         379 for my $dir (@inc) {
25 37 100       314 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       1561 next unless $$file =~ s/\.pm$//;
27 146         261 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         395  
28             }
29             }
30              
31 100         903 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 3288 my $ns = shift;
36 64     64   579 no strict 'refs';
  64         154  
  64         30968  
37 14 100       26 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         825  
  14         158  
38             }
39              
40             sub load_class {
41 1003     1003 1 16086 my $class = shift;
42              
43             # Invalid class name
44 1003 100 50     6558 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45              
46             # Load if not already loaded
47 996 100 100     51731 return undef if $class->can('new') || eval "require $class; 1";
48              
49             # Does not exist
50 118 100       1557 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  118         522  
51              
52             # Real error
53 12         154 return Mojo::Exception->new($@)->inspect;
54             }
55              
56             sub load_classes {
57 83     83 1 6040 my $ns = shift;
58              
59 83         200 my @classes;
60 83         525 for my $module (find_modules($ns, {recursive => 1})) {
61 14 100       43 push @classes, $module unless my $e = load_class($module);
62 14 100       67 die $e if ref $e;
63             }
64              
65 82         390 return @classes;
66             }
67              
68             sub _all {
69 391     391   846 my $class = shift;
70              
71 391 100       3073 return $CACHE{$class} if $CACHE{$class};
72 96         470 local $.;
73 64     64   547 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  64         229  
  64         23772  
  96         203  
  96         158  
  96         555  
74 96 100       1255 return {} unless fileno $handle;
75 37         583 seek $handle, 0, 0;
76 37         5997 my $data = join '', <$handle>;
77              
78             # Ignore everything before __DATA__ (some versions seek to start of file)
79 37         3858 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
80              
81             # Ignore everything after __END__
82 37         369 $data =~ s/\n__END__\r?\n.*$/\n/s;
83              
84             # Split files
85 37         998 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
86              
87             # Find data
88 37         227 my $all = $CACHE{$class} = {};
89 37         370 while (@files) {
90 155         401 my ($name, $data) = splice @files, 0, 2;
91 155 100 66     940 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
92             }
93              
94 37         578 return $all;
95             }
96              
97             1;
98              
99             =encoding utf8
100              
101             =head1 NAME
102              
103             Mojo::Loader - Load all kinds of things
104              
105             =head1 SYNOPSIS
106              
107             use Mojo::Loader qw(data_section find_modules load_class);
108              
109             # Find modules in a namespace
110             for my $module (find_modules 'Some::Namespace') {
111              
112             # Load them safely
113             my $e = load_class $module;
114             warn qq{Loading "$module" failed: $e} and next if ref $e;
115              
116             # And extract files from the DATA section
117             say data_section($module, 'some_file.txt');
118             }
119              
120             =head1 DESCRIPTION
121              
122             L is a class loader and plugin framework. Aside from finding modules and loading classes, it allows
123             multiple files to be stored in the C section of a class, which can then be accessed individually.
124              
125             package Foo;
126              
127             1;
128             __DATA__
129              
130             @@ test.txt
131             This is the first file.
132              
133             @@ test2.html (base64)
134             VGhpcyBpcyB0aGUgc2Vjb25kIGZpbGUu
135              
136             @@ test
137             This is the
138             third file.
139              
140             Each file has a header starting with C<@@>, followed by the file name and optional instructions for decoding its
141             content. Currently only the Base64 encoding is supported, which can be quite convenient for the storage of binary data.
142              
143             =head1 FUNCTIONS
144              
145             L implements the following functions, which can be imported individually.
146              
147             =head2 data_section
148              
149             my $all = data_section 'Foo::Bar';
150             my $index = data_section 'Foo::Bar', 'index.html';
151              
152             Extract embedded file from the C section of a class, all files will be cached once they have been accessed for
153             the first time.
154              
155             # List embedded files
156             say for keys %{data_section 'Foo::Bar'};
157              
158             =head2 file_is_binary
159              
160             my $bool = file_is_binary 'Foo::Bar', 'test.png';
161              
162             Check if embedded file from the C section of a class was Base64 encoded.
163              
164             =head2 find_packages
165              
166             my @pkgs = find_packages 'MyApp::Namespace';
167              
168             Search for packages in a namespace non-recursively.
169              
170             =head2 find_modules
171              
172             my @modules = find_modules 'MyApp::Namespace';
173             my @modules = find_modules 'MyApp::Namespace', {recursive => 1};
174              
175             Search for modules in a namespace.
176              
177             These options are currently available:
178              
179             =over 2
180              
181             =item recursive
182              
183             recursive => 1
184              
185             Search namespace recursively.
186              
187             =back
188              
189             =head2 load_class
190              
191             my $e = load_class 'Foo::Bar';
192              
193             Load a class and catch exceptions, returns a false value if loading was successful, a true value if the class was not
194             found, or a L object if loading failed. Note that classes are checked for a C method to see if
195             they are already loaded, so trying to load the same class multiple times may yield different results.
196              
197             # Handle exceptions
198             if (my $e = load_class 'Foo::Bar') {
199             die ref $e ? "Exception: $e" : 'Not found!';
200             }
201              
202             =head2 load_classes
203              
204             my @classes = load_classes 'Foo::Bar';
205              
206             Load all classes in a namespace recursively.
207              
208             =head1 SEE ALSO
209              
210             L, L, L.
211              
212             =cut