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   978 use Mojo::Base -strict;
  68         111  
  68         365  
3              
4 68     68   338 use Exporter qw(import);
  68         111  
  68         2039  
5 68     68   3854 use Mojo::Exception;
  68         103  
  68         2621  
6 68     68   1721 use Mojo::File qw(path);
  68         112  
  68         3196  
7 68     68   301 use Mojo::Util qw(b64_decode class_to_path deprecated);
  68         107  
  68         36639  
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 8388 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 21 100   21 1 5202 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  21         46  
16              
17             sub find_modules {
18 105   100 105 1 3636 my ($ns, $options) = (shift, shift // {});
19              
20 105         412 my @ns = split /::/, $ns;
21 105         264 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  840         7837  
  840         1386  
22              
23 105         334 my %modules;
24 105         275 for my $dir (@inc) {
25 37 100       236 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       959 next unless $$file =~ s/\.pm$//;
27 146         165 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         308  
28             }
29             }
30              
31 105         658 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 3189 my $ns = shift;
36 68     68   1188 no strict 'refs';
  68         362  
  68         36077  
37 14 100       22 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         720  
  14         152  
38             }
39              
40             sub load_class {
41 1032     1032 1 159020 my $class = shift;
42              
43             # Invalid class name
44 1032 100 50     6473 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45 1025 50       2908 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     57464 return undef if $class->can('new') || eval "require $class; 1";
51              
52             # Does not exist
53 118 100       1299 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  118         391  
54              
55             # Real error
56 12         91 return Mojo::Exception->new($@)->inspect;
57             }
58              
59             sub load_classes {
60 88     88 1 5859 my $ns = shift;
61              
62 88         135 my @classes;
63 88         397 for my $module (find_modules($ns, {recursive => 1})) {
64 14 100       35 push @classes, $module unless my $e = load_class($module);
65 14 100       47 die $e if ref $e;
66             }
67              
68 87         346 return @classes;
69             }
70              
71             sub _all {
72 406     406   582 my $class = shift;
73              
74 406 100       2568 return $CACHE{$class} if $CACHE{$class};
75 104         397 local $.;
76 68     68   424 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  68         435  
  68         22770  
  104         148  
  104         125  
  104         541  
77 104 100       562 return {} unless fileno $handle;
78 38         402 seek $handle, 0, 0;
79 38         4740 my $data = join '', <$handle>;
80              
81             # Ignore everything before __DATA__ (some versions seek to start of file)
82 38         2308 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
83              
84             # Ignore everything after __END__
85 38         135 $data =~ s/\n__END__\r?\n.*$/\n/s;
86              
87             # Split files
88 38         830 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
89              
90             # Find data
91 38         150 my $all = $CACHE{$class} = {};
92 38         119 while (@files) {
93 160         266 my ($name, $data) = splice @files, 0, 2;
94 160 100 66     657 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
95             }
96              
97 38         437 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