Skip to content

Commit 5c74052

Browse files
committed
Proposal for Test2::V1
Still need to add tests and POD (I finished the POD for V1 itself) Also need to modigy other docs that currently reference V0.
1 parent 5497eed commit 5c74052

File tree

6 files changed

+1364
-1
lines changed

6 files changed

+1364
-1
lines changed

lib/T2.pm

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package T2;
2+
use strict;
3+
use warnings;
4+
5+
my $INIT;
6+
my $HANDLE;
7+
sub handle { $HANDLE }
8+
9+
sub import {
10+
my $class = shift;
11+
my ($handle) = @_;
12+
13+
my ($caller, $file, $line) = caller;
14+
15+
die "The ${ \__PACKAGE__ } namespace has already been initialized (Originally initiated at $INIT->[1] line $INIT->[2]) at $file line $line.\n"
16+
if $INIT;
17+
18+
unless ($handle) {
19+
die "The '$caller' package does not provide a T2 handler at $file line $line.\n"
20+
unless $caller->can('T2');
21+
22+
$handle = $caller->T2 or die "Could not get handle via '$caller\->T2()' at $file line $line.\n";
23+
}
24+
25+
die "'$handle' is not a Test2::Handle instance at $file line $line.\n"
26+
unless $handle->isa('Test2::Handle');
27+
28+
$INIT = [$caller, $file, $line];
29+
$HANDLE = $handle;
30+
31+
for my $sym ($HANDLE->HANDLE_SUBS) {
32+
next if $sym eq 'import';
33+
next if $sym eq 'handle';
34+
35+
my $code = $HANDLE->HANDLE_NAMESPACE->can($sym);
36+
my $proto = prototype($code);
37+
38+
my $header = defined($proto) ? "sub $sym($proto) {" : "sub $sym {";
39+
40+
my $line = __LINE__ + 3;
41+
my $sub = eval <<" EOT" or die $@;
42+
#line $line ${ \__FILE__ }
43+
$header
44+
my (\$f) = \@_;
45+
shift if \$f && "\$f" eq "$class";
46+
goto &\$code;
47+
};
48+
49+
\\&$sym;
50+
EOT
51+
52+
no strict 'refs';
53+
*$sym = $sub;
54+
}
55+
}
56+
57+
sub AUTOLOAD {
58+
my ($this) = @_;
59+
60+
if ($this) {
61+
shift if "$this" eq 'T2';
62+
shift if ref($this) eq 'T2';
63+
}
64+
65+
my ($name) = (our $AUTOLOAD =~ m/^(?:.*::)?([^:]+)$/);
66+
67+
my @caller = caller;
68+
my $sub = $HANDLE->HANDLE_NAMESPACE->can($name) or die qq{"$name" is not provided by this T2 handle at $caller[1] line $caller[2].\n};
69+
goto &$sub;
70+
}
71+
72+
1;

