line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Module::Lazy; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
506156
|
use 5.008; |
|
11
|
|
|
|
|
113
|
|
4
|
11
|
|
|
11
|
|
58
|
use strict; |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
264
|
|
5
|
11
|
|
|
11
|
|
55
|
use warnings; |
|
11
|
|
|
|
|
19
|
|
|
11
|
|
|
|
|
547
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Module::Lazy - postpone loading a module until it's actually used |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use Module::Lazy "My::Module"; |
15
|
|
|
|
|
|
|
# My::Module has not been loaded |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $var = My::Module->new; |
18
|
|
|
|
|
|
|
# My::Module is loaded now, and new() method is called |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
no Module::Lazy; |
21
|
|
|
|
|
|
|
# Force loading of all postponed modules |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
In large projects loading all the dependencies may take a lot of time. |
26
|
|
|
|
|
|
|
This module attempts to reduce the startup time by postponing initialization. |
27
|
|
|
|
|
|
|
The improvement be significant for unit test scripts |
28
|
|
|
|
|
|
|
and small command-line tools |
29
|
|
|
|
|
|
|
which do not utilize all the functionality at once. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This comes at a cost of reduced stability, |
32
|
|
|
|
|
|
|
as load-time errors are also postponed. |
33
|
|
|
|
|
|
|
The C directive is provided to mitigate the risk |
34
|
|
|
|
|
|
|
by forcing the pending modules to load. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 EXPORTED FUNCTIONS |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
None. |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 METHODS |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=cut |
43
|
|
|
|
|
|
|
|
44
|
11
|
|
|
11
|
|
75
|
use Carp; |
|
11
|
|
|
|
|
37
|
|
|
11
|
|
|
|
|
8897
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head2 import |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
When C |
49
|
|
|
|
|
|
|
the module in question is not loaded. |
50
|
|
|
|
|
|
|
A stub package with the same name is created instead. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Should any method call be performed on the stub package, |
53
|
|
|
|
|
|
|
it loads the original one and jumps to respective method. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
In particular, C and C are overloaded |
56
|
|
|
|
|
|
|
and will trigger module loading. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Upon loading, C is not called on the target package. |
59
|
|
|
|
|
|
|
This MAY change in the future. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
No extra options (except from target module name) are allowed. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $dont; |
66
|
|
|
|
|
|
|
my %seen; |
67
|
|
|
|
|
|
|
my $inc_stub = "pending load by ".__PACKAGE__; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub import { |
70
|
14
|
|
|
14
|
|
1469
|
my ($class, $target, @rest) = @_; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# bare use statement is ok |
73
|
14
|
100
|
|
|
|
63
|
return unless defined $target; |
74
|
|
|
|
|
|
|
|
75
|
13
|
100
|
|
|
|
66
|
croak "Usage: use Module::Lazy 'Module::Name'; extra options not supported" |
76
|
|
|
|
|
|
|
unless @rest == 0; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# return ASAP if already loaded by us or Perl itself |
79
|
12
|
100
|
|
|
|
44
|
return if $seen{$target}; |
80
|
11
|
|
|
|
|
19
|
my $mod = $target; |
81
|
11
|
|
|
|
|
70
|
$mod =~ s,::,/,g; |
82
|
11
|
|
|
|
|
24
|
$mod .= ".pm"; |
83
|
|
|
|
|
|
|
|
84
|
11
|
50
|
|
|
|
38
|
return if $INC{$mod}; |
85
|
11
|
50
|
|
|
|
44
|
return _load( $target, $mod ) |
86
|
|
|
|
|
|
|
if $dont; |
87
|
|
|
|
|
|
|
|
88
|
11
|
50
|
|
|
|
115
|
croak "Bad module name '$target'" |
89
|
|
|
|
|
|
|
unless $target =~ /^[A-Za-z_][A-Za-z_0-9]*(?:::[A-Za-z_0-9]+)*$/; |
90
|
|
|
|
|
|
|
|
91
|
11
|
|
|
|
|
37
|
$seen{$target} = $mod; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# If $target is later require'd directly, |
94
|
|
|
|
|
|
|
# autoload and destroy will be overwritten and will cause a warning. |
95
|
|
|
|
|
|
|
# Preventing them from being loaded seems like a lesser evil. |
96
|
11
|
|
|
|
|
24
|
$INC{$mod} = $inc_stub; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
_set_function( $target, AUTOLOAD => sub { |
99
|
4
|
|
|
4
|
|
1578
|
our $AUTOLOAD; |
100
|
4
|
|
|
|
|
29
|
$AUTOLOAD =~ s/.*:://; |
101
|
4
|
|
|
|
|
15
|
my $jump = _jump( $target, $AUTOLOAD ); |
102
|
4
|
|
|
|
|
17
|
goto $jump; |
103
|
11
|
|
|
|
|
66
|
} ); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Provide DESTROY just in case someone blesses an object directly |
106
|
|
|
|
|
|
|
# without ever loading a module |
107
|
11
|
|
|
|
|
34
|
_set_function( $target, DESTROY => _jump( $target, DESTROY => "no_die" ) ); |
108
|
|
|
|
|
|
|
|
109
|
11
|
|
|
|
|
31
|
foreach (qw( can isa )) { |
110
|
22
|
|
|
|
|
48
|
_set_function( $target, $_ => _jump( $target, $_ ) ); |
111
|
|
|
|
|
|
|
}; |
112
|
|
|
|
|
|
|
}; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 unimport |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Calling C or, alternatively, Cunimport;> |
117
|
|
|
|
|
|
|
will cause all postponed modules to be loaded immediately, |
118
|
|
|
|
|
|
|
in alphabetical order. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
This may be useful to avoid deferred errors and/or side effects |
121
|
|
|
|
|
|
|
of module loading. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
No extra options to unimport are supported. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub unimport { |
128
|
1
|
|
|
1
|
|
619
|
my $class = shift; |
129
|
|
|
|
|
|
|
|
130
|
1
|
50
|
|
|
|
6
|
croak "usage: no Module::Lazy;" |
131
|
|
|
|
|
|
|
if @_; |
132
|
|
|
|
|
|
|
|
133
|
1
|
|
|
|
|
2
|
$dont++; |
134
|
|
|
|
|
|
|
# sort keys to ensure load order stability in case of bugs |
135
|
1
|
|
|
|
|
5
|
foreach (sort keys %seen) { |
136
|
1
|
|
|
|
|
3
|
_inflate($_); |
137
|
|
|
|
|
|
|
}; |
138
|
|
|
|
|
|
|
}; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
my %known_method; |
141
|
|
|
|
|
|
|
sub _inflate { |
142
|
11
|
|
|
11
|
|
23
|
my $target = shift; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# TODO distinguish between "not seen" and "already loaded" |
145
|
11
|
|
|
|
|
34
|
my $mod = delete $seen{$target}; |
146
|
11
|
50
|
|
|
|
59
|
croak "Module '$target' was never loaded via Module::Lazy, that's possibly a bug" |
147
|
|
|
|
|
|
|
unless $mod; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
croak "Module '$target' already loaded from '$INC{$mod}'" |
150
|
11
|
50
|
33
|
|
|
86
|
unless $INC{$mod} and $INC{$mod} eq $inc_stub; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# reset stub methods prior to loading |
153
|
11
|
50
|
|
|
|
22
|
foreach (keys %{ $known_method{$target} || {} }) { |
|
11
|
|
|
|
|
65
|
|
154
|
44
|
|
|
|
|
90
|
_set_function( $target, $_ => undef ); |
155
|
|
|
|
|
|
|
}; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# make the module loadable again |
158
|
11
|
|
|
|
|
33
|
delete $INC{$mod}; |
159
|
11
|
|
|
|
|
34
|
_load( $target, $mod ); |
160
|
|
|
|
|
|
|
}; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub _load { |
163
|
11
|
|
|
11
|
|
27
|
my ($target, $mod) = @_; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
package |
166
|
|
|
|
|
|
|
Module::Lazy::_::quarantine; |
167
|
|
|
|
|
|
|
|
168
|
11
|
|
|
|
|
32
|
local $Carp::Internal{ __PACKAGE__ } = 1; |
169
|
11
|
|
|
|
|
4136
|
require $mod; |
170
|
|
|
|
|
|
|
# TODO maybe $target->import() |
171
|
|
|
|
|
|
|
}; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub _jump { |
174
|
37
|
|
|
37
|
|
83
|
my ($target, $todo, $nodie) = @_; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
return sub { |
177
|
10
|
|
|
10
|
|
1097
|
_inflate( $target ); |
178
|
|
|
|
|
|
|
|
179
|
10
|
|
|
|
|
1340
|
my $jump = $target->can($todo); |
180
|
10
|
100
|
|
|
|
77
|
goto $jump |
181
|
|
|
|
|
|
|
if $jump; # TODO should also check it's a CODEREF |
182
|
|
|
|
|
|
|
|
183
|
1
|
50
|
|
|
|
7
|
croak qq{Can't locate object method "$todo" via package "$target"} |
184
|
|
|
|
|
|
|
unless $nodie; |
185
|
37
|
|
|
|
|
160
|
}; |
186
|
|
|
|
|
|
|
}; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub _set_function { |
189
|
88
|
|
|
88
|
|
170
|
my ($target, $name, $code) = @_; |
190
|
|
|
|
|
|
|
|
191
|
88
|
100
|
|
|
|
200
|
if (ref $code) { |
192
|
44
|
|
|
|
|
92
|
$known_method{$target}{$name}++; |
193
|
11
|
|
|
11
|
|
89
|
no strict 'refs'; ## no critic |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
561
|
|
194
|
44
|
|
|
|
|
60
|
*{ $target."::".$name } = $code; |
|
44
|
|
|
|
|
6156
|
|
195
|
|
|
|
|
|
|
} else { |
196
|
11
|
|
|
11
|
|
69
|
no strict 'refs'; ## no critic |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
1070
|
|
197
|
44
|
|
|
|
|
58
|
delete ${ $target."::" }{ $name }; |
|
44
|
|
|
|
|
210
|
|
198
|
|
|
|
|
|
|
}; |
199
|
|
|
|
|
|
|
}; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head1 AUTHOR |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Konstantin S. Uvarin, C<< >> |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 BUGS |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=over |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item * C |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=item * C is not called on the modules being loaded. |
212
|
|
|
|
|
|
|
The decision is yet to be made whether it's good or bad. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * no way to preload prototyped exported functions |
215
|
|
|
|
|
|
|
(that's what L does), |
216
|
|
|
|
|
|
|
but maybe there should be? |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=item * certainly not enough interoperability tests. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=back |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Please report bugs via github or RT: |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=over |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item * L |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item * C |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=item * L |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=back |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 SUPPORT |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
You can find documentation for this module with the C command. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
perldoc Module::Lazy |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
You can also look for information at: |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=over 4 |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=item * github: L |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
L |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
L |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item * CPAN Ratings |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
L |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item * Search CPAN |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
L |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=back |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head1 SEE ALSO |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
L is another module with similar idea, however, |
267
|
|
|
|
|
|
|
it does it for imported functions rather than methods. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Copyright 2019 Konstantin S. Uvarin. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
276
|
|
|
|
|
|
|
under the terms of the the Artistic License (2.0). You may obtain a |
277
|
|
|
|
|
|
|
copy of the full license at: |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
L |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Any use, modification, and distribution of the Standard or Modified |
282
|
|
|
|
|
|
|
Versions is governed by this Artistic License. By using, modifying or |
283
|
|
|
|
|
|
|
distributing the Package, you accept this license. Do not use, modify, |
284
|
|
|
|
|
|
|
or distribute the Package, if you do not accept this license. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
If your Modified Version has been derived from a Modified Version made |
287
|
|
|
|
|
|
|
by someone other than you, you are nevertheless required to ensure that |
288
|
|
|
|
|
|
|
your Modified Version complies with the requirements of this license. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
This license does not grant you the right to use any trademark, service |
291
|
|
|
|
|
|
|
mark, tradename, or logo of the Copyright Holder. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
This license includes the non-exclusive, worldwide, free-of-charge |
294
|
|
|
|
|
|
|
patent license to make, have made, use, offer to sell, sell, import and |
295
|
|
|
|
|
|
|
otherwise transfer the Package with respect to any patent claims |
296
|
|
|
|
|
|
|
licensable by the Copyright Holder that are necessarily infringed by the |
297
|
|
|
|
|
|
|
Package. If you institute patent litigation (including a cross-claim or |
298
|
|
|
|
|
|
|
counterclaim) against any party alleging that the Package constitutes |
299
|
|
|
|
|
|
|
direct or contributory patent infringement, then this Artistic License |
300
|
|
|
|
|
|
|
to you shall terminate on the date that such litigation is filed. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER |
303
|
|
|
|
|
|
|
AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. |
304
|
|
|
|
|
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
305
|
|
|
|
|
|
|
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY |
306
|
|
|
|
|
|
|
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR |
307
|
|
|
|
|
|
|
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR |
308
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, |
309
|
|
|
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
1; # End of Module::Lazy |