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   1008 use Mojo::Base -strict;
  68         115  
  68         386  
3              
4 68     68   333 use Exporter qw(import);
  68         108  
  68         2146  
5 68     68   4086 use Mojo::Exception;
  68         102  
  68         2702  
6 68     68   1882 use Mojo::File qw(path);
  68         108  
  68         3246  
7 68     68   302 use Mojo::Util qw(b64_decode class_to_path deprecated);
  68         114  
  68         34991  
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 8216 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 21 100   21 1 5093 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  21         43  
16              
17             sub find_modules {
18 105   100 105 1 3462 my ($ns, $options) = (shift, shift // {});
19              
20 105         440 my @ns = split /::/, $ns;
21 105         292 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  840         7581  
  840         1389  
22              
23 105         341 my %modules;
24 105         249 for my $dir (@inc) {
25 37 100       227 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       1001 next unless $$file =~ s/\.pm$//;
27 146         175 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         320  
28             }
29             }
30              
31 105         691 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 3117 my $ns = shift;
36 68     68   467 no strict 'refs';
  68         108  
  68         34117  
37 14 100       19 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         643  
  14         173  
38             }
39              
40             sub load_class {
41 1037     1037 1 161490 my $class = shift;
42              
43             # Invalid class name
44 1037 100 50     5787 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45 1030 50       2029 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 1030 100 100     50562 return undef if $class->can('new') || eval "require $class; 1";
51              
52             # Does not exist
53 120 100       1277 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  120         376  
54              
55             # Real error
56 12         141 return Mojo::Exception->new($@)->inspect;
57             }
58              
59             sub load_classes {
60 88     88 1 5668 my $ns = shift;
61              
62 88         146 my @classes;
63 88         406 for my $module (find_modules($ns, {recursive => 1})) {
64 14 100       38 push @classes, $module unless my $e = load_class($module);
65 14 100       47 die $e if ref $e;
66             }
67              
68 87         376 return @classes;
69             }
70              
71             sub _all {
72 406     406   674 my $class = shift;
73              
74 406 100       2348 return $CACHE{$class} if $CACHE{$class};
75 104         368 local $.;
76 68     68   410 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  68         110  
  68         22405  
  104         154  
  104         145  
  104         492  
77 104 100       626 return {} unless fileno $handle;
78 38         409 seek $handle, 0, 0;
79 38         4767 my $data = join '', <$handle>;
80              
81             # Ignore everything before __DATA__ (some versions seek to start of file)
82 38         2242 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
83              
84             # Ignore everything after __END__
85 38         137 $data =~ s/\n__END__\r?\n.*$/\n/s;
86              
87             # Split files
88 38         801 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
89              
90             # Find data
91 38         147 my $all = $CACHE{$class} = {};
92 38         125 while (@files) {
93 160         277 my ($name, $data) = splice @files, 0, 2;
94 160 100 66     576 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
95             }
96              
97 38         394 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