File Coverage

blib/lib/Bolts/Role/RootLocator.pm
Criterion Covered Total %
statement 59 65 90.7
branch 24 30 80.0
condition 18 23 78.2
subroutine 10 10 100.0
pod 3 3 100.0
total 114 131 87.0


line stmt bran cond sub pod time code
1             package Bolts::Role::RootLocator;
2             $Bolts::Role::RootLocator::VERSION = '0.143171';
3             # ABSTRACT: Interface for locating artifacts from some root bag
4              
5 11     11   6546 use Moose::Role;
  11         6526  
  11         68  
6              
7             with 'Bolts::Role::Locator';
8              
9 11     11   42518 use Bolts::Locator;
  11         17  
  11         218  
10 11     11   37 use Bolts::Util;
  11         16  
  11         172  
11 11     11   37 use Carp ();
  11         12  
  11         109  
12 11     11   924 use Safe::Isa;
  11         650  
  11         1267  
13 11     11   51 use Scalar::Util ();
  11         12  
  11         5474  
14              
15              
16             # TODO Rename root to something better.
17             requires 'root';
18              
19              
20             sub acquire {
21 1029     1029 1 21223 my ($self, @path) = @_;
22             # use Data::Dumper;
23             # Carp::cluck(Dumper(\@path));
24              
25 1029         1245 my $options = {};
26 1029 100 100     4160 if (@path > 1 and ref $path[-1]) {
27 770         875 $options = pop @path;
28             }
29            
30 1029         1420 my $current_path = '';
31              
32 1029         2744 my $item = $self->root;
33 1029         1856 while (@path) {
34 1506         1578 my $component = shift @path;
35              
36 1506         1435 my $bag = $item;
37 1506         2719 $item = $self->_get_from($bag, $component, $current_path);
38 1502         3858 $item = $self->resolve($bag, $item, $options);
39              
40             # If the $item is a locator, pass control of the process to that
41 1500 100 100     4552 if (@path && $item->$_can('does') && $item->$_does('Bolts::Role::Locator')) {
      100        
42 474         30206 $item->acquire(@path, $options);
43             }
44              
45 1499 100       2997 $current_path .= ' ' if $current_path;
46 1499         4421 $current_path .= qq["$component"];
47             }
48              
49 1022         9073 return $item;
50             }
51              
52              
53             sub acquire_all {
54 63     63 1 411 my ($self, @path) = @_;
55              
56 63         85 my $options = {};
57 63 50 33     201 if (@path > 1 and ref $path[-1]) {
58 0         0 $options = pop @path;
59             }
60            
61 63         128 my $bag = $self->acquire(@path, $options);
62 63 100       166 if ('ARRAY' eq ref $bag) {
    100          
63             return [
64 59         119 map { $self->resolve($bag, $_, $options) } @$bag
  63         115  
65             ];
66             }
67              
68             elsif ('HASH' eq ref $bag) {
69             return [
70 2         6 map { $self->resolve($bag, $_, $options) } values %$bag
  6         8  
71             ];
72             }
73              
74             else {
75 2         10 return [ $bag ];
76             }
77             }
78              
79              
80             sub resolve {
81 1571     1571 1 1776 my ($self, $bag, $item, $options) = @_;
82              
83 1571 100 100     3154 return $item->get($bag, %$options)
84             if $item->$_can('does')
85             and $item->$_does('Bolts::Role::Artifact');
86              
87 535         38766 return $item;
88             }
89              
90             sub _get_from {
91 1506     1506   1746 my ($self, $bag, $component, $current_path) = @_;
92 1506   50     2435 $current_path //= '';
93              
94 1506 50       2360 Carp::croak("unable to acquire artifact for [$current_path]")
95             unless defined $bag;
96              
97             # A bag can be any blessed object...
98 1506 100       3631 if (Scalar::Util::blessed($bag)) {
    100          
    50          
99              
100             # If it is marked as an Opaque object, we can't locate within it
101 1503 100 66     6066 if ($bag->can('does') && $bag->does('Bolts::Role::Opaque')) {
    50          
102 1         318 Carp::croak(qq{may not examine "$component" at opaque path [$current_path]});
103             }
104              
105             # So long as it has that method
106             elsif ($bag->can($component)) {
107 1502         162562 return $bag->$component;
108             }
109            
110             # We don't know how to deal with it otherwise
111             else {
112 0         0 Carp::croak(qq{no artifact named "$component" at [$current_path]});
113             }
114             }
115              
116             # Or any unblessed hash
117             elsif (ref $bag eq 'HASH') {
118 1 50       5 if (exists $bag->{ $component }) {
119 0         0 return $bag->{ $component };
120             }
121             else {
122 1         126 Carp::croak(qq{no artifact keyed "$component" at [$current_path]});
123             }
124             }
125              
126             # Or any unblessed array
127             elsif (ref $bag eq 'ARRAY') {
128 2 50 66     16 if ($component =~ /^\d+$/ && @{ $bag } > $component) {
  1         7  
129 0         0 return $bag->[ $component ];
130             }
131             else {
132 2         287 Carp::croak(qq{no artifact indexed "$component" at [$current_path]});
133             }
134             }
135              
136             # But nothing else...
137             else {
138 0           my $path = join ' ', grep defined, $current_path, $component;
139 0           Carp::croak(qq{not able to acquire artifact for [$path]});
140             }
141             }
142              
143             1;
144              
145             __END__
146              
147             =pod
148              
149             =encoding UTF-8
150              
151             =head1 NAME
152              
153             Bolts::Role::RootLocator - Interface for locating artifacts from some root bag
154              
155             =head1 VERSION
156              
157             version 0.143171
158              
159             =head1 DESCRIPTION
160              
161             This is the interface that any locator must implement. A locator's primary job is to provide a way to find artifacts within a bag or selection of bags. This performs the acquisition and resolution process. This actually also implements everything needed for the process except for the L</root>.
162              
163             =head1 ROLES
164              
165             =over
166              
167             =item *
168              
169             L<Bolts::Role::Locator>
170              
171             =back
172              
173             =head1 REQUIRED METHODS
174              
175             =head2 root
176              
177             This is the object to use as the bag to start searching. It may be an object, a reference to an array, or a reference to a hash.
178              
179             B<Caution:> This will be renamed in the future.
180              
181             =head1 METHODS
182              
183             =head2 acquire
184              
185             my $artifact = $loc->acquire(@path, \%options);
186              
187             Given a C<@path> of symbol names to traverse, this goes through each artifact in turn, resolves it, if necessary, and then continues to the next path component.
188              
189             After it finds the artifact, it will resolve the artifact using the L</resolve> method, which is passed the (optional) B<%options>.
190              
191             When complete, the complete, resolved artifact found is returned.
192              
193             Acquisition in this implementation proceeds according to the following rules:
194              
195             =over
196              
197             =item 1.
198              
199             If the bag is an object:
200              
201             =over
202              
203             =item *
204              
205             If the object implements the L<Bolts::Role::Opaque> role, lookup will end in an error.
206              
207             =item *
208              
209             If the object implements the L<Bolts::Role::Locator> role, control of the lookup for the remaining components in C<@path> will pass over to that object.
210              
211             =item *
212              
213             If the C<can> method on the object returns false for the next component in C<@path>, lookup will end in an error.
214              
215             =item *
216              
217             Finally, the method named in the next component in C<@path> will be called and the result used as either the value to resolve or the next bag to locate within (repeating this process).
218              
219             =back
220              
221             =item 2.
222              
223             If the bag is a hash, the name of the next component in C<@path> will be used as a key in the hash to find the next value to resolve or the next bag to locate within.
224              
225             =item 3.
226              
227             If the bag is an array and the name of the next component in C<@path> is a number, it will be used as the index to fetch from the array to use as the value to resolve or the next bag to locate within.
228              
229             =item 4.
230              
231             Anything else will result in lookup ending in an error.
232              
233             =back
234              
235             =head2 acquire_all
236              
237             my @artifacts = @{ $loc->acquire_all(\@path) };
238              
239             This is similar to L<acquire>, but returns the value as a reference to an array of resolved artifacts.
240              
241             =head2 resolve
242              
243             my $resolved_artifact = $loc->resolve($bag, $artifact, \%options);
244              
245             After the artifact has been found, this method resolves the a partial artifact implementing the L<Bolts::Role::Artifact> and turns it into the complete artifact.
246              
247             =head1 AUTHOR
248              
249             Andrew Sterling Hanenkamp <hanenkamp@cpan.org>
250              
251             =head1 COPYRIGHT AND LICENSE
252              
253             This software is copyright (c) 2014 by Qubling Software LLC.
254              
255             This is free software; you can redistribute it and/or modify it under
256             the same terms as the Perl 5 programming language system itself.
257              
258             =cut