File Coverage

blib/lib/Path/Resolver/Role/Resolver.pm
Criterion Covered Total %
statement 17 20 85.0
branch 3 6 50.0
condition n/a
subroutine 6 7 85.7
pod 3 3 100.0
total 29 36 80.5


line stmt bran cond sub pod time code
1             package Path::Resolver::Role::Resolver 3.100455;
2             # ABSTRACT: resolving paths is just what resolvers do!
3 2     2   1683 use Moose::Role;
  2         4034  
  2         11  
4              
5 2     2   9214 use namespace::autoclean;
  2         4  
  2         15  
6              
7 2     2   122 use File::Spec::Unix;
  2         4  
  2         78  
8 2     2   10 use MooseX::Types;
  2         4  
  2         17  
9              
10             #pod =head1 DESCRIPTION
11             #pod
12             #pod A class that implements this role can be used to resolve paths into entities.
13             #pod They declare the type of entity that they will produce internally, and may have
14             #pod a mechanism for converting that entity into another type before returning it.
15             #pod
16             #pod =method entity_at
17             #pod
18             #pod my $entity = $resolver->entity_at($path);
19             #pod
20             #pod This is the most important method in a resolver. It is handed a unix-style
21             #pod filepath and does one of three things:
22             #pod
23             #pod =over
24             #pod
25             #pod =item * returns an entity if a suitable one can be found
26             #pod
27             #pod =item * returns undef if no entity can be found
28             #pod
29             #pod =item * raises an exception if the entity found is unsuitable or if an error occurs
30             #pod
31             #pod =back
32             #pod
33             #pod Much of the logic of this method is implemented by an C<around> modifier
34             #pod applied by the role. This modifier will convert paths from strings into
35             #pod arrayrefs of path parts.
36             #pod
37             #pod Empty path parts are removed -- except for the first, which would represent the
38             #pod root are skipped, and the last, which would imply that you provided a path
39             #pod ending in /, which is a directory.
40             #pod
41             #pod If the resolver has a C<converter> (see below) then the found entity will be
42             #pod passed to the converter and the result will be returned. Otherwise, the entity
43             #pod will be type-checked and returned.
44             #pod
45             #pod This means that to write a resolver, you must write a C<entity_at> method that
46             #pod accepts an arrayref of path parts (strings) and returns an object of the type
47             #pod indicated by the resolver's C<native_type> method (below).
48             #pod
49             #pod =cut
50              
51             requires 'entity_at';
52              
53             around entity_at => sub {
54             my ($orig, $self, $path) = @_;
55             my @input_path;
56              
57             if (ref $path) {
58             @input_path = @$path;
59             } else {
60             Carp::confess("invalid path: empty") unless defined $path and length $path;
61              
62             @input_path = File::Spec::Unix->splitdir($path);
63             }
64              
65             Carp::confess("invalid path: empty") unless @input_path;
66             Carp::confess("invalid path: ends with non-filename")
67             if $input_path[-1] eq '';
68              
69             my @return_path;
70             push @return_path, (shift @input_path) if $input_path[0] eq '';
71             push @return_path, grep { defined $_ and length $_ } @input_path;
72              
73             my $entity = $self->$orig(\@return_path);
74              
75             return unless defined $entity;
76              
77             if (my $conv = $self->converter) {
78             return $conv->convert($entity);
79             } else {
80             my $native_type = $self->native_type;
81              
82             if (my $error = $native_type->validate($entity)) {
83             confess $error;
84             }
85              
86             return $entity;
87             }
88             };
89              
90             #pod =method native_type
91             #pod
92             #pod This method should return a L<Moose::Meta::TypeConstraint> indicating the type
93             #pod of entity that will be located by the resolver's native C<entity_at>.
94             #pod
95             #pod It must be provided by classes implementing the Path::Resolver::Role::Resolver
96             #pod role.
97             #pod
98             #pod =cut
99              
100             requires 'native_type';
101              
102             #pod =method effective_type
103             #pod
104             #pod This method returns the type that the wrapped C<entity_at> method will return.
105             #pod This means that if there is a converter (see below) it will return the
106             #pod converter's output type. Otherwise, it will return the resolver's native type.
107             #pod
108             #pod =cut
109              
110             sub effective_type {
111 0     0 1 0 my ($self) = @_;
112 0 0       0 return $self->native_type unless $self->converter;
113 0         0 return $self->converter->output_type;
114             }
115              
116             #pod =method converter
117             #pod
118             #pod The converter method (actually an attribute) may be undef or may be an object
119             #pod that implements the
120             #pod L<Path::Resolver::Role::Converter|Path::Resolver::Role::Converter> object.
121             #pod
122             #pod It will be used to convert objects from the resolver's native type to another
123             #pod type.
124             #pod
125             #pod =cut
126              
127             has converter => (
128             is => 'ro',
129             isa => maybe_type( role_type('Path::Resolver::Role::Converter') ),
130             builder => 'default_converter',
131             );
132              
133             #pod =method default_converter
134             #pod
135             #pod This method can be implemented by resolver classes to set a default converter.
136             #pod The version provided by this role returns false.
137             #pod
138             #pod To see an example of this put to use, see
139             #pod L<Path::Resolver::Role::FileResolver>.
140             #pod
141             #pod =cut
142              
143 8     8 1 10479 sub default_converter { return }
144              
145             #pod =method content_for
146             #pod
147             #pod my $content_ref = $resolver->content_for($path);
148             #pod
149             #pod This method is provided with backward compatibility with previous versions of
150             #pod Path::Resolver. B<This method will be removed in the near future.>
151             #pod
152             #pod It calls C<entity_at> and then calls the C<content_ref> on the entity. If
153             #pod the entity doesn't provide a C<content_ref> method, an exception will be
154             #pod thrown.
155             #pod
156             #pod =cut
157              
158             sub content_for {
159 33     33 1 21520 my ($self, $path) = @_;
160 33 100       102 return unless my $entity = $self->entity_at($path);
161              
162 23 50       2851 confess "located entity can't perform the content_ref method"
163             unless $entity->can('content_ref');
164              
165 23         593 return $entity->content_ref;
166             }
167              
168             1;
169              
170             __END__
171              
172             =pod
173              
174             =encoding UTF-8
175              
176             =head1 NAME
177              
178             Path::Resolver::Role::Resolver - resolving paths is just what resolvers do!
179              
180             =head1 VERSION
181              
182             version 3.100455
183              
184             =head1 DESCRIPTION
185              
186             A class that implements this role can be used to resolve paths into entities.
187             They declare the type of entity that they will produce internally, and may have
188             a mechanism for converting that entity into another type before returning it.
189              
190             =head1 PERL VERSION
191              
192             This library should run on perls released even a long time ago. It should work
193             on any version of perl released in the last five years.
194              
195             Although it may work on older versions of perl, no guarantee is made that the
196             minimum required version will not be increased. The version may be increased
197             for any reason, and there is no promise that patches will be accepted to lower
198             the minimum required perl.
199              
200             =head1 METHODS
201              
202             =head2 entity_at
203              
204             my $entity = $resolver->entity_at($path);
205              
206             This is the most important method in a resolver. It is handed a unix-style
207             filepath and does one of three things:
208              
209             =over
210              
211             =item * returns an entity if a suitable one can be found
212              
213             =item * returns undef if no entity can be found
214              
215             =item * raises an exception if the entity found is unsuitable or if an error occurs
216              
217             =back
218              
219             Much of the logic of this method is implemented by an C<around> modifier
220             applied by the role. This modifier will convert paths from strings into
221             arrayrefs of path parts.
222              
223             Empty path parts are removed -- except for the first, which would represent the
224             root are skipped, and the last, which would imply that you provided a path
225             ending in /, which is a directory.
226              
227             If the resolver has a C<converter> (see below) then the found entity will be
228             passed to the converter and the result will be returned. Otherwise, the entity
229             will be type-checked and returned.
230              
231             This means that to write a resolver, you must write a C<entity_at> method that
232             accepts an arrayref of path parts (strings) and returns an object of the type
233             indicated by the resolver's C<native_type> method (below).
234              
235             =head2 native_type
236              
237             This method should return a L<Moose::Meta::TypeConstraint> indicating the type
238             of entity that will be located by the resolver's native C<entity_at>.
239              
240             It must be provided by classes implementing the Path::Resolver::Role::Resolver
241             role.
242              
243             =head2 effective_type
244              
245             This method returns the type that the wrapped C<entity_at> method will return.
246             This means that if there is a converter (see below) it will return the
247             converter's output type. Otherwise, it will return the resolver's native type.
248              
249             =head2 converter
250              
251             The converter method (actually an attribute) may be undef or may be an object
252             that implements the
253             L<Path::Resolver::Role::Converter|Path::Resolver::Role::Converter> object.
254              
255             It will be used to convert objects from the resolver's native type to another
256             type.
257              
258             =head2 default_converter
259              
260             This method can be implemented by resolver classes to set a default converter.
261             The version provided by this role returns false.
262              
263             To see an example of this put to use, see
264             L<Path::Resolver::Role::FileResolver>.
265              
266             =head2 content_for
267              
268             my $content_ref = $resolver->content_for($path);
269              
270             This method is provided with backward compatibility with previous versions of
271             Path::Resolver. B<This method will be removed in the near future.>
272              
273             It calls C<entity_at> and then calls the C<content_ref> on the entity. If
274             the entity doesn't provide a C<content_ref> method, an exception will be
275             thrown.
276              
277             =head1 AUTHOR
278              
279             Ricardo Signes <cpan@semiotic.systems>
280              
281             =head1 COPYRIGHT AND LICENSE
282              
283             This software is copyright (c) 2022 by Ricardo Signes.
284              
285             This is free software; you can redistribute it and/or modify it under
286             the same terms as the Perl 5 programming language system itself.
287              
288             =cut