File Coverage

blib/lib/Config/MVP/Section.pm
Criterion Covered Total %
statement 45 46 97.8
branch 14 20 70.0
condition 2 2 100.0
subroutine 11 11 100.0
pod 4 5 80.0
total 76 84 90.4


line stmt bran cond sub pod time code
1             package Config::MVP::Section 2.200013;
2             # ABSTRACT: one section of an MVP configuration sequence
3              
4 4     4   28 use Moose 0.91;
  4         76  
  4         27  
5              
6 4     4   25027 use Class::Load 0.17 ();
  4         85  
  4         89  
7 4     4   21 use Config::MVP::Error;
  4         10  
  4         3492  
8              
9             #pod =head1 DESCRIPTION
10             #pod
11             #pod For the most part, you can just consult L<Config::MVP> to understand what this
12             #pod class is and how it's used.
13             #pod
14             #pod =attr name
15             #pod
16             #pod This is the section's name. It's a string, and it must be provided.
17             #pod
18             #pod =cut
19              
20             has name => (
21             is => 'ro',
22             isa => 'Str',
23             required => 1
24             );
25              
26             #pod =attr package
27             #pod
28             #pod This is the (Perl) package with which the section is associated. It is
29             #pod optional. When the section is instantiated, it will ensure that this package
30             #pod is loaded.
31             #pod
32             #pod =cut
33              
34             has package => (
35             is => 'ro',
36             isa => 'Str', # should be class-like string, but can't be ClassName
37             required => 0,
38             predicate => 'has_package',
39             );
40              
41             #pod =attr multivalue_args
42             #pod
43             #pod This attribute is an arrayref of value names that should be considered
44             #pod multivalue properties in the section. When added to the section, they will
45             #pod always be wrapped in an arrayref, and they may be added to the section more
46             #pod than once.
47             #pod
48             #pod If this attribute is not given during construction, it will default to the
49             #pod result of calling section's package's C<mvp_multivalue_args> method. If the
50             #pod section has no associated package or if the package doesn't provide that
51             #pod method, it default to an empty arrayref.
52             #pod
53             #pod =cut
54              
55             has multivalue_args => (
56             is => 'ro',
57             isa => 'ArrayRef',
58             lazy => 1,
59             default => sub {
60             my ($self) = @_;
61              
62             return []
63             unless $self->has_package and $self->package->can('mvp_multivalue_args');
64              
65             return [ $self->package->mvp_multivalue_args ];
66             },
67             );
68              
69             #pod =attr aliases
70             #pod
71             #pod This attribute is a hashref of name remappings. For example, if it contains
72             #pod this hashref:
73             #pod
74             #pod {
75             #pod file => 'files',
76             #pod path => 'files',
77             #pod }
78             #pod
79             #pod Then attempting to set either the "file" or "path" setting for the section
80             #pod would actually set the "files" setting.
81             #pod
82             #pod If this attribute is not given during construction, it will default to the
83             #pod result of calling section's package's C<mvp_aliases> method. If the
84             #pod section has no associated package or if the package doesn't provide that
85             #pod method, it default to an empty hashref.
86             #pod
87             #pod =cut
88              
89             has aliases => (
90             is => 'ro',
91             isa => 'HashRef',
92             lazy => 1,
93             default => sub {
94             my ($self) = @_;
95              
96             return {} unless $self->has_package and $self->package->can('mvp_aliases');
97              
98             return $self->package->mvp_aliases;
99             },
100             );
101              
102             #pod =attr payload
103             #pod
104             #pod This is the storage into which properties are set. It is a hashref of names
105             #pod and values. You should probably not alter the contents of the payload, and
106             #pod should read its contents only.
107             #pod
108             #pod =cut
109              
110             has payload => (
111             is => 'ro',
112             isa => 'HashRef',
113             init_arg => undef,
114             default => sub { {} },
115             );
116              
117             #pod =attr is_finalized
118             #pod
119             #pod This attribute is true if the section has been marked finalized, which will
120             #pod prevent any new values from being added to it. It can be set with the
121             #pod C<finalize> method.
122             #pod
123             #pod =cut
124              
125             has is_finalized => (
126             is => 'ro',
127             isa => 'Bool',
128             traits => [ 'Bool' ],
129             init_arg => undef,
130             default => 0,
131             handles => { finalize => 'set' },
132             );
133              
134             before finalize => sub {
135             my ($self) = @_;
136              
137             Config::MVP::Error->throw("can't finalize unsequenced Config::MVP::Section")
138             unless $self->sequence;
139             };
140              
141             #pod =attr sequence
142             #pod
143             #pod This attributes points to the sequence into which the section has been
144             #pod assembled. It may be unset if the section has been created but not yet placed
145             #pod in a sequence.
146             #pod
147             #pod =cut
148              
149             has sequence => (
150             is => 'ro',
151             isa => 'Config::MVP::Sequence',
152             weak_ref => 1,
153             predicate => '_sequence_has_been_set',
154             reader => '_sequence',
155             writer => '__set_sequence',
156             clearer => '_clear_sequence',
157             );
158              
159             sub _set_sequence {
160 31     31   55 my ($self, $seq) = @_;
161              
162 31 50       69 Config::MVP::Error->throw("Config::MVP::Section cannot be resequenced")
163             if $self->sequence;
164              
165 31         925 $self->__set_sequence($seq);
166             }
167              
168             sub sequence {
169 65     65 1 126 my ($self) = @_;
170 65 100       1922 return undef unless $self->_sequence_has_been_set;
171 34         869 my $seq = $self->_sequence;
172              
173 34 50       74 Config::MVP::Error->throw("can't access section's destroyed sequence")
174             unless defined $seq;
175              
176 34         101 return $seq;
177             }
178              
179             #pod =method add_value
180             #pod
181             #pod $section->add_value( $name => $value );
182             #pod
183             #pod This method sets the value for the named property to the given value. If the
184             #pod property is a multivalue property, the new value will be pushed onto the end of
185             #pod an arrayref that will store all values for that property.
186             #pod
187             #pod Attempting to add a value for a non-multivalue property whose value was already
188             #pod added will result in an exception.
189             #pod
190             #pod =cut
191              
192             sub add_value {
193 58     58 1 112 my ($self, $name, $value) = @_;
194              
195 58 50       1336 confess "can't add values to finalized section " . $self->name
196             if $self->is_finalized;
197              
198 58         1213 my $alias = $self->aliases->{ $name };
199 58 50       114 $name = $alias if defined $alias;
200              
201 58         1239 my $mva = $self->multivalue_args;
202              
203 58 100       110 if (grep { $_ eq $name } @$mva) {
  36         136  
204 22   100     470 my $array = $self->payload->{$name} ||= [];
205 22         41 push @$array, $value;
206 22         59 return;
207             }
208              
209 36 50       767 if (exists $self->payload->{$name}) {
210 0         0 Carp::croak "multiple values given for property $name in section "
211             . $self->name;
212             }
213              
214 36         745 $self->payload->{$name} = $value;
215             }
216              
217             #pod =method load_package
218             #pod
219             #pod $section->load_package($package, $section_name);
220             #pod
221             #pod This method is used to ensure that the given C<$package> is loaded, and is
222             #pod called whenever a section with a package is created. By default, it delegates
223             #pod to L<Class::Load>. If the package can't be found, it calls the
224             #pod L<missing_package> method. Errors in compilation are not suppressed.
225             #pod
226             #pod =cut
227              
228             sub load_package {
229 32     32 1 63 my ($self, $package, $section_name) = @_;
230              
231 32 100       106 Class::Load::load_optional_class($package)
232             or $self->missing_package($package, $section_name);
233             }
234              
235             #pod =method missing_package
236             #pod
237             #pod $section->missing_package($package, $section_name);
238             #pod
239             #pod This method is called when C<load_package> encounters a package that is not
240             #pod installed. By default, it throws an exception.
241             #pod
242             #pod =cut
243              
244             sub missing_package {
245 1     1 1 470 my ($self, $package, $section_name) = @_ ;
246              
247 1         14 my $class = Moose::Meta::Class->create_anon_class(
248             superclasses => [ 'Config::MVP::Error' ],
249             cached => 1,
250             attributes => [
251             Moose::Meta::Attribute->new(package => (
252             is => 'ro',
253             required => 1,
254             )),
255             Moose::Meta::Attribute->new(section_name => (
256             is => 'ro',
257             required => 1,
258             )),
259             ],
260             );
261              
262 1         8163 $class->name->throw({
263             ident => 'package not installed',
264             message => "$package (for section $section_name) does not appear to be installed",
265             package => $package,
266             section_name => $section_name,
267             });
268             }
269              
270             sub _BUILD_package_settings {
271 33     33   53 my ($self) = @_;
272              
273 33 100       1163 return unless defined (my $pkg = $self->package);
274              
275 32 50       110 confess "illegal package name $pkg" unless Params::Util::_CLASS($pkg);
276              
277 32         981 $self->load_package($pkg, $self->name);
278              
279             # We call these accessors for lazy attrs to ensure they're initialized from
280             # defaults if needed. Crash early! -- rjbs, 2009-08-09
281 30         7835 $self->multivalue_args;
282 30         674 $self->aliases;
283             }
284              
285             sub BUILD {
286 33     33 0 40525 my ($self) = @_;
287 33         90 $self->_BUILD_package_settings;
288             }
289              
290 4     4   29 no Moose;
  4         9  
  4         21  
