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   6249 use strict;
  13         22  
  13         301  
2 13     13   51 use warnings;
  13         15  
  13         483  
3             package Class::Load; # git description: v0.24-5-g22a44fd
4             # ABSTRACT: A working (require "Class::Name") and more
5             # KEYWORDS: class module load require use runtime
6              
7             our $VERSION = '0.25';
8              
9 13     13   56 use base 'Exporter';
  13         16  
  13         1616  
10 13     13   5000 use Data::OptList 0.110 ();
  13         86672  
  13         294  
11 13     13   72 use Module::Implementation 0.04;
  13         131  
  13         243  
12 13     13   50 use Module::Runtime 0.012 ();
  13         133  
  13         182  
13 13     13   46 use Try::Tiny;
  13         17  
  13         12800  
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 6900 my $class = shift;
33 21         33 my $options = shift;
34              
35 21         36 my ($res, $e) = try_load_class($class, $options);
36 15 100       155 return $class if $res;
37              
38 8         17 _croak($e);
39             }
40              
41             sub load_first_existing_class {
42 14 50   14 1 7897 my $classes = Data::OptList::mkopt(\@_)
43             or return;
44              
45 14         481 foreach my $class (@{$classes}) {
  14         27  
46 29         190 Module::Runtime::check_module_name($class->[0]);
47             }
48              
49 13         133 for my $class (@{$classes}) {
  13         22  
50 25         34 my ($name, $options) = @{$class};
  25         47  
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       75 return $name if is_class_loaded($name, ($options ? $options : ()));
    100          
55              
56 22         63 my ($res, $e) = try_load_class($name, $options);
57              
58 22 100       123 return $name if $res;
59              
60 21         45 my $file = Module::Runtime::module_notional_filename($name);
61              
62 21 100       620 next if $e =~ /^Can't locate \Q$file\E in \@INC/;
63             next
64             if $options
65             && defined $options->{-version}
66 11 100 66     53 && $e =~ _version_fail_re($name, $options->{-version});
      100        
67              
68 2         8 _croak("Couldn't load class ($name) because: $e");
69             }
70              
71             my @list = map {
72             $_->[0]
73             . ( $_->[1] && defined $_->[1]{-version}
74 13 100 66     58 ? " (version >= $_->[1]{-version})"
75             : q{} )
76 7         14 } @{$classes};
  7         15  
77              
78 7         18 my $err
79             .= q{Can't locate }
80             . _or_list(@list)
81             . " in \@INC (\@INC contains: @INC).";
82 7         14 _croak($err);
83             }
84              
85             sub _version_fail_re {
86 11     11   17 my $name = shift;
87 11         16 my $vers = shift;
88              
89 11         174 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         18 my $file = Module::Runtime::module_notional_filename($name);
96 8         256 return qr/Can't locate \Q$file\E in \@INC/;
97             }
98              
99             sub _or_list {
100 7 100   7   30 return $_[0] if @_ == 1;
101              
102 4 100       16 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         8 return $list;
110             }
111              
112             sub load_optional_class {
113 15     15 1 7370 my $class = shift;
114 15         18 my $options = shift;
115              
116 15         43 Module::Runtime::check_module_name($class);
117              
118 15         198 my ($res, $e) = try_load_class($class, $options);
119 15 100       119 return 1 if $res;
120              
121             return 0
122             if $options
123             && defined $options->{-version}
124 9 50 66     27 && $e =~ _version_fail_re($class, $options->{-version});
      66        
125              
126 8 100       17 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 5140 my $class = shift;
134 78         91 my $options = shift;
135              
136 78         182 Module::Runtime::check_module_name($class);
137              
138 72         800 local $@;
139 72         103 undef $ERROR;
140              
141 72 100       145 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     97 return 1 unless $options && defined $options->{-version};
146             return try {
147 13     13   522 $class->VERSION($options->{-version});
148 3         10 1;
149             }
150             catch {
151 10     10   117 _error($_);
152 13         66 };
153             }
154              
155 48         93 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         817 delete $INC{$file};
168             return try {
169 48     48   1781 local $SIG{__DIE__} = 'DEFAULT';
170 48 100 66     150 if ($options && defined $options->{-version}) {
171 7         20 Module::Runtime::use_module($class, $options->{-version});
172             }
173             else {
174 41         86 Module::Runtime::require_module($class);
175             }
176 10         3547 1;
177             }
178             catch {
179 38     38   9652 _error($_);
180 48         281 };
181             }
182              
183             sub _error {
184 48     48   84 my $e = shift;
185              
186 48         325 $e =~ s/ at .+?Runtime\.pm line [0-9]+\.$//;
187 48         103 chomp $e;
188              
189 48         72 $ERROR = $e;
190 48 100       134 return 0 unless wantarray;
191 41         243 return 0, $ERROR;
192             }
193              
194             sub _croak {
195 23     23   100 require Carp;
196 23         44 local $Carp::CarpLevel = $Carp::CarpLevel + 2;
197 23         3113 Carp::croak(shift);
198             }
199              
200             1;
201              
202             __END__