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   1161 use Mojo::Base -strict;
  64         135  
  64         455  
3              
4 64     64   416 use Exporter qw(import);
  64         166  
  64         2170  
5 64     64   4516 use Mojo::Exception;
  64         159  
  64         2794  
6 64     64   1847 use Mojo::File qw(path);
  64         150  
  64         3514  
7 64     64   453 use Mojo::Util qw(b64_decode class_to_path);
  64         140  
  64         36363  
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 8315 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 19 100   19 1 5901 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  19         48  
16              
17             sub find_modules {
18 100   100 100 1 3035 my ($ns, $options) = (shift, shift // {});
19              
20 100         460 my @ns = split /::/, $ns;
21 100         341 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  1044         12334  
  1044         2237  
22              
23 100         463 my %modules;
24 100         331 for my $dir (@inc) {
25 37 100       289 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       1595 next unless $$file =~ s/\.pm$//;
27 146         351 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         409  
28             }
29             }
30              
31 100         887 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 3181 my $ns = shift;
36 64     64   540 no strict 'refs';
  64         151  
  64         30716  
37 14 100       28 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         802  
  14         147  
38             }
39              
40             sub load_class {
41 1003     1003 1 15717 my $class = shift;
42              
43             # Invalid class name
44 1003 100 50     6369 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45              
46             # Load if not already loaded
47 996 100 100     49207 return undef if $class->can('new') || eval "require $class; 1";
48              
49             # Does not exist
50 118 100       1485 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  118         427  
51              
52             # Real error
53 12         112 return Mojo::Exception->new($@)->inspect;
54             }
55              
56             sub load_classes {
57 83     83 1 5831 my $ns = shift;
58              
59 83         189 my @classes;
60 83         490 for my $module (find_modules($ns, {recursive => 1})) {
61 14 100       41 push @classes, $module unless my $e = load_class($module);
62 14 100       66 die $e if ref $e;
63             }
64              
65 82         418 return @classes;
66             }
67              
68             sub _all {
69 391     391   798 my $class = shift;
70              
71 391 100       2863 return $CACHE{$class} if $CACHE{$class};
72 96         403 local $.;
73 64     64   522 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  64         213  
  64         22656  
  96         207  
  96         159  
  96         571  
74 96 100       1066 return {} unless fileno $handle;
75 37         521 seek $handle, 0, 0;
76 37         5646 my $data = join '', <$handle>;
77              
78             # Ignore everything before __DATA__ (some versions seek to start of file)
79 37         3624 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
80              
81             # Ignore everything after __END__
82 37         382 $data =~ s/\n__END__\r?\n.*$/\n/s;
83              
84             # Split files
85 37         916 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
86              
87             # Find data
88 37         181 my $all = $CACHE{$class} = {};
89 37         347 while (@files) {
90 155         420 my ($name, $data) = splice @files, 0, 2;
91 155 100 66     850 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
92             }
93              
94 37         562 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