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 65     65   1076 use Mojo::Base -strict;
  65         146  
  65         479  
3              
4 65     65   440 use Exporter qw(import);
  65         170  
  65         2164  
5 65     65   4724 use Mojo::Exception;
  65         152  
  65         3053  
6 65     65   1894 use Mojo::File qw(path);
  65         157  
  65         3399  
7 65     65   480 use Mojo::Util qw(b64_decode class_to_path);
  65         232  
  65         38480  
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 646 100   646 1 8748 sub data_section { $_[0] ? $_[1] ? _all($_[0])->{$_[1]} : _all($_[0]) : undef }
    100          
14              
15 21 100   21 1 6063 sub file_is_binary { keys %{_all($_[0])} ? !!$BIN{$_[0]}{$_[1]} : undef }
  21         64  
16              
17             sub find_modules {
18 101   100 101 1 3114 my ($ns, $options) = (shift, shift // {});
19              
20 101         567 my @ns = split /::/, $ns;
21 101         359 my @inc = grep { -d $$_ } map { path($_, @ns) } @INC;
  1055         13939  
  1055         2239  
22              
23 101         618 my %modules;
24 101         412 for my $dir (@inc) {
25 37 100       349 for my $file ($options->{recursive} ? $dir->list_tree->each : $dir->list->each) {
26 152 100       1701 next unless $$file =~ s/\.pm$//;
27 146         364 $modules{join('::', $ns, @{$file->to_rel($$dir)})}++;
  146         381  
28             }
29             }
30              
31 101         1016 return sort keys %modules;
32             }
33              
34             sub find_packages {
35 14     14 1 3211 my $ns = shift;
36 65     65   572 no strict 'refs';
  65         144  
  65         31827  
37 14 100       21 return sort map { /^(.+)::$/ ? "${ns}::$1" : () } keys %{"${ns}::"};
  273         821  
  14         179  
38             }
39              
40             sub load_class {
41 1014     1014 1 16088 my $class = shift;
42              
43             # Invalid class name
44 1014 100 50     6746 return 1 if ($class || '') !~ /^\w(?:[\w:']*\w)?$/;
45              
46             # Load if not already loaded
47 1007 100 100     52947 return undef if $class->can('new') || eval "require $class; 1";
48              
49             # Does not exist
50 120 100       1592 return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $class]}\E in \@INC/;
  120         521  
51              
52             # Real error
53 12         504 return Mojo::Exception->new($@)->inspect;
54             }
55              
56             sub load_classes {
57 84     84 1 6127 my $ns = shift;
58              
59 84         199 my @classes;
60 84         511 for my $module (find_modules($ns, {recursive => 1})) {
61 14 100       40 push @classes, $module unless my $e = load_class($module);
62 14 100       74 die $e if ref $e;
63             }
64              
65 83         415 return @classes;
66             }
67              
68             sub _all {
69 399     399   1338 my $class = shift;
70              
71 399 100       3074 return $CACHE{$class} if $CACHE{$class};
72 97         498 local $.;
73 65     65   574 my $handle = do { no strict 'refs'; \*{"${class}::DATA"} };
  65         221  
  65         24189  
  97         217  
  97         175  
  97         571  
74 97 100       757 return {} unless fileno $handle;
75 38         603 seek $handle, 0, 0;
76 38         6021 my $data = join '', <$handle>;
77              
78             # Ignore everything before __DATA__ (some versions seek to start of file)
79 38         4011 $data =~ s/^.*\n__DATA__\r?\n/\n/s;
80              
81             # Ignore everything after __END__
82 38         445 $data =~ s/\n__END__\r?\n.*$/\n/s;
83              
84             # Split files
85 38         1087 (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data;
86              
87             # Find data
88 38         317 my $all = $CACHE{$class} = {};
89 38         185 while (@files) {
90 160         387 my ($name, $data) = splice @files, 0, 2;
91 160 100 66     1022 $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// && ++$BIN{$class}{$name} ? b64_decode $data : $data;
92             }
93              
94 38         601 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