File Coverage

blib/lib/Mojo/Loader.pm
Criterion Covered Total %
statement 70 70 100.0
branch 29 30 96.6
condition 8 10 80.0
subroutine 14 14 100.0
pod 6 6 100.0
total 127 130 97.6


line stmt bran cond sub pod time code
1             package Mojo::Loader;
2 68     68   1441 use Mojo::Base -strict;
  68         163  
  68         612  
3              
4 68     68   506 use Exporter qw(import);
  68         184  
  68         3994  
5 68     68   7784 use Mojo::Exception;
  68         186  
  68         3895  
6 68     68   2481 use Mojo::File qw(path);
  68         362  
  68         5428  
7 68     68   493 use Mojo::Util qw(b64_decode class_to_path deprecated);
  68         166  
  68         52245  
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 653 100   653 1 14890 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 21 100   21 1 10500 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  21         77  
16              
17             sub find_modules {
18 105   100 105 1 7306 my ($ns, $options) = (shift, shift // {});
19              
20 105         584 my @ns = split /::/, $ns;
21 105         426 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  840         22851  
  840         2121  
22              
23 105         524 my %modules;
24 105         395 for my $dir (@inc) {
25 37 100       378 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       2485 next unless $$file =~ s/\.pm$//;
27 146         281 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         554  
28             }
29             }
30              
31 105         1025 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 7039 my $ns = shift;
36 68     68   658 no strict 'refs';
  68         152  
  68         50290  
37 14 100       33 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         4766  
  14         234  
38             }
39              
40             sub load_class {
41 1032     1032 1 376618 my $class = shift;
42              
43             # Invalid class name
44 1032 100 50     13601 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45 1025 50       3665 deprecated
46             q{Calling Mojo::Loader::load_class with a class name using the old package separator "'" is deprecated; use "::"}
47             if $class =~ m/'/;
48              
49             # Load if not already loaded
50 1025 100 100     79439 return undef if $class->can('new') || eval "require $class; 1";
51              
52             # Does not exist
53 118 100       2234 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  118         571  
54              
55             # Real error
56 12         187 return Mojo::Exception->new($@)->inspect;
57             }
58              
59             sub load_classes {
60 88     88 1 10960 my $ns = shift;
61              
62 88         218 my @classes;
63 88         617 for my $module (find_modules($ns, {recursive => 1})) {
64 14 100       56 push @classes, $module unless my $e = load_class($module);
65 14 100       70 die $e if ref $e;
66             }
67              
68 87         515 return @classes;
69             }
70              
71             sub _all {
72 406     406   940 my $class = shift;
73              
74 406 100       3598 return $CACHE{$class} if $CACHE{$class};
75 104         534 local $.;
76 68     68   624 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  68         149  
  68         33294  
  104         280  
  104         234  
  104         697  
77 104 100       745 return {} unless fileno $handle;
78 38         504 seek $handle, 0, 0;
79 38         7429 my $data = join '', <$handle>;
80              
81             # Ignore everything before __DATA__ (some versions seek to start of file)
82 38         2838 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
83              
84             # Ignore everything after __END__
85 38         214 $data =~ s/\n__END__\r?\n.*$/\n/s;
86              
87             # Split files
88 38         1251 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
89              
90             # Find data
91 38         276 my $all = $CACHE{$class} = {};
92 38         211 while (@files) {
93 160         359 my ($name, $data) = splice @files, 0, 2;
94 160 100 66     944 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
95             }
96              
97 38         605 return $all;
98             }
99              
100             1;
101              
102             =encoding utf8
103              
104             =head1 NAME
105              
106             Mojo::Loader - Load all kinds of things
107              
108             =head1 SYNOPSIS
109              
110             use Mojo::Loader qw(data_section find_modules load_class);
111              
112             # Find modules in a namespace
113             for my $module (find_modules 'Some::Namespace') {
114              
115             # Load them safely
116             my $e = load_class $module;
117             warn qq{Loading "$module" failed: $e} and next if ref $e;
118              
119             # And extract files from the DATA section
120             say data_section($module, 'some_file.txt');
121             }
122              
123             =head1 DESCRIPTION
124              
125             L is a class loader and plugin framework. Aside from finding modules and loading classes, it allows
126             multiple files to be stored in the C section of a class, which can then be accessed individually.
127              
128             package Foo;
129              
130             1;
131             __DATA__
132              
133             @@ test.txt
134             This is the first file.
135              
136             @@ test2.html (base64)
137             VGhpcyBpcyB0aGUgc2Vjb25kIGZpbGUu
138              
139             @@ test
140             This is the
141             third file.
142              
143             Each file has a header starting with C<@@>, followed by the file name and optional instructions for decoding its
144             content. Currently only the Base64 encoding is supported, which can be quite convenient for the storage of binary data.
145              
146             =head1 FUNCTIONS
147              
148             L implements the following functions, which can be imported individually.
149              
150             =head2 data_section
151              
152             my $all = data_section 'Foo::Bar';
153             my $index = data_section 'Foo::Bar', 'index.html';
154              
155             Extract embedded file from the C section of a class, all files will be cached once they have been accessed for
156             the first time.
157              
158             # List embedded files
159             say for keys %{data_section 'Foo::Bar'};
160              
161             =head2 file_is_binary
162              
163             my $bool = file_is_binary 'Foo::Bar', 'test.png';
164              
165             Check if embedded file from the C section of a class was Base64 encoded.
166              
167             =head2 find_packages
168              
169             my @pkgs = find_packages 'MyApp::Namespace';
170              
171             Search for packages in a namespace non-recursively.
172              
173             =head2 find_modules
174              
175             my @modules = find_modules 'MyApp::Namespace';
176             my @modules = find_modules 'MyApp::Namespace', {recursive => 1};
177              
178             Search for modules in a namespace.
179              
180             These options are currently available:
181              
182             =over 2
183              
184             =item recursive
185              
186             recursive => 1
187              
188             Search namespace recursively.
189              
190             =back
191              
192             =head2 load_class
193              
194             my $e = load_class 'Foo::Bar';
195              
196             Load a class and catch exceptions, returns a false value if loading was successful, a true value if the class was not
197             found, or a L object if loading failed. Note that classes are checked for a C method to see if
198             they are already loaded, so trying to load the same class multiple times may yield different results.
199              
200             # Handle exceptions
201             if (my $e = load_class 'Foo::Bar') {
202             die ref $e ? "Exception: $e" : 'Not found!';
203             }
204              
205             =head2 load_classes
206              
207             my @classes = load_classes 'Foo::Bar';
208              
209             Load all classes in a namespace recursively.
210              
211             =head1 SEE ALSO
212              
213             L, L, L.
214              
215             =cut