File Coverage

lib/UR/ModuleLoader.pm
Criterion Covered Total %
statement 54 62 87.1
branch 35 48 72.9
condition 19 30 63.3
subroutine 7 7 100.0
pod 0 3 0.0
total 115 150 76.6


line stmt bran cond sub pod time code
1              
2             package UR::ModuleLoader;
3              
4 266     266   2739 use strict;
  266         372  
  266         7975  
5 266     266   928 use warnings;
  266         338  
  266         180529  
6             require UR;
7             our $VERSION = "0.46"; # UR $VERSION;
8              
9             Class::Autouse->autouse(\&dynamically_load_class);
10             Class::Autouse->autouse(\&dynamically_load_role);
11             Class::Autouse->sugar(\&define_class);
12              
13             our @CARP_NOT = qw(Class::Autouse UR::Namespace);
14              
15             sub define_class {
16 2105     2105 0 11424 my ($class,$func,@params) = @_;
17 2105 50       5570 return unless $UR::initialized;
18 2105 50       5173 return unless $Class::Autouse::ORIGINAL_CAN->("UR::Object::Type","get");
19              
20             # Handle the special case of defining a new class
21             # This lets us have the effect of a UNIVERSAL::class method, w/o mucking with UNIVERSAL
22 2105 100 66     31314 if (defined($func) and $func eq "class" and @params > 1 and $class ne "UR::Object::Type") {
      100        
      66        
23 1729         2152 my @class_params;
24 1729 50 33     9364 if (@params == 2 and ref($params[1]) eq 'HASH') {
    0 0        
25 1729         2128 @class_params = %{ $params[1] };
  1729         6507  
26             }
27             elsif (@params == 2 and ref($params[1]) eq 'ARRAY') {
28 0         0 @class_params = @{ $params[1] };
  0         0  
29             }
30             else {
31 0         0 @class_params = @params[1..$#params];
32             }
33 1729         10157 my $class_meta = UR::Object::Type->define(class_name => $class, @class_params);
34 1703 50       6068 unless ($class_meta) {
35 0         0 Carp::croak "error defining class $class!";
36             }
37 1703     1703   15807 return sub { $class };
  1703         15121  
38             }
39              
40             else {
41 376         608 return;
42             }
43             }
44              
45             sub _should_dynamically_load_package {
46 17263     17263   17549 my $package = shift;
47             # Don't even try to load unless we're done boostrapping somewhat.
48 17263 50       30622 return unless $UR::initialized;
49 17263 50       33864 return unless $Class::Autouse::ORIGINAL_CAN->("UR::Object::Type","get");
50              
51             # Some modules (Class::DBI, recently) call UNIVERSAL::can directly with things which don't even resemble
52             # class names. Skip doing any work on anything which isn't at least a two-part class name.
53             # We refuse explicitly to handle top-level namespaces below anyway, and this will keep us from
54             # slowing down other modules just to fail late.
55              
56 17263         136096 my ($namespace) = ($package =~ /^(.*?)::/);
57 17263 100       35610 return unless $namespace;
58              
59 14247 100       44291 unless ($namespace->isa("UR::Namespace")) {
60 10034         102299 return;
61             }
62              
63 4213 50       18533 unless ($namespace->should_dynamically_load_class($package)) {
64 0         0 return;
65             }
66              
67 4213         12342 return $namespace;
68             }
69              
70             our %loading;
71              
72             sub dynamically_load_class {
73 10017     10017 0 82167768 my ($class,$func,@params) = @_;
74              
75 10017 100       48194 return unless my $namespace = _should_dynamically_load_package($class);
76              
77 3492 100 100     17024 if (defined($func) and $func eq "class" and @params > 1 and $class ne "UR::Object::Type") {
      100        
      66        
78             # a "class" statement caught by the above define_class call
79 58         156 return;
80             }
81              
82 3434 100       9665 return if $loading{$class};
83 3433         12928 local %loading = ( %loading, $class => 1 );
84              
85             # Attempt to get a class object, loading it as necessary (probably).
86             # TODO: this is a non-standard accessor
87 3433         17084 my $meta = $namespace->get_member_class($class);
88 3433 100       10333 unless ($meta) {
89 662         2092 return;
90             }
91              
92             # Handle the case in which the class is not "generated".
93             # These are generated by default when used, so this is a corner case.
94 2771 50       8616 unless ($meta->generated())
95             {
96             # we have a new class
97             # attempt to auto-generate it
98 0 0       0 unless ($meta->generate)
99             {
100 0         0 Carp::confess("failed to auto-generate $class");
101             }
102             }
103              
104             # Return a descriptive error message for the caller.
105 2771         4663 my $fref;
106 2771 100       7851 if (defined $func) {
107 940         4426 $fref = $class->can($func);
108 940 100       9592 unless ($fref) {
109 1         16 Carp::confess("$class was auto-generated successfully but cannot find method $func");
110             }
111 939         4676 return $fref;
112             }
113              
114 1831         7630 return 1;
115             };
116              
117             sub dynamically_load_role {
118 7246     7246 0 45848 my($role_name, $func, @params) = @_;
119              
120 7246 100       11062 return unless _should_dynamically_load_package($role_name);
121              
122 721 100       2320 return if $loading{$role_name};
123 717         2492 local %loading = ( %loading, $role_name => 1 );
124              
125             # The module may have actually been loaded by dynamically_load_class(),
126             # but failed the check for class-ness
127 717 100 66     3443 if (UR::Role::Prototype->is_loaded($role_name)
128             &&
129             $role_name->can($func)
130             ) {
131 1         17 return 1;
132             }
133              
134 716 100       2515 if (UR::Util::use_package_optimistically($role_name)) {
135 7 50 33     27 if (UR::Role::Prototype->is_loaded($role_name)
136             &&
137             $role_name->can($func)
138             ) {
139 0         0 return 1;
140             }
141             }
142              
143 716         2177 return;
144             }
145              
146             1;
147              
148              
149             =pod
150              
151             =head1 NAME
152              
153             UR::ModuleLoader - UR hooks into Class::Autouse
154              
155             =head1 DESCRIPTION
156              
157             UR uses Class::Autouse to handle automagic loading for modules. As long
158             as some part of an application "use"s a Namespace module, the autoloader
159             will handle loading modules under that namespace when they are needed.
160              
161             =head1 SEE ALSO
162              
163             UR, UR::Namespace
164              
165             =cut