File: | local/lib/perl5/Sub/Install.pm |
Coverage: | 54.7% |
line | stmt | bran | cond | sub | time | code |
---|---|---|---|---|---|---|
1 | 6 6 6 | 12 5 93 | use strict; | |||
2 | 6 6 6 | 10 4 124 | use warnings; | |||
3 | package Sub::Install; | |||||
4 | { | |||||
5 | $Sub::Install::VERSION = '0.927'; | |||||
6 | } | |||||
7 | # ABSTRACT: install subroutines into packages easily | |||||
8 | ||||||
9 | 6 6 6 | 12 3 149 | use Carp; | |||
10 | 6 6 6 | 12 6 1180 | use Scalar::Util (); | |||
11 | ||||||
12 | ||||||
13 | sub _name_of_code { | |||||
14 | 0 | 0 | my ($code) = @_; | |||
15 | 0 | 0 | require B; | |||
16 | 0 | 0 | my $name = B::svref_2object($code)->GV->NAME; | |||
17 | 0 | 0 | return $name unless $name =~ /\A__ANON__/; | |||
18 | 0 | 0 | return; | |||
19 | } | |||||
20 | ||||||
21 | # See also Params::Util, to which this code was donated. | |||||
22 | sub _CODELIKE { | |||||
23 | 28 | 71 | (Scalar::Util::reftype($_[0])||'') eq 'CODE' | |||
24 | || Scalar::Util::blessed($_[0]) | |||||
25 | && (overload::Method($_[0],'&{}') ? $_[0] : undef); | |||||
26 | } | |||||
27 | ||||||
28 | # do the heavy lifting | |||||
29 | sub _build_public_installer { | |||||
30 | 12 | 10 | my ($installer) = @_; | |||
31 | ||||||
32 | sub { | |||||
33 | 28 | 10 | my ($arg) = @_; | |||
34 | 28 | 32 | my ($calling_pkg) = caller(0); | |||
35 | ||||||
36 | # I'd rather use ||= but I'm whoring for Devel::Cover. | |||||
37 | 28 56 | 29 82 | for (qw(into from)) { $arg->{$_} = $calling_pkg unless $arg->{$_} } | |||
38 | ||||||
39 | # This is the only absolutely required argument, in many cases. | |||||
40 | 28 | 44 | Carp::croak "named argument 'code' is not optional" unless $arg->{code}; | |||
41 | ||||||
42 | 28 | 25 | if (_CODELIKE($arg->{code})) { | |||
43 | 28 | 25 | $arg->{as} ||= _name_of_code($arg->{code}); | |||
44 | } else { | |||||
45 | 0 | 0 | Carp::croak | |||
46 | "couldn't find subroutine named $arg->{code} in package $arg->{from}" | |||||
47 | unless my $code = $arg->{from}->can($arg->{code}); | |||||
48 | ||||||
49 | 0 | 0 | $arg->{as} = $arg->{code} unless $arg->{as}; | |||
50 | 0 | 0 | $arg->{code} = $code; | |||
51 | } | |||||
52 | ||||||
53 | 28 | 29 | Carp::croak "couldn't determine name under which to install subroutine" | |||
54 | unless $arg->{as}; | |||||
55 | ||||||
56 | 28 | 29 | $installer->(@$arg{qw(into as code) }); | |||
57 | } | |||||
58 | 12 | 30 | } | |||
59 | ||||||
60 | # do the ugly work | |||||
61 | ||||||
62 | my $_misc_warn_re; | |||||
63 | my $_redef_warn_re; | |||||
64 | BEGIN { | |||||
65 | 6 | 12 | $_misc_warn_re = qr/ | |||
66 | Prototype\ mismatch:\ sub\ .+? | | |||||
67 | Constant subroutine \S+ redefined | |||||
68 | /x; | |||||
69 | 6 | 97 | $_redef_warn_re = qr/Subroutine\ \S+\ redefined/x; | |||
70 | } | |||||
71 | ||||||
72 | my $eow_re; | |||||
73 | 6 | 843 | BEGIN { $eow_re = qr/ at .+? line \d+\.\Z/ }; | |||
74 | ||||||
75 | sub _do_with_warn { | |||||
76 | 18 | 13 | my ($arg) = @_; | |||
77 | 18 | 20 | my $code = delete $arg->{code}; | |||
78 | my $wants_code = sub { | |||||
79 | 18 | 10 | my $code = shift; | |||
80 | sub { | |||||
81 | 28 0 | 39 0 | my $warn = $SIG{__WARN__} ? $SIG{__WARN__} : sub { warn @_ }; ## no critic | |||
82 | local $SIG{__WARN__} = sub { | |||||
83 | 0 | 0 | my ($error) = @_; | |||
84 | 0 0 | 0 0 | for (@{ $arg->{suppress} }) { | |||
85 | 0 | 0 | return if $error =~ $_; | |||
86 | } | |||||
87 | 0 0 | 0 0 | for (@{ $arg->{croak} }) { | |||
88 | 0 | 0 | if (my ($base_error) = $error =~ /\A($_) $eow_re/x) { | |||
89 | 0 | 0 | Carp::croak $base_error; | |||
90 | } | |||||
91 | } | |||||
92 | 0 0 | 0 0 | for (@{ $arg->{carp} }) { | |||
93 | 0 | 0 | if (my ($base_error) = $error =~ /\A($_) $eow_re/x) { | |||
94 | 0 | 0 | return $warn->(Carp::shortmess $base_error); | |||
95 | } | |||||
96 | } | |||||
97 | 0 | 0 | ($arg->{default} || $warn)->($error); | |||
98 | 28 | 65 | }; | |||
99 | 28 | 27 | $code->(@_); | |||
100 | 18 | 812 | }; | |||
101 | 18 | 25 | }; | |||
102 | 18 | 42 | return $wants_code->($code) if $code; | |||
103 | 12 | 12 | return $wants_code; | |||
104 | } | |||||
105 | ||||||
106 | sub _installer { | |||||
107 | sub { | |||||
108 | 28 | 20 | my ($pkg, $name, $code) = @_; | |||
109 | 6 6 6 | 18 4 436 | no strict 'refs'; ## no critic ProhibitNoStrict | |||
110 | 28 28 | 13 56 | *{"$pkg\::$name"} = $code; | |||
111 | 28 | 4395 | return $code; | |||
112 | } | |||||
113 | 18 | 34 | } | |||
114 | ||||||
115 | BEGIN { | |||||
116 | 6 | 15 | *_ignore_warnings = _do_with_warn({ | |||
117 | carp => [ $_misc_warn_re, $_redef_warn_re ] | |||||
118 | }); | |||||
119 | ||||||
120 | 6 | 11 | *install_sub = _build_public_installer(_ignore_warnings(_installer)); | |||
121 | ||||||
122 | 6 | 13 | *_carp_warnings = _do_with_warn({ | |||
123 | carp => [ $_misc_warn_re ], | |||||
124 | suppress => [ $_redef_warn_re ], | |||||
125 | }); | |||||
126 | ||||||
127 | 6 | 9 | *reinstall_sub = _build_public_installer(_carp_warnings(_installer)); | |||
128 | ||||||
129 | 6 | 7 | *_install_fatal = _do_with_warn({ | |||
130 | code => _installer, | |||||
131 | croak => [ $_redef_warn_re ], | |||||
132 | }); | |||||
133 | } | |||||
134 | ||||||
135 | ||||||
136 | sub install_installers { | |||||
137 | 0 | 0 | my ($into) = @_; | |||
138 | ||||||
139 | 0 | 0 | for my $method (qw(install_sub reinstall_sub)) { | |||
140 | my $code = sub { | |||||
141 | 0 | 0 | my ($package, $subs) = @_; | |||
142 | 0 | 0 | my ($caller) = caller(0); | |||
143 | 0 | 0 | my $return; | |||
144 | 0 | 0 | for (my ($name, $sub) = %$subs) { | |||
145 | 0 | 0 | $return = Sub::Install->can($method)->({ | |||
146 | code => $sub, | |||||
147 | from => $caller, | |||||
148 | into => $package, | |||||
149 | as => $name | |||||
150 | }); | |||||
151 | } | |||||
152 | 0 | 0 | return $return; | |||
153 | 0 | 0 | }; | |||
154 | 0 | 0 | install_sub({ code => $code, into => $into, as => $method }); | |||
155 | } | |||||
156 | } | |||||
157 | ||||||
158 | ||||||
159 | sub exporter { | |||||
160 | 12 | 12 | my ($arg) = @_; | |||
161 | ||||||
162 | 12 24 12 | 6 32 17 | my %is_exported = map { $_ => undef } @{ $arg->{exports} }; | |||
163 | ||||||
164 | sub { | |||||
165 | 0 | my $class = shift; | ||||
166 | 0 | my $target = caller; | ||||
167 | 0 | for (@_) { | ||||
168 | 0 | Carp::croak "'$_' is not exported by $class" if !exists $is_exported{$_}; | ||||
169 | 0 | install_sub({ code => $_, from => $class, into => $target }); | ||||
170 | } | |||||
171 | } | |||||
172 | 12 | 163 | } | |||
173 | ||||||
174 | 6 | 25 | BEGIN { *import = exporter({ exports => [ qw(install_sub reinstall_sub) ] }); } | |||
175 | ||||||
176 | ||||||
177 | 1; | |||||
178 |