File Coverage

blib/lib/Bread/Board/Traversable.pm
Criterion Covered Total %
statement 48 53 90.5
branch 24 30 80.0
condition 6 9 66.6
subroutine 5 5 100.0
pod 2 2 100.0
total 85 99 85.8


line stmt bran cond sub pod time code
1             package Bread::Board::Traversable;
2             our $AUTHORITY = 'cpan:STEVAN';
3             # ABSTRACT: role for traversing a container service tree
4             $Bread::Board::Traversable::VERSION = '0.37';
5 63     63   44035 use Moose::Role;
  63         163  
  63         396  
6              
7             with 'MooseX::Clone' => { -version => 0.05 };
8              
9             has 'parent' => (
10             is => 'rw',
11             isa => 'Bread::Board::Traversable',
12             weak_ref => 1,
13             clearer => 'detach_from_parent',
14             predicate => 'has_parent',
15             );
16              
17             sub get_root_container {
18 43     43 1 74 my $c = shift;
19 43         1557 while ($c->has_parent) {
20 33         862 $c = $c->parent;
21             }
22 43         95 return $c;
23             }
24              
25             sub fetch {
26 373     373 1 31058 my ($self, $path) = @_;
27              
28 373         709 my $root;
29 373 100       1075 if ($path =~ /^\//) {
30 43         125 $root = $self->get_root_container;
31             }
32             else {
33 330         624 $root = $self;
34 330         1502 while (!$root->isa('Bread::Board::Container')) {
35 228         5948 $root = $root->parent;
36             }
37             }
38              
39 373         1242 my @path = grep { $_ } split /\// => $path;
  526         1516  
40              
41 373 100       1094 if ($path[0] eq '..') {
42 14         26 my $c = $root;
43 14   100     22 do {
      66        
44 16         26 shift @path;
45 16   33     492 $c = $c->parent
46             || confess "Expected parent for " . $c->name . " but found none";
47             } while (defined $path[0] && $path[0] eq '..' && $c->has_parent);
48 14         33 $root = $c;
49             }
50              
51 373 100       934 return $root unless @path;
52              
53 371         652 my $c = $root;
54 371         1065 while (my $h = shift @path) {
55 467         1177 $c = _get_container_or_service($c, $h);
56             }
57 368 100       1997 if (!$self->isa('Bread::Board::Service::Alias')) {
58 345         674 my %seen;
59 345         1549 while ($c->isa('Bread::Board::Service::Alias')) {
60 48         1172 $c = $c->aliased_from;
61 47 100       600 confess "Cycle detected in aliases" if exists $seen{$c};
62 41         188 $seen{$c}++;
63             }
64             }
65 361         4468 return $c;
66             }
67              
68             sub _get_container_or_service {
69 467     467   883 my ($c, $name) = @_;
70              
71 467 50       1688 (blessed $c)
72             || confess "Expected object, got $c";
73              
74 467 50       2056 if ($c->isa('Bread::Board::Dependency')) {
75             # make sure to evaluate this from the parent
76 0         0 return _get_container_or_service($c->parent->parent, $name);
77             }
78              
79 467 100       1428 if ($c->does('Bread::Board::Service::WithDependencies')) {
80 35 50       19991 return $c->get_dependency($name) if $c->has_dependency($name);
81 0         0 confess "Could not find dependency ($name) from service " . $c->name;
82             }
83              
84             # name() is implemented in Service and Container
85             # get_sub_container and get_service is implemented in Container
86             # there must be a better way to do this
87              
88 432 50       28509 if ($c->does('Bread::Board::Service')) {
    100          
89 0 0       0 if ($c->name eq $name) {
90 0         0 warn "Traversing into the current service ($name) is deprecated."
91             . " You should remove the $name component from the path.";
92 0         0 return $c;
93             }
94             }
95             elsif ($c->isa('Bread::Board::Container')) {
96 431 100       35798 if ($c->name eq $name) {
97 1         25 warn "Traversing into the current container ($name) is deprecated;"
98             . " you should remove the $name component from the path";
99 1         11 return $c;
100             }
101 430 100       14658 return $c->get_sub_container($name) if $c->has_sub_container($name);
102 345 100       11856 return $c->get_service($name) if $c->has_service($name);
103             }
104              
105 3         142 confess "Could not find container or service for $name in " . $c->name;
106             }
107              
108 63     63   346794 no Moose::Role; 1;
  63         185  
  63         330  
109              
110             __END__
111              
112             =pod
113              
114             =encoding UTF-8
115              
116             =head1 NAME
117              
118             Bread::Board::Traversable - role for traversing a container service tree
119              
120             =head1 VERSION
121              
122             version 0.37
123              
124             =head1 SYNOPSIS
125              
126             my $service = $container->fetch('/some/service/path');
127              
128             my $root = $service->get_root_container;
129              
130             =head1 DESCRIPTION
131              
132             This role provides the basic functionality to traverse a container /
133             service tree. Instances of classes consuming this role will get a
134             parent-child relationship between them.
135              
136             =head1 ATTRIBUTES
137              
138             =head2 C<parent>
139              
140             Weak ref to another L<Bread::Board::Traversable> object, read/write
141             accessor (although you should probably not change this value directly
142             in normal code).
143              
144             =head1 METHODS
145              
146             =head2 C<has_parent>
147              
148             Predicate for the L</parent> attribute, true if a parent has been set.
149              
150             =head2 C<detach_from_parent>
151              
152             Clearer for the L</parent> attribute, you should probably not call
153             this method in normal code.
154              
155             =head2 C<get_root_container>
156              
157             Returns the farthest ancestor of the invocant, i.e. the top-most
158             container this object is a part of.
159              
160             =head2 C<fetch>
161              
162             my $service = $this->fetch('/absolute/path');
163             my $service = $this->fetch('relative/path');
164             my $service = $this->fetch('../relative/path');
165              
166             Given a (relative or absolute) path to a service or container, this
167             method walks the tree and returns the L<Bread::Board::Service> or
168             L<Bread::Board::Container> instance for that path. Dies if no object
169             can be found for the given
170             path.
171              
172             L<Aliases|Bread::Board::Service::Alias> are resolved in this call, by
173             calling L<< C<aliased_from>|Bread::Board::Service::Alias/aliased_from
174             >> until we get an actual service.
175              
176             =head1 AUTHOR
177              
178             Stevan Little <stevan@iinteractive.com>
179              
180             =head1 BUGS
181              
182             Please report any bugs or feature requests on the bugtracker website
183             https://github.com/stevan/BreadBoard/issues
184              
185             When submitting a bug or request, please include a test-file or a
186             patch to an existing test-file that illustrates the bug or desired
187             feature.
188              
189             =head1 COPYRIGHT AND LICENSE
190              
191             This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive.
192              
193             This is free software; you can redistribute it and/or modify it under
194             the same terms as the Perl 5 programming language system itself.
195              
196             =cut