lib/Test2/Handle.pm

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package Test2::Handle;
2+
use strict;
3+
use warnings;
4+
5+
our $VERSION = '1.302217';
6+
7+
require Carp;
8+
require Test2::Util;
9+
10+
use Test2::Util::HashBase qw{
11+
+namespace
12+
+base
13+
+include
14+
+import
15+
+stash
16+
+stomp
17+
};
18+
19+
my $NS = 1;
20+
21+
# Things we do not want to import automagically
22+
my %EXCLUDE_SYMBOLS = (
23+
BEGIN => 1,
24+
DESTROY => 1,
25+
DOES => 1,
26+
END => 1,
27+
VERSION => 1,
28+
does => 1,
29+
);
30+
31+
sub HANDLE_BASE { Carp::croak("Not Implemented") }
32+
33+
sub HANDLE_NAMESPACE { $_[0]->{+NAMESPACE} }
34+
35+
sub HANDLE_SUBS {
36+
my $self = shift;
37+
38+
my @out;
39+
40+
my $seen = {class => {}, export => {}};
41+
my @todo = ($self->{+NAMESPACE});
42+
43+
while (my $check = shift @todo) {
44+
next if $seen->{class}->{$check}++;
45+
46+
no strict 'refs';
47+
my $stash = \%{"$check\::"};
48+
push @out => grep { !$seen->{export}->{$_}++ && !$EXCLUDE_SYMBOLS{$_} && $_ !~ m/^_/ && $check->can($_) } keys %$stash;
49+
push @todo => @{"$check\::ISA"};
50+
}
51+
52+
return @out;
53+
}
54+
55+
sub import {
56+
my $class = shift;
57+
my ($name, %params) = @_;
58+
59+
my $self = $class->new(%params);
60+
61+
my $caller = caller;
62+
no strict 'refs';
63+
*{"$caller\::$name"} = sub() { $self };
64+
}
65+
66+
sub init {
67+
my $self = shift;
68+
69+
my $stomp = $self->{+STOMP} ||= 0;
70+
my $inc = $self->{+INCLUDE} ||= [];
71+
my $base = $self->{+BASE} ||= $self->HANDLE_BASE;
72+
73+
require(Test2::Util::pkg_to_file($base));
74+
75+
my $new;
76+
my $ns = $self->{+NAMESPACE} ||= do { $new = 1; __PACKAGE__ . '::GEN_' . $NS++ };
77+
my $stash = $self->{+STASH} ||= do { no strict 'refs'; \%{"$ns\::"} };
78+
79+
Carp::croak("Namespace '$ns' already appears to be populated") if !$stomp && keys %$stash;
80+
81+
$INC{Test2::Util::pkg_to_file($ns)} ||= __FILE__ if $new;
82+
83+
my $line = __LINE__ + 3;
84+
$self->{+IMPORT} ||= eval <<" EOT" or die $@;
85+
#line $line ${ \__FILE__ }
86+
package $ns;
87+
sub {
88+
my (\$module, \@imports) = \@_;
89+
require(Test2::Util::pkg_to_file(\$module));
90+
\$module->import(\@imports);
91+
};
92+
EOT
93+
94+
{
95+
no strict 'refs';
96+
push @{"$ns\::ISA"} => $self->{+BASE};
97+
}
98+
99+
if (my $include = $self->{+INCLUDE}) {
100+
my $r = ref($include);
101+
if ($r eq 'ARRAY') {
102+
$self->include(ref($_) ? @{$_} : $_) for @$include;
103+
}
104+
elsif ($r eq 'HASH') {
105+
$self->include($_ => @{$include->{$_} || []});
106+
}
107+
else {
108+
die "Not sure what to do with '$r'";
109+
}
110+
}
111+
}
112+
113+
my $WRAP = sub {
114+
my $self = shift;
115+
my ($name) = @_;
116+
117+
return if $self->can($name);
118+
119+
my $wrap = sub {
120+
my $handle = shift;
121+
my $ns = $handle->{+NAMESPACE};
122+
my @caller = caller;
123+
my $sub = $ns->can($name) or die qq{"$name" is not provided by this T2 handle at $caller[1] line $caller[2].\n};
124+
goto &$sub;
125+
};
126+
127+
{
128+
no strict 'refs';
129+
*$name = $wrap;
130+
}
131+
132+
return $wrap;
133+
};
134+
135+
sub include {
136+
my $self = shift;
137+
my ($mod, @imports) = @_;
138+
@imports = @{$imports[0]} if @imports == 1 && ref($imports[0]) eq 'ARRAY';
139+
140+
$self->{+IMPORT}->($mod, @imports);
141+
$self->$WRAP($_) for @imports;
142+
}
143+
144+
sub AUTOLOAD {
145+
my ($self) = @_;
146+
147+
my ($name) = (our $AUTOLOAD =~ m/^(?:.*::)?([^:]+)$/);
148+
return if $EXCLUDE_SYMBOLS{$name};
149+
150+
my $wrap = $self->$WRAP($name);
151+
goto &$wrap;
152+
}
153+
154+
1;
155+
156+
=head1 FIX POD

0 commit comments

Comments
 (0)