File Coverage

blib/lib/DCI/Meta/Cast.pm
Criterion Covered Total %
statement 87 88 98.8
branch 17 24 70.8
condition 4 10 40.0
subroutine 20 20 100.0
pod 5 7 71.4
total 133 149 89.2


line stmt bran cond sub pod time code
1             package DCI::Meta::Cast;
2 23     23   43395 use strict;
  23         1097  
  23         931  
3 23     23   135 use warnings;
  23         43  
  23         2975  
4              
5 23     23   136 use base 'DCI::Meta';
  23         30  
  23         11838  
6 23     23   576 use Carp qw/croak/;
  23         50  
  23         1402  
7 23     23   151 use Scalar::Util qw/blessed reftype/;
  23         131  
  23         1255  
8 23     23   122 use Exporter::Declare qw/export_tag export default_export gen_export export_to import/;
  23         39  
  23         209  
9             require DCI::Cast;
10              
11             our $ANONYMOUS_CLASS = "AAAAAAAAAA";
12             our $AUTOLOAD;
13              
14 46     46 0 631 sub base { 'DCI::Cast' }
15 46     46 0 849 sub init { shift->{allowed_cores} = [] }
16 59     59 1 133 sub allowed_cores { @{shift->{allowed_cores}} }
  59         489  
17              
18             sub require_core {
19 32     32 1 64 my $self = shift;
20 32         35 push @{$self->{allowed_cores}} => @_;
  32         164  
21             }
22              
23             export_tag auto_delegate => qw/ AUTOLOAD can -default /;
24              
25             default_export delegate => sub {
26 19     19   278 caller->dci_meta->delegate( @_ )
27             };
28              
29             default_export require_core => sub {
30 32     32   189 caller->dci_meta->require_core( @_ );
31             };
32              
33             default_export accessors => sub {
34 26     26   143 caller->dci_meta->accessors( @_ );
35             };
36              
37             export can => sub {
38 29     29   1998 my $self = shift;
39 29         534 my ( $name ) = @_;
40              
41 29         264 my $existing = $self->SUPER::can( $name );
42 29 100       121 return $existing if $existing;
43              
44 24 100       152 return unless blessed( $self );
45              
46 11         85 my $sub = $self->dci_true_core->can( $name );
47              
48 11 100       78 unless ( $sub ) {
49 1 50       10 return if grep { m/^$name$/ } qw/DESTROY import unimport/;
  3         126  
50 0         0 croak "Core object " . $self->dci_true_core . " has no method '$name()'";
51             }
52              
53 10         54 $self->dci_meta->delegate( $name );
54              
55 10         80 $self->SUPER::can( $name );
56             };
57              
58             gen_export AUTOLOAD => sub {
59             my ( $exported_by, $import_class ) = @_;
60              
61             return sub {
62 10     10   3698 my ($self) = @_;
63 10         126 my ( $package, $name ) = ( $AUTOLOAD =~ m/^(.+)::([^:]+)$/ );
64 10         25 $AUTOLOAD = undef;
65 10   50     66 my $sub = $self->can($name) || return;
66 10         40 goto &$sub;
67             };
68             };
69              
70             sub accessors {
71 26     26 1 39 my $self = shift;
72 26         91 my $target = $self->target;
73              
74 26         65 for my $accessor ( @_ ) {
75 26 50       286 croak "'$accessor()' already defined by '$target'"
76             if $target->can( $accessor );
77              
78             $self->inject( $target, $accessor, sub {
79 3     3   8 my $self = shift;
80              
81 3 50       24 croak "$accessor cannot be called on unblessed reference '$self'"
82             unless blessed( $self );
83              
84 3         17 my $state = $self->dci_state();
85 3 50       64 ($state->{$accessor}) = @_ if @_;
86 3         20 return $state->{$accessor};
87 26         169 });
88             }
89             };
90              
91             sub delegate {
92 35     35 1 65 my $self = shift;
93 35         110 my @methods = @_;
94 35         5199 my $target = $self->target;
95              
96 35         123 for my $method ( @methods ) {
97             $self->inject( $target, $method => sub {
98 65     65   1421893 my $cast = shift;
99              
100 65 100       463 croak "method '$method()' cannot be called on string '$cast'"
101             unless blessed $cast;
102              
103 64         335 my $core = $cast->dci_core;
104 64         523 my $sub = $core->can($method);
105              
106 64 100       187 unless( $sub ) {
107 1         25 my $core_class = blessed( $core );
108 1         85 croak "method '$method()' is not implemented by core class '$core_class'";
109             }
110              
111 63         124 unshift @_ => $core;
112              
113 63         280 goto &$sub;
114 61         766 });
115             }
116             }
117              
118             sub anonymous {
119 6     6 1 12 my $class = shift;
120 6         18 my %params = @_;
121              
122 6         24 my $package = __PACKAGE__ . "::__ANON__::" . $ANONYMOUS_CLASS++;
123 6         18 my $file = "/$package.pm";
124 6         33 $file =~ s|::|/|g;
125 6   50     42 $INC{$file} ||= __FILE__;
126              
127 6         30 my $meta = $class->new( $package );
128              
129 6         21 for my $sugar ( grep { $class->can( $_ )} keys %params ) {
  12         66  
130 6         24 my $item = delete $params{$sugar};
131 6         15 my $type = reftype( $item );
132              
133 6 50 33     24 croak "'$sugar' accepts either a string, or an array of strings, not '$type'"
134             if $type && $type ne 'ARRAY';
135              
136 6 50       33 $meta->$sugar( $type ? (@$item) : ($item) );
137             }
138              
139 6         15 for my $method ( keys %params ) {
140 6         9 my $sub = delete $params{$method};
141 6         27 my $type = reftype( $sub );
142              
143 6 50 33     36 croak "value for '$method' must be a subref, '$method' is not a method on '$class'"
144             unless $type && $type eq 'CODE';
145              
146 6         15 $meta->inject( $package, $method => $sub );
147             }
148              
149 6         21 return $package;
150             }
151              
152             1;
153              
154             __END__