File Coverage

blib/lib/Class/Load.pm
Criterion Covered Total %
statement 95 95 100.0
branch 32 34 94.1
condition 15 21 71.4
subroutine 20 20 100.0
pod 4 4 100.0
total 166 174 95.4


line stmt bran cond sub pod time code
1 13     13   5185 use strict;
  13         18  
  13         383  
2 13     13   47 use warnings;
  13         15  
  13         483  
3             package Class::Load; # git description: v0.22-9-g29ebb54
4             # ABSTRACT: A working (require "Class::Name") and more
5             # KEYWORDS: class module load require use runtime
6              
7             our $VERSION = '0.23';
8              
9 13     13   48 use base 'Exporter';
  13         90  
  13         1127  
10 13     13   5422 use Data::OptList ();
  13         87109  
  13         284  
11 13     13   77 use Module::Implementation 0.04;
  13         204  
  13         250  
12 13     13   49 use Module::Runtime 0.012 ();
  13         147  
  13         182  
13 13     13   51 use Try::Tiny;
  13         16  
  13         12295  
14              
15             {
16             my $loader = Module::Implementation::build_loader_sub(
17             implementations => [ 'XS', 'PP' ],
18             symbols => ['is_class_loaded'],
19             );
20              
21             $loader->();
22             }
23              
24             our @EXPORT_OK = qw/load_class load_optional_class try_load_class is_class_loaded load_first_existing_class/;
25             our %EXPORT_TAGS = (
26             all => \@EXPORT_OK,
27             );
28              
29             our $ERROR;
30              
31             sub load_class {
32 21     21 1 6721 my $class = shift;
33 21         26 my $options = shift;
34              
35 21         42 my ($res, $e) = try_load_class($class, $options);
36 15 100       160 return $class if $res;
37              
38 8         14 _croak($e);
39             }
40              
41             sub load_first_existing_class {
42 14 50   14 1 6452 my $classes = Data::OptList::mkopt(\@_)
43             or return;
44              
45 14         428 foreach my $class (@{$classes}) {
  14         25  
46 29         198 Module::Runtime::check_module_name($class->[0]);
47             }
48              
49 13         104 for my $class (@{$classes}) {
  13         23  
50 25         56 my ($name, $options) = @{$class};
  25         36  
51              
52             # We need to be careful not to pass an undef $options to this sub,
53             # since the XS version will blow up if that happens.
54 25 100       76 return $name if is_class_loaded($name, ($options ? $options : ()));
    100          
55              
56 22         51 my ($res, $e) = try_load_class($name, $options);
57              
58 22 100       117 return $name if $res;
59              
60 21         40 my $file = Module::Runtime::module_notional_filename($name);
61              
62 21 100       596 next if $e =~ /^Can't locate \Q$file\E in \@INC/;
63             next
64 11 100 66     71 if $options
      100        
65             && defined $options->{-version}
66             && $e =~ _version_fail_re($name, $options->{-version});
67              
68 2         8 _croak("Couldn't load class ($name) because: $e");
69             }
70              
71 13 100 66     65 my @list = map {
72 7         11 $_->[0]
73             . ( $_->[1] && defined $_->[1]{-version}
74             ? " (version >= $_->[1]{-version})"
75             : q{} )
76 7         9 } @{$classes};
77              
78 7         18 my $err
79             .= q{Can't locate }
80             . _or_list(@list)
81             . " in \@INC (\@INC contains: @INC).";
82 7         15 _croak($err);
83             }
84              
85             sub _version_fail_re {
86 11     11   13 my $name = shift;
87 11         11 my $vers = shift;
88              
89 11         180 return qr/\Q$name\E version \Q$vers\E required--this is only version/;
90             }
91              
92             sub _nonexistent_fail_re {
93 8     8   11 my $name = shift;
94              
95 8         19 my $file = Module::Runtime::module_notional_filename($name);
96 8         248 return qr/Can't locate \Q$file\E in \@INC/;
97             }
98              
99             sub _or_list {
100 7 100   7   25 return $_[0] if @_ == 1;
101              
102 4 100       22 return join ' or ', @_ if @_ ==2;
103              
104 2         3 my $last = pop;
105              
106 2         5 my $list = join ', ', @_;
107 2         4 $list .= ', or ' . $last;
108              
109 2         9 return $list;
110             }
111              
112             sub load_optional_class {
113 15     15 1 4736 my $class = shift;
114 15         16 my $options = shift;
115              
116 15         35 Module::Runtime::check_module_name($class);
117              
118 15         196 my ($res, $e) = try_load_class($class, $options);
119 15 100       113 return 1 if $res;
120              
121 9 50 66     34 return 0
      66        
122             if $options
123             && defined $options->{-version}
124             && $e =~ _version_fail_re($class, $options->{-version});
125              
126 8 100       13 return 0
127             if $e =~ _nonexistent_fail_re($class);
128              
129 6         17 _croak($e);
130             }
131              
132             sub try_load_class {
133 78     78 1 4914 my $class = shift;
134 78         79 my $options = shift;
135              
136 78         158 Module::Runtime::check_module_name($class);
137              
138 72         784 local $@;
139 72         85 undef $ERROR;
140              
141 72 100       242 if (is_class_loaded($class)) {
142             # We need to check this here rather than in is_class_loaded() because
143             # we want to return the error message for a failed version check, but
144             # is_class_loaded just returns true/false.
145 24 100 66     106 return 1 unless $options && defined $options->{-version};
146             return try {
147 13     13   397 $class->VERSION($options->{-version});
148 3         9 1;
149             }
150             catch {
151 10     10   81 _error($_);
152 13         73 };
153             }
154              
155 48         101 my $file = Module::Runtime::module_notional_filename($class);
156             # This says "our diagnostics of the package
157             # say perl's INC status about the file being loaded are
158             # wrong", so we delete it from %INC, so when we call require(),
159             # perl will *actually* try reloading the file.
160             #
161             # If the file is already in %INC, it won't retry,
162             # And on 5.8, it won't fail either!
163             #
164             # The extra benefit of this trick, is it helps even on
165             # 5.10, as instead of dying with "Compilation failed",
166             # it will die with the actual error, and that's a win-win.
167 48         645 delete $INC{$file};
168             return try {
169 48     48   1176 local $SIG{__DIE__} = 'DEFAULT';
170 48 100 66     154 if ($options && defined $options->{-version}) {
171 7         21 Module::Runtime::use_module($class, $options->{-version});
172             }
173             else {
174 41         87 Module::Runtime::require_module($class);
175             }
176 10         4095 1;
177             }
178             catch {
179 38     38   9689 _error($_);
180 48         331 };
181             }
182              
183             sub _error {
184 48     48   65 my $e = shift;
185              
186 48         293 $e =~ s/ at .+?Runtime\.pm line [0-9]+\.$//;
187 48         74 chomp $e;
188              
189 48         61 $ERROR = $e;
190 48 100       132 return 0 unless wantarray;
191 41         160 return 0, $ERROR;
192             }
193              
194             sub _croak {
195 23     23   109 require Carp;
196 23         44 local $Carp::CarpLevel = $Carp::CarpLevel + 2;
197 23         3588 Carp::croak(shift);
198             }
199              
200             1;
201              
202             __END__