File Coverage

blib/lib/Nephia/Chain.pm
Criterion Covered Total %
statement 99 99 100.0
branch 25 30 83.3
condition n/a
subroutine 21 21 100.0
pod 9 11 81.8
total 154 161 95.6


line stmt bran cond sub pod time code
1             package Nephia::Chain;
2 14     14   30689 use strict;
  14         27  
  14         469  
3 14     14   75 use warnings;
  14         25  
  14         333  
4 14     14   131 use Carp;
  14         25  
  14         1022  
5 14     14   78 use Scalar::Util ();
  14         23  
  14         24770  
6              
7             sub new {
8 82     82 0 5137 my ($class, %opts) = @_;
9 82 100       229 $opts{namespace} = $class.'::Item' unless defined $opts{namespace};
10 82 100       223 $opts{name_normalize} = 1 unless defined $opts{name_normalize};
11 82         574 bless {chain => [], from => {}, %opts}, $class;
12             }
13              
14             sub append {
15 94     94 1 5468 my $self = shift;
16 94         169 my $from = caller;
17 94         1026 my @actions = $self->_inject('Tail', 1, @_);
18 92         138 $self->{from}{$_} = $from for map {ref($_)} @actions;
  93         655  
19             }
20              
21             sub prepend {
22 21     21 1 36 my $self = shift;
23 21         50 my $from = caller;
24 21         241 my @actions = $self->_inject('Head', 0, @_);
25 21         42 $self->{from}{$_} = $from for map {ref($_)} @actions;
  21         129  
26             }
27              
28             sub before {
29 1     1 1 4 my ($self, $search, @opts) = @_;
30 1         4 my $from = caller;
31 1         14 my @actions = $self->_inject($search, 0, @opts);
32 1         2 $self->{from}{$_} = $from for map {ref($_)} @actions;
  1         5  
33             }
34              
35             sub after {
36 1     1 1 5 my ($self, $search, @opts) = @_;
37 1         4 my $from = caller;
38 1         18 my @actions = $self->_inject($search, 1, @opts);
39 1         3 $self->{from}{$_} = $from for map {ref($_)} @actions;
  1         7  
40             }
41              
42             sub delete {
43 1     1 1 408 my ($self, $search) = @_;
44 1         2 splice( @{$self->{chain}}, $self->index($search), 1);
  1         3  
45 1         12 delete $self->{from}{$self->_normalize_name($search)};
46             }
47              
48             sub size {
49 223     223 1 7846 my $self = shift;
50 223         272 return scalar( @{$self->{chain}} );
  223         781  
51             }
52              
53             sub from {
54 9     9 1 27 my ($self, $name) = @_;
55 9         44 return $self->{from}{$self->_normalize_name($name)};
56             }
57              
58             sub index {
59 238     238 1 719 my ($self, $name) = @_;
60 238 100       521 return 0 if $name eq 'Head';
61 217 100       525 return $self->size - 1 if $name eq 'Tail';
62 123         229 my $normalized_name = $self->_normalize_name($name);
63 123         272 for my $i (0 .. $self->size -1) {
64 123 100       1323 return $i if $self->{chain}[$i]->isa($normalized_name);
65             }
66             }
67              
68             sub as_array {
69 125     125 1 177 my $self = shift;
70 125         142 return @{$self->{chain}};
  125         538  
71             }
72              
73             sub fetch {
74 2     2 0 3 my ($self, $plugin) = @_;
75 2         4 for my $obj (@{$self->{chain}}) {
  2         4  
76 3 100       14 return $obj if ref($obj) eq $plugin;
77             }
78 1         4 return undef;
79             }
80              
81             sub _inject {
82 117     117   182 local $Carp::CarpLevel = $Carp::CarpLevel + 2;
83 117         240 my ($self, $search, $after, @opts) = @_;
84 117         280 my $index = $self->index($search);
85 117         160 $index += $after;
86 117         283 my @actions = $self->_bless_actions(@opts);
87 115         144 splice @{$self->{chain}}, $index, 0, @actions;
  115         275  
88 115         300 return @actions;
89             }
90              
91             sub _validate_action_opts {
92 77     77   115 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
93 77         112 my ($self, $name, $code) = @_;
94 77 50       160 croak "name is undefined" unless $name;
95 77 100       194 croak "code for $name is undefined" unless $code;
96 75 50       160 croak "illegal name $name" if ref($name);
97 75         172 return ( $self->_normalize_name($name), $code );
98             }
99              
100             sub _check_duplicates {
101 115     115   167 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
102 115         197 my ($self, @action) = @_;
103 115         171 for my $name ( map {ref($_)} @action ) {
  116         319  
104 116 50       247 croak "name $name is already stored" if $self->index($name);
105 116 50       198 croak "duplicate name $name" if scalar( grep {ref($_) eq $name} @action ) > 1;
  118         576  
106             }
107             }
108              
109             sub _normalize_name {
110 208     208   274 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
111 208         365 my ($self, $name) = @_;
112 208 100       526 return $name unless $self->{name_normalize};
113 163         223 my $namespace = $self->{namespace};
114 163 100       1467 return $name =~ /^$namespace\:\:/ ? $name : $namespace.'::'.$name;
115             }
116              
117             sub _bless_actions {
118 117     117   159 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
119 117         239 my ($self, @opts) = @_;
120 117         125 my @rtn;
121 117         255 while (@opts) {
122 120         296 push @rtn, $self->_shift_as_action(\@opts);
123             }
124 115         733 $self->_check_duplicates(@rtn);
125 115 50       395 return wantarray ? @rtn : $rtn[0];
126             }
127              
128             sub _shift_as_action {
129 120     120   158 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
130 120         150 my ($self, $opts) = @_;
131 120 100       533 return shift(@$opts) if Scalar::Util::blessed($opts->[0]);
132 77         127 my $name = shift(@$opts);
133 77         113 my $code = shift(@$opts);
134 77         184 ($name, $code) = $self->_validate_action_opts($name, $code);
135 75         564 return bless($code, $name);
136             }
137              
138             1;
139              
140             __END__