291             1;
292              
293             __END__
294              
295             =pod
296              
297             =encoding UTF-8
298              
299             =head1 NAME
300              
301             Config::MVP::Section - one section of an MVP configuration sequence
302              
303             =head1 VERSION
304              
305             version 2.200013
306              
307             =head1 DESCRIPTION
308              
309             For the most part, you can just consult L<Config::MVP> to understand what this
310             class is and how it's used.
311              
312             =head1 PERL VERSION
313              
314             This module should work on any version of perl still receiving updates from
315             the Perl 5 Porters. This means it should work on any version of perl released
316             in the last two to three years. (That is, if the most recently released
317             version is v5.40, then this module should work on both v5.40 and v5.38.)
318              
319             Although it may work on older versions of perl, no guarantee is made that the
320             minimum required version will not be increased. The version may be increased
321             for any reason, and there is no promise that patches will be accepted to lower
322             the minimum required perl.
323              
324             =head1 ATTRIBUTES
325              
326             =head2 name
327              
328             This is the section's name. It's a string, and it must be provided.
329              
330             =head2 package
331              
332             This is the (Perl) package with which the section is associated. It is
333             optional. When the section is instantiated, it will ensure that this package
334             is loaded.
335              
336             =head2 multivalue_args
337              
338             This attribute is an arrayref of value names that should be considered
339             multivalue properties in the section. When added to the section, they will
340             always be wrapped in an arrayref, and they may be added to the section more
341             than once.
342              
343             If this attribute is not given during construction, it will default to the
344             result of calling section's package's C<mvp_multivalue_args> method. If the
345             section has no associated package or if the package doesn't provide that
346             method, it default to an empty arrayref.
347              
348             =head2 aliases
349              
350             This attribute is a hashref of name remappings. For example, if it contains
351             this hashref:
352              
353             {
354             file => 'files',
355             path => 'files',
356             }
357              
358             Then attempting to set either the "file" or "path" setting for the section
359             would actually set the "files" setting.
360              
361             If this attribute is not given during construction, it will default to the
362             result of calling section's package's C<mvp_aliases> method. If the
363             section has no associated package or if the package doesn't provide that
364             method, it default to an empty hashref.
365              
366             =head2 payload
367              
368             This is the storage into which properties are set. It is a hashref of names
369             and values. You should probably not alter the contents of the payload, and
370             should read its contents only.
371              
372             =head2 is_finalized
373              
374             This attribute is true if the section has been marked finalized, which will
375             prevent any new values from being added to it. It can be set with the
376             C<finalize> method.
377              
378             =head2 sequence
379              
380             This attributes points to the sequence into which the section has been
381             assembled. It may be unset if the section has been created but not yet placed
382             in a sequence.
383              
384             =head1 METHODS
385              
386             =head2 add_value
387              
388             $section->add_value( $name => $value );
389              
390             This method sets the value for the named property to the given value. If the
391             property is a multivalue property, the new value will be pushed onto the end of
392             an arrayref that will store all values for that property.
393              
394             Attempting to add a value for a non-multivalue property whose value was already
395             added will result in an exception.
396              
397             =head2 load_package
398              
399             $section->load_package($package, $section_name);
400              
401             This method is used to ensure that the given C<$package> is loaded, and is
402             called whenever a section with a package is created. By default, it delegates
403             to L<Class::Load>. If the package can't be found, it calls the
404             L<missing_package> method. Errors in compilation are not suppressed.
405              
406             =head2 missing_package
407              
408             $section->missing_package($package, $section_name);
409              
410             This method is called when C<load_package> encounters a package that is not
411             installed. By default, it throws an exception.
412              
413             =head1 AUTHOR
414              
415             Ricardo Signes <cpan@semiotic.systems>
416              
417             =head1 COPYRIGHT AND LICENSE
418              
419             This software is copyright (c) 2022 by Ricardo Signes.
420              
421             This is free software; you can redistribute it and/or modify it under
422             the same terms as the Perl 5 programming language system itself.
423              
424             =cut