File Coverage

blib/lib/Getopt/Module.pm
Criterion Covered Total %
statement 68 72 94.4
branch 25 34 73.5
condition 5 8 62.5
subroutine 11 11 100.0
pod 1 1 100.0
total 110 126 87.3


line stmt bran cond sub pod time code
1             package Getopt::Module;
2              
3 1     1   768 use strict;
  1         2  
  1         30  
4 1     1   5 use warnings;
  1         2  
  1         28  
5              
6 1     1   5 use vars qw($VERSION @EXPORT_OK);
  1         1  
  1         54  
7              
8 1     1   4 use Carp qw(confess);
  1         12  
  1         61  
9 1     1   5 use Exporter qw(import);
  1         2  
  1         43  
10 1     1   6 use Scalar::Util;
  1         2  
  1         39  
11              
12             # XXX this declaration must be on a single line
13             # https://metacpan.org/pod/version#How-to-declare()-a-dotted-decimal-version
14 1     1   546 use version; our $VERSION = version->declare('v1.0.0');
  1         1968  
  1         6  
15              
16             @EXPORT_OK = qw(GetModule);
17              
18             my $MODULE_RE = qr{
19             ^ # match the beginning of the string
20             (-)? # optional: leading hyphen: use 'no' instead of 'use'
21             (\w+(?:::\w+)*) # required: Module::Name
22             (?:(=|\s+) (.+))? # optional: args prefixed by '=' e.g. 'Module=arg1,arg2' or \s+ e.g. 'Module qw(foo bar)'
23             $ # match the end of the string
24             }x;
25              
26             # return true if $ref ISA $class - works with non-references, unblessed references and objects
27             sub _isa($$) {
28 230     230   382 my ($ref, $class) = @_;
29 230 50       834 return Scalar::Util::blessed(ref) ? $ref->isa($class) : ref($ref) eq $class;
30             }
31              
32             # dump value like Data::Dump/Data::Dumper::Concise
33             sub _pp($) {
34 1096     1096   216488 my $value = shift;
35 1096         4853 require Data::Dumper;
36 1096         8650 local $Data::Dumper::Deepcopy = 1;
37 1096         1436 local $Data::Dumper::Indent = 0;
38 1096         1376 local $Data::Dumper::Purity = 0;
39 1096         1440 local $Data::Dumper::Terse = 1;
40 1096         1349 local $Data::Dumper::Useqq = 1;
41 1096         2141 return Data::Dumper::Dumper($value);
42             }
43              
44             sub GetModule($@) {
45 72     72 1 3109 my $target = shift;
46 72         101 my $params;
47              
48 72 50       188 if (@_ == 1) {
    0          
49 72         101 $params = shift;
50              
51 72 50       136 unless (_isa($params, 'HASH')) {
52 0         0 confess 'invalid parameter; expected HASH or HASHREF, got ', _pp(ref($params));
53             }
54             } elsif ((@_ % 2) == 0) {
55 0         0 $params = { @_ };
56             } else {
57 0         0 confess 'invalid parameters; expected hash or hashref, got odd number of arguments > 1';
58             }
59              
60 72         150 my $no_import = $params->{no_import};
61 72 100       160 my $separator = defined($params->{separator}) ? $params->{separator} : ' ';
62              
63             return sub {
64 72     72   121 my $name = shift;
65 72         101 my $value = shift;
66              
67 72 50 33     319 confess 'invalid option definition: option must target a scalar ("foo=s") or array ("foo=@")'
68             unless (defined($value) && (@_ == 0));
69 72 50       668 confess sprintf('invalid value for %s option: %s', $name, _pp($value))
70             unless ($value =~ $MODULE_RE);
71              
72 72         388 my ($hyphen, $module, $args_start, $args) = ($1, $2, $3, $4);
73 72         122 my ($statement, $method, $eval);
74              
75 72 100       131 if ($hyphen) {
76 31         51 $statement = 'no';
77 31         57 $method = 'unimport';
78             } else {
79 41         56 $statement = 'use';
80 41         52 $method = 'import';
81             }
82              
83 72 100       140 if ($args_start) { # this takes precedence over no_import - see perlrun
    100          
84 31 50       61 $args = '' unless (defined $args);
85              
86 31 100       64 if ($args_start eq '=') {
87 23         68 $eval = "$statement $module split(/,/,q\0$args\0);"; # see perl.c
88             } else { # space: arbitrary expression
89 8         27 $eval = "$statement $module $args;";
90             }
91             } elsif ($no_import) {
92 11         31 $eval = "$statement $module ();";
93             } else {
94 30         74 $eval = "$statement $module;";
95             }
96              
97 72         309 my $parsed = {
98             args => $args,
99             eval => $eval,
100             method => $method,
101             module => $module,
102             name => $name,
103             statement => $statement,
104             value => $value,
105             };
106              
107 72 100       147 if (_isa($target, 'ARRAY')) {
    100          
    100          
    50          
108 24         53 push @$target, $eval;
109             } elsif (_isa($target, 'SCALAR')) { # SCALAR ref
110 20 100 66     61 if (defined($$target) && length($$target)) {
111 4         10 $$target .= "$separator$eval";
112             } else {
113 16         28 $$target = $eval;
114             }
115             } elsif (_isa($target, 'HASH')) {
116 18   100     79 $target->{$module} ||= [];
117 18         29 push @{ $target->{$module} }, $eval;
  18         42  
118             } elsif (_isa($target, 'CODE')) {
119 10         23 $target->($name, $eval, $parsed);
120             } else {
121 0         0 confess 'invalid target type - expected array ref, code ref, hash ref or scalar ref, got: ', ref($target);
122             }
123              
124 72         3201 return $parsed; # ignored by Getopt::Long, but useful for testing
125 72         393 };
126             }
127              
128             1;