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             # ABSTRACT: one section of an MVP configuration sequence
3             $Config::MVP::Section::VERSION = '2.200012';
4 4     4   32 use Moose 0.91;
  4         69  
  4         68  
5              
6 4     4   26629 use Class::Load 0.17 ();
  4         83  
  4         100  
7 4     4   34 use Config::MVP::Error;
  4         9  
  4         3804  
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   69 my ($self, $seq) = @_;
161              
162 31 50       106 Config::MVP::Error->throw("Config::MVP::Section cannot be resequenced")
163             if $self->sequence;
164              
165 31         946 $self->__set_sequence($seq);
166             }
167              
168             sub sequence {
169 65     65 1 165 my ($self) = @_;
170 65 100       2102 return undef unless $self->_sequence_has_been_set;
171 34         1061 my $seq = $self->_sequence;
172              
173 34 50       95 Config::MVP::Error->throw("can't access section's destroyed sequence")
174             unless defined $seq;
175              
176 34         115 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 138 my ($self, $name, $value) = @_;
194              
195 58 50       1636 confess "can't add values to finalized section " . $self->name
196             if $self->is_finalized;
197              
198 58         1477 my $alias = $self->aliases->{ $name };
199 58 50       120 $name = $alias if defined $alias;
200              
201 58         1543 my $mva = $self->multivalue_args;
202              
203 58 100       134 if (grep { $_ eq $name } @$mva) {
  36         116  
204 22   100     568 my $array = $self->payload->{$name} ||= [];
205 22         51 push @$array, $value;
206 22         73 return;
207             }
208              
209 36 50       907 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         875 $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 73 my ($self, $package, $section_name) = @_;
230              
231 32 100       120 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 489 my ($self, $package, $section_name) = @_ ;
246              
247 1         13 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         8390 $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   62 my ($self) = @_;
272              
273 33 100       1025 return unless defined (my $pkg = $self->package);
274              
275 32 50       143 confess "illegal package name $pkg" unless Params::Util::_CLASS($pkg);
276              
277 32         1138 $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         9701 $self->multivalue_args;
282 30         771 $self->aliases;
283             }
284              
285             sub BUILD {
286 33     33 0 48998 my ($self) = @_;
287 33         95 $self->_BUILD_package_settings;
288             }
289              
290 4     4   31 no Moose;
  4         16  
  4         38  
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.200012
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 ATTRIBUTES
313              
314             =head2 name
315              
316             This is the section's name. It's a string, and it must be provided.
317              
318             =head2 package
319              
320             This is the (Perl) package with which the section is associated. It is
321             optional. When the section is instantiated, it will ensure that this package
322             is loaded.
323              
324             =head2 multivalue_args
325              
326             This attribute is an arrayref of value names that should be considered
327             multivalue properties in the section. When added to the section, they will
328             always be wrapped in an arrayref, and they may be added to the section more
329             than once.
330              
331             If this attribute is not given during construction, it will default to the
332             result of calling section's package's C<mvp_multivalue_args> method. If the
333             section has no associated package or if the package doesn't provide that
334             method, it default to an empty arrayref.
335              
336             =head2 aliases
337              
338             This attribute is a hashref of name remappings. For example, if it contains
339             this hashref:
340              
341             {
342             file => 'files',
343             path => 'files',
344             }
345              
346             Then attempting to set either the "file" or "path" setting for the section
347             would actually set the "files" setting.
348              
349             If this attribute is not given during construction, it will default to the
350             result of calling section's package's C<mvp_aliases> method. If the
351             section has no associated package or if the package doesn't provide that
352             method, it default to an empty hashref.
353              
354             =head2 payload
355              
356             This is the storage into which properties are set. It is a hashref of names
357             and values. You should probably not alter the contents of the payload, and
358             should read its contents only.
359              
360             =head2 is_finalized
361              
362             This attribute is true if the section has been marked finalized, which will
363             prevent any new values from being added to it. It can be set with the
364             C<finalize> method.
365              
366             =head2 sequence
367              
368             This attributes points to the sequence into which the section has been
369             assembled. It may be unset if the section has been created but not yet placed
370             in a sequence.
371              
372             =head1 METHODS
373              
374             =head2 add_value
375              
376             $section->add_value( $name => $value );
377              
378             This method sets the value for the named property to the given value. If the
379             property is a multivalue property, the new value will be pushed onto the end of
380             an arrayref that will store all values for that property.
381              
382             Attempting to add a value for a non-multivalue property whose value was already
383             added will result in an exception.
384              
385             =head2 load_package
386              
387             $section->load_package($package, $section_name);
388              
389             This method is used to ensure that the given C<$package> is loaded, and is
390             called whenever a section with a package is created. By default, it delegates
391             to L<Class::Load>. If the package can't be found, it calls the
392             L<missing_package> method. Errors in compilation are not suppressed.
393              
394             =head2 missing_package
395              
396             $section->missing_package($package, $section_name);
397              
398             This method is called when C<load_package> encounters a package that is not
399             installed. By default, it throws an exception.
400              
401             =head1 AUTHOR
402              
403             Ricardo Signes <rjbs@cpan.org>
404              
405             =head1 COPYRIGHT AND LICENSE
406              
407             This software is copyright (c) 2021 by Ricardo Signes.
408              
409             This is free software; you can redistribute it and/or modify it under
410             the same terms as the Perl 5 programming language system itself.
411              
412             =cut