File Coverage

blib/lib/Module/Load/Util.pm
Criterion Covered Total %
statement 90 97 92.7
branch 59 78 75.6
condition 4 8 50.0
subroutine 9 9 100.0
pod 4 4 100.0
total 166 196 84.6


line stmt bran cond sub pod time code
1             package Module::Load::Util;
2              
3 2     2   418875 use strict 'subs', 'vars';
  2         5  
  2         122  
4 2     2   1207 use Regexp::Pattern::Perl::Module ();
  2         783  
  2         165  
5              
6             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
7             our $DATE = '2024-05-13'; # DATE
8             our $DIST = 'Module-Load-Util'; # DIST
9             our $VERSION = '0.012'; # VERSION
10              
11 2     2   15 use Exporter 'import';
  2         36  
  2         3674  
12             our @EXPORT_OK = qw(
13             load_module_with_optional_args
14             instantiate_class_with_optional_args
15             call_module_function_with_optional_args
16             call_module_method_with_optional_args
17             );
18              
19             sub _normalize_module_with_optional_args {
20 22     22   45 my $module_with_optional_args = shift;
21              
22 22         43 my ($module, $args);
23 22 100       254 if (ref $module_with_optional_args eq 'ARRAY') {
    50          
    100          
24 7 100 66     60 die "array form or module/class name must have 1 or 2 elements"
25             unless @$module_with_optional_args == 1 || @$module_with_optional_args == 2;
26 6         17 $module = $module_with_optional_args->[0];
27 6   50     16 $args = $module_with_optional_args->[1] || [];
28 6 100       25 $args = [%$args] if ref $args eq 'HASH';
29 6 100       30 die "In array form of module/class name, the 2nd element must be ".
30             "arrayref or hashref" unless ref $args eq 'ARRAY';
31             } elsif (ref $module_with_optional_args) {
32 0         0 die "module/class name must be string or 2-element array, not ".
33             $module_with_optional_args;
34             } elsif ($module_with_optional_args =~ /(.+?)[=,](.*)/) {
35 10         33 $module = $1;
36 10         50 $args = [split /,/, $2];
37             } else {
38 5         10 $module = $module_with_optional_args;
39 5         12 $args = [];
40             }
41 20         91 ($module, $args);
42             }
43              
44             sub _load_module {
45 20 50   20   55 my $opts = ref $_[0] eq 'HASH' ? shift : {};
46 20         31 my $module = shift;
47              
48 20 100       65 my $do_load = defined $opts->{load} ? $opts->{load} : 1;
49              
50 20 100       56 unless ($do_load) {
51 2 50 33     14 if ($opts->{ns_prefix}) {
    50          
52 0 0       0 $module = $opts->{ns_prefix} . ($opts->{ns_prefix} =~ /::\z/ ? '' : '::') . $module;
53 0         0 } elsif ($opts->{ns_prefixes} && @{ $opts->{ns_prefixes} }) {
54 0 0       0 $module = $opts->{ns_prefixes}[0] . ($opts->{ns_prefixes}[0] =~ /::\z/ ? '' : '::') . $module;
55             }
56 2         4 return $module;
57             }
58              
59 1         4 my @ns_prefixes = $opts->{ns_prefixes} ? @{$opts->{ns_prefixes}} :
60 18 100       131 defined($opts->{ns_prefix}) ? ($opts->{ns_prefix}) : ('');
    100          
61 18 100       41 my $try_all = $opts->{ns_prefixes} ? 1:0;
62 18         25 my $module_with_prefix;
63 18         62 for my $i (0 .. $#ns_prefixes) {
64 19         33 my $ns_prefix = $ns_prefixes[$i];
65 19 100       43 if (length $ns_prefix) {
66 3 50       15 $module_with_prefix =
67             $ns_prefix . ($ns_prefix =~ /::\z/ ? '':'::') . $module;
68             } else {
69 16         42 $module_with_prefix = $module;
70             }
71              
72 19         112 (my $module_with_prefix_pm = "$module_with_prefix.pm") =~ s!::!/!g;
73 19 100       51 if ($try_all) {
74 2 100       4 eval { require $module_with_prefix_pm }; last unless $@;
  2         887  
  2         412  
75 1 50       10 warn $@ if $@ !~ /\ACan't locate/;
76             } else {
77 17         3684 require $module_with_prefix_pm;
78             }
79             }
80 14 50       744 if ($@) {
81 0         0 die "load_module_with_optional_args(): Failed to load module '$module' (all prefixes tried: ".join(", ", @ns_prefixes).")";
82             }
83 14         49 $module_with_prefix;
84             }
85              
86             sub load_module_with_optional_args {
87 20 100   20 1 533852 my $opts = ref($_[0]) eq 'HASH' ? shift : {};
88 20         43 my $module_with_optional_args = shift;
89              
90             my $target_package =
91             defined $opts->{target_package} ? $opts->{target_package} :
92             defined $opts->{caller} ? $opts->{caller} :
93 20 50       135 caller(0);
    100          
94             # check because we will use eval ""
95             $target_package =~ $Regexp::Pattern::Perl::Module::RE{perl_modname}{pat}
96 20 50       691 or die "Invalid syntax in target package '$target_package'";
97              
98 20         61 my ($module, $args) = _normalize_module_with_optional_args(
99             $module_with_optional_args);
100 18         58 $module = _load_module($opts, $module);
101              
102 14 100       44 my $do_import = defined $opts->{import} ? $opts->{import} : 1;
103 14 100       40 if ($do_import) {
104 9         1165 eval "package $target_package; $module->import(\@{\$args});"; ## no critic: BuiltinFunctions::ProhibitStringyEval
105 9 100       720 die if $@;
106             }
107              
108 13         69 {module=>$module, args=>$args};
109             }
110              
111             sub instantiate_class_with_optional_args {
112 4 100   4 1 19831 my $opts = ref($_[0]) eq 'HASH' ? {%{shift()}} : {}; # shallow copy
  3         6  
113 4         8 my $class_with_optional_args = shift;
114              
115 4         7 $opts->{import} = 0;
116 4         22 $opts->{target_package} = caller(0);
117 4         113 my $res = load_module_with_optional_args($opts, $class_with_optional_args);
118             #use DD; print "Options: "; dd $opts; print "Result: "; dd $res;
119 4         7 my $class = $res->{module};
120 4         6 my $args = $res->{args};
121              
122 4 100       8 my $do_construct = defined $opts->{construct} ? $opts->{construct} : 1;
123 4 100       7 if ($do_construct) {
124             my $constructor = defined $opts->{constructor} ?
125 3 100       7 $opts->{constructor} : 'new';
126 3         25 my $obj = $class->$constructor(@$args);
127 3         25 return $obj;
128             } else {
129 1         7 return +{class=>$class, args=>$args};
130             }
131             }
132              
133             sub call_module_function_with_optional_args {
134 1 50   1 1 5339 my $opts = ref($_[0]) eq 'HASH' ? shift : {};
135 1         3 my $module_with_optional_args = shift;
136              
137 1         4 my ($module, $args) = _normalize_module_with_optional_args(
138             $module_with_optional_args);
139 1         3 my $function;
140 1 50       8 if (defined $opts->{function}) {
141 0         0 $function = $opts->{function};
142             } else {
143 1 50       12 $module =~ s/\A(.+)::(\w+)\z/$1/ or die "Please specify MODULE::FUNCTION, not just module name '$module'";
144 1         4 $function = $2;
145             }
146 1         3 $module = _load_module($opts, $module);
147              
148 1         4 &{"$module\::$function"}(@$args);
  1         15  
149             }
150              
151             sub call_module_method_with_optional_args {
152 1 50   1 1 2521 my $opts = ref($_[0]) eq 'HASH' ? shift : {};
153 1         3 my $module_with_optional_args = shift;
154              
155 1         3 my ($module, $args) = _normalize_module_with_optional_args(
156             $module_with_optional_args);
157 1         2 my $method;
158 1 50       3 if (defined $opts->{method}) {
159 0         0 $method = $opts->{method};
160             } else {
161 1 50       9 $module =~ s/\A(.+)::(\w+)\z/$1/ or die "Please specify MODULE::FUNCTION, not just module name '$module'";
162 1         3 $method = $2;
163             }
164              
165 1         2 $module = _load_module($opts, $module);
166              
167 1         8 $module->$method(@$args);
168             }
169              
170             1;
171             # ABSTRACT: Some utility routines related to module loading
172              
173             __END__