File Coverage

blib/lib/Import/Box.pm
Criterion Covered Total %
statement 46 48 95.8
branch 12 16 75.0
condition n/a
subroutine 15 16 93.7
pod 2 2 100.0
total 75 82 91.4


line stmt bran cond sub pod time code
1             package Import::Box;
2 8     8   3855 use strict;
  8         8  
  8         199  
3 8     8   27 use warnings;
  8         16  
  8         269  
4              
5             our $VERSION = '0.001';
6              
7 8     8   25 use Scalar::Util();
  8         10  
  8         85  
8 8     8   23 use Carp();
  8         6  
  8         128  
9 8     8   37 use vars qw/$AUTOLOAD/;
  8         14  
  8         3101  
10              
11             my %STASHES;
12              
13       0     sub __DEFAULT_AS { }
14       17     sub __DEFAULT_NS { }
15              
16             # We do not want this methods to be accessible, so we are putting it in a
17             # lexical variable to use internally.
18             my $GEN_STASH = do {
19             my $GEN = 'A';
20             sub { __PACKAGE__ . '::__GEN_STASH__::' . shift(@_) . '::__' . ($GEN++) . '__' };
21             };
22              
23             # We do not want this methods to be accessible, so we are putting it in a
24             # lexical variable to use internally.
25             my $IMPORT = sub {
26             my $proto = shift;
27             my $caller = shift;
28              
29             return unless @_;
30              
31             my (%params, @loads);
32             while (my $arg = shift) {
33             if (substr($arg, 0, 1) eq '-') {
34             $params{$arg} = shift;
35             }
36             else {
37             my $args = ref($_[0]) ? shift : [];
38             push @loads => [$arg, @$args];
39             }
40             }
41              
42             my ($stash, $class, $no_scope);
43             if ($class = Scalar::Util::blessed($proto)) {
44             Carp::croak("Params are not allowed when using import() as an object method: " . join(', ', keys %params))
45             if keys %params;
46              
47             $stash = $$proto;
48             $no_scope = 1;
49             }
50             else {
51             $class = $proto;
52              
53             my $as = delete $params{'-as'} || $class->__DEFAULT_AS;
54             Carp::croak(qq{No box name specified, and no default available, please specify with '-as => "BOXNAME"'})
55             unless defined $as;
56              
57             my $from = delete $params{'-from'} || $caller->[0];
58             $no_scope = delete $params{'-no_scope'};
59              
60             Carp::croak("Invalid params: " . join(', ', keys %params))
61             if keys %params;
62              
63             $stash = $STASHES{$from}->{$as} ||= $GEN_STASH->($caller->[0]);
64             bless(\$stash, $class) unless Scalar::Util::blessed(\$stash);
65              
66             unless ($caller->[0]->can($as)) {
67             my $t = sub {
68 59 100   59   13011 return \$stash unless @_;
69              
70 10         14 my $meth = shift;
71              
72 10 50       66 my $sub = $stash->can($meth)
73             or Carp::croak("No such function: '$meth'");
74              
75 10         33 goto &$sub;
76             };
77              
78 8     8   36 no strict 'refs';
  8         12  
  8         3711  
79             *{"$caller->[0]\::$as"} = $t;
80             }
81             }
82              
83             return unless @loads;
84              
85             my $header = qq{package $stash;\n#line $caller->[2] "$caller->[1]"};
86             my $sub = $no_scope ? undef : eval qq{$header\nsub { shift\->import(\@_) };} || die $@;
87             my $prefix = $class->__DEFAULT_NS;
88              
89             for my $set (@loads) {
90             my ($mod, @args) = @$set;
91              
92             # Strip '+' prefix OR append module prefix
93             unless ($mod =~ s/^\+//) {
94             $mod = "$prefix\::$mod" if $prefix;
95             }
96              
97             my $file = $mod;
98             $file =~ s{::|'}{/}g;
99             $file .= '.pm';
100             require $file;
101              
102             if ($no_scope) {
103             eval qq{$header\nBEGIN { \$mod\->import(\@args) }; 1} or die $@;
104             }
105             else {
106             $sub->($mod, @args);
107             }
108             }
109             };
110              
111             sub import {
112 20     20   5449 my $proto = shift;
113 20         133 my @caller = caller(0);
114              
115 20         67 $proto->$IMPORT(\@caller, @_);
116             }
117              
118             sub new {
119             my $class = shift;
120             my @caller = caller(0);
121              
122             my $stash = $GEN_STASH->($caller[0]);
123             my $self = bless(\$stash, $class);
124              
125             $self->$IMPORT(\@caller, @_) if @_;
126              
127             return $self;
128             }
129              
130             sub box {
131             my $class = shift;
132             my $stash = shift;
133             my $self = bless(\$stash, $class);
134             return $self;
135             }
136              
137             # These methods need to instead call the stash version when called on an
138             # instance.
139             for my $meth (qw/new box/) {
140             my $orig = __PACKAGE__->can($meth);
141              
142             my $new = sub {
143 4     4   4698 my ($proto) = @_;
144 4 50       27 goto &$orig unless Scalar::Util::blessed($proto);
145              
146 0 0       0 my $sub = $proto->can($meth) or Carp::croak("No such function: '$meth'");
147 0         0 goto &$sub;
148             };
149              
150 8     8   40 no strict 'refs';
  8         7  
  8         183  
151 8     8   25 no warnings 'redefine';
  8         7  
  8         1740  
152             *$meth = $new;
153             }
154              
155             sub can {
156 82     82 1 604 my $proto = shift;
157 82 100       335 my $class = Scalar::Util::blessed($proto)
158             or return $proto->SUPER::can(@_);
159              
160 64         77 my $stash = $$proto;
161 64         610 return $stash->can(@_);
162             }
163              
164             sub isa {
165 3     3 1 257 my $proto = shift;
166 3         26 return $proto->SUPER::isa(@_);
167             }
168              
169             sub AUTOLOAD {
170 56     56   1814 my $meth = $AUTOLOAD;
171 56         220 $meth =~ s/^.*:://g;
172              
173 56 100       124 return if $meth eq 'DESTROY';
174              
175 55 100       343 my $class = Scalar::Util::blessed($_[0])
176             or Carp::croak(qq{Can't locate object method "$meth" via package "$_[0]"});
177              
178             # Need to shift self as the "methods" are actually functions imported into
179             # the stash.
180 54         67 my $self = shift @_;
181              
182 54 100       93 my $sub = $self->can($meth)
183             or Carp::croak("No such function '$meth'");
184              
185 52         180 goto &$sub;
186             }
187              
188             1;
189              
190             __END__