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   1414 use Mojo::Base -strict;
  68         140  
  68         515  
3              
4 68     68   457 use Exporter qw(import);
  68         127  
  68         2792  
5 68     68   5616 use Mojo::Exception;
  68         205  
  68         3841  
6 68     68   2096 use Mojo::File qw(path);
  68         207  
  68         5462  
7 68     68   451 use Mojo::Util qw(b64_decode class_to_path deprecated);
  68         118  
  68         47544  
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 10540 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 21 100   21 1 6712 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  21         58  
16              
17             sub find_modules {
18 105   100 105 1 3790 my ($ns, $options) = (shift, shift // {});
19              
20 105         614 my @ns = split /::/, $ns;
21 105         402 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  840         14337  
  840         2132  
22              
23 105         523 my %modules;
24 105         365 for my $dir (@inc) {
25 37 100       440 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       1879 next unless $$file =~ s/\.pm$//;
27 146         313 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         459  
28             }
29             }
30              
31 105         982 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 4051 my $ns = shift;
36 68     68   818 no strict 'refs';
  68         152  
  68         45844  
37 14 100       40 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         1167  
  14         259  
38             }
39              
40             sub load_class {
41 1032     1032 1 249077 my $class = shift;
42              
43             # Invalid class name
44 1032 100 50     8487 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45 1025 50       9216 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     73287 return undef if $class->can('new') || eval "require $class; 1";
51              
52             # Does not exist
53 118 100       1827 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  118         656  
54              
55             # Real error
56 12         111 return Mojo::Exception->new($@)->inspect;
57             }
58              
59             sub load_classes {
60 88     88 1 12862 my $ns = shift;
61              
62 88         217 my @classes;
63 88         560 for my $module (find_modules($ns, {recursive => 1})) {
64 14 100       52 push @classes, $module unless my $e = load_class($module);
65 14 100       60 die $e if ref $e;
66             }
67              
68 87         556 return @classes;
69             }
70              
71             sub _all {
72 406     406   924 my $class = shift;
73              
74 406 100       3629 return $CACHE{$class} if $CACHE{$class};
75 104         479 local $.;
76 68     68   599 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  68         129  
  68         30647  
  104         262  
  104         176  
  104         794  
77 104 100       840 return {} unless fileno $handle;
78 38         457 seek $handle, 0, 0;
79 38         7449 my $data = join '', <$handle>;
80              
81             # Ignore everything before __DATA__ (some versions seek to start of file)
82 38         2830 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
83              
84             # Ignore everything after __END__
85 38         219 $data =~ s/\n__END__\r?\n.*$/\n/s;
86              
87             # Split files
88 38         1362 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
89              
90             # Find data
91 38         203 my $all = $CACHE{$class} = {};
92 38         157 while (@files) {
93 160         406 my ($name, $data) = splice @files, 0, 2;
94 160 100 66     899 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
95             }
96              
97 38         568 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