File Coverage

blib/lib/Config/MVP/Assembler.pm
Criterion Covered Total %
statement 38 38 100.0
branch 13 18 72.2
condition 2 3 66.6
subroutine 11 11 100.0
pod 6 6 100.0
total 70 76 92.1


line stmt bran cond sub pod time code
1             package Config::MVP::Assembler 2.200013;
2             # ABSTRACT: multivalue-property config-loading state machine
3              
4 4     4   3134 use Moose;
  4         465303  
  4         30  
5              
6 4     4   29731 use Config::MVP::Error;
  4         15  
  4         160  
7 4     4   2056 use Config::MVP::Sequence;
  4         16  
  4         158  
8 4     4   35 use Config::MVP::Section;
  4         9  
  4         2037  
9              
10             #pod =head1 DESCRIPTION
11             #pod
12             #pod First, you should probably read the L<example of using
13             #pod Config::MVP|Config::MVP/EXAMPLE>. If you already know how it works, keep
14             #pod going.
15             #pod
16             #pod Config::MVP::Assembler is a helper for constructing a Config::MVP::Sequence
17             #pod object. It's a very simple state machine that lets you signal what kind of
18             #pod events you've encountered while reading configuration.
19             #pod
20             #pod =head1 TYPICAL USE
21             #pod
22             #pod my $assembler = Config::MVP::Assembler->new;
23             #pod
24             #pod # Maybe you want a starting section:
25             #pod my $starting_section = $assembler->section_class->new({ name => '_' });
26             #pod $assembler->sequence->add_section($section_starting);
27             #pod
28             #pod # We'll add some values, which will go to the starting section:
29             #pod $assembler->add_value(x => 10);
30             #pod $assembler->add_value(y => 20);
31             #pod
32             #pod # Change to a new section...
33             #pod $assembler->change_section($moniker);
34             #pod
35             #pod # ...and add values to that section.
36             #pod $assembler->add_value(x => 100);
37             #pod $assembler->add_value(y => 200);
38             #pod
39             #pod The code above creates an assembler and populates it step by step. In the end,
40             #pod to get values, you could do something like this:
41             #pod
42             #pod my @output;
43             #pod
44             #pod for my $section ($assembler->sequence->sections) {
45             #pod push @output, [ $section->name, $section->package, $section->payload ];
46             #pod }
47             #pod
48             #pod When changing sections, the given section "moniker" is used for the new section
49             #pod name. The result of passing that moniker to the assembler's
50             #pod C<L</expand_package>> method is used as the section's package name. (By
51             #pod default, this method does nothing.) The new section's C<multivalue_args> and
52             #pod C<aliases> are determined by calling the C<mvp_multivalue_args> and
53             #pod C<mvp_aliases> methods on the package.
54             #pod
55             #pod =attr sequence_class
56             #pod
57             #pod This attribute stores the name of the class to be used for the assembler's
58             #pod sequence. It defaults to Config::MVP::Sequence.
59             #pod
60             #pod =cut
61              
62             has sequence_class => (
63             is => 'ro',
64             isa => 'ClassName',
65             lazy => 1,
66             default => 'Config::MVP::Sequence',
67             );
68              
69             #pod =attr section_class
70             #pod
71             #pod This attribute stores the name of the class to be used for sections created by
72             #pod the assembler. It defaults to Config::MVP::Section.
73             #pod
74             #pod =cut
75              
76             has section_class => (
77             is => 'ro',
78             isa => 'ClassName',
79             lazy => 1,
80             default => 'Config::MVP::Section',
81             );
82              
83             #pod =attr sequence
84             #pod
85             #pod This is the sequence that the assembler is assembling. It defaults to a new
86             #pod instance of the assembler's C<sequence_class>.
87             #pod
88             #pod =cut
89              
90             has sequence => (
91             is => 'ro',
92             isa => 'Config::MVP::Sequence',
93             default => sub { $_[0]->sequence_class->new({ assembler => $_[0] }) },
94             init_arg => undef,
95             handles => [ qw(is_finalized finalize) ],
96             );
97              
98             before finalize => sub {
99             my ($self) = @_;
100              
101             $self->end_section if $self->current_section;
102             };
103              
104             #pod =method begin_section
105             #pod
106             #pod $assembler->begin_section($package_moniker, $name);
107             #pod
108             #pod $assembler->begin_section($package_moniker);
109             #pod
110             #pod $assembler->begin_section( \$package );
111             #pod
112             #pod This method tells the assembler that it should begin work on a new section with
113             #pod the given identifier. If it is already working on a section, an error will be
114             #pod raised. See C<L</change_section>> for a method to begin a new section, ending
115             #pod the current one if needed.
116             #pod
117             #pod The package moniker is expanded by the C<L</expand_package>> method. The name,
118             #pod if not given, defaults to the package moniker. These data are used to create a
119             #pod new section and the section is added to the end of the sequence. If the
120             #pod package argument is a reference, it is used as the literal value for the
121             #pod package, and no expansion is performed. If it is a reference to undef, a
122             #pod section with no package is created.
123             #pod
124             #pod =cut
125              
126             has _between_sections => (
127             is => 'rw',
128             isa => 'Bool',
129             default => 0,
130             );
131              
132             sub begin_section {
133 18     18 1 2146 my ($self, $package_moniker, $name) = @_;
134              
135 18 50       73 Config::MVP::Error->throw("can't begin a new section while a section is open")
136             if $self->current_section;
137              
138 18 100 66     82 $name = $package_moniker unless defined $name and length $name;
139              
140 18 50       88 my $package = ref($package_moniker)
141             ? $$package_moniker
142             : $self->expand_package($package_moniker);
143              
144 18 50       439 my $section = $self->section_class->new({
145             name => $name,
146             (defined $package ? (package => $package) : ()),
147             });
148              
149 16         537 $self->_between_sections(0);
150 16         388 $self->sequence->add_section($section);
151             }
152              
153             #pod =method end_section
154             #pod
155             #pod $assembler->end_section;
156             #pod
157             #pod This ends the current section. If there is no current section, an exception is
158             #pod raised.
159             #pod
160             #pod =cut
161              
162             sub end_section {
163 16     16 1 955 my ($self) = @_;
164              
165 16 50       34 Config::MVP::Error->throw("can't end a section when no section is active")
166             unless $self->current_section;
167              
168 16         41 $self->current_section->finalize;
169              
170 16         433 $self->_between_sections(1);
171             }
172              
173             #pod =method change_section
174             #pod
175             #pod $assembler->change_section($package_moniker, $name);
176             #pod
177             #pod $assembler->change_section($package_moniker);
178             #pod
179             #pod This method calls C<begin_section>, first calling C<end_section> if needed.
180             #pod
181             #pod =cut
182              
183             sub change_section {
184 11     11 1 6197 my $self = shift;
185              
186 11 100       31 $self->end_section if $self->current_section;
187 11         53 $self->begin_section(@_);
188             }
189              
190             #pod =method add_value
191             #pod
192             #pod $assembler->add_value( $name => $value );
193             #pod
194             #pod This method tells the assembler that it has encountered a named value and
195             #pod should add it to the current section. If there is no current section, an
196             #pod exception is raised. (If this is not the first time we've seen the name in the
197             #pod section and it's not a multivalue property, the section class will raise an
198             #pod exception on its own.)
199             #pod
200             #pod =cut
201              
202             sub add_value {
203 36     36 1 280 my ($self, $name, $value) = @_;
204              
205 36 50       86 Config::MVP::Error->throw("can't set value when no section is active")
206             unless my $section = $self->current_section;
207              
208 36         91 $section->add_value($name => $value);
209             }
210              
211             #pod =method expand_package
212             #pod
213             #pod This method is passed a short identifier for a package and is expected to
214             #pod return the full name of the module to load and package to interrogate. By
215             #pod default it simply returns the name it was passed, meaning that package names
216             #pod must be given whole to the C<change_section> method.
217             #pod
218             #pod =cut
219              
220 18     18 1 37 sub expand_package { $_[1] }
221              
222             #pod =method current_section
223             #pod
224             #pod This returns the section object onto which the assembler is currently adding
225             #pod values. If no section has yet been created, this method will return false.
226             #pod
227             #pod =cut
228              
229             sub current_section {
230 99     99 1 151 my ($self) = @_;
231              
232 99 100       2619 return if $self->_between_sections;
233 87         2018 my (@sections) = $self->sequence->sections;
234 87 100       2512 return $sections[ -1 ] if @sections;
235              
236 10         28 return;
237             }
238              
239 4     4   32 no Moose;
  4         10  
  4         26  
240             1;
241              
242             __END__
243              
244             =pod
245              
246             =encoding UTF-8
247              
248             =head1 NAME
249              
250             Config::MVP::Assembler - multivalue-property config-loading state machine
251              
252             =head1 VERSION
253              
254             version 2.200013
255              
256             =head1 DESCRIPTION
257              
258             First, you should probably read the L<example of using
259             Config::MVP|Config::MVP/EXAMPLE>. If you already know how it works, keep
260             going.
261              
262             Config::MVP::Assembler is a helper for constructing a Config::MVP::Sequence
263             object. It's a very simple state machine that lets you signal what kind of
264             events you've encountered while reading configuration.
265              
266             =head1 PERL VERSION
267              
268             This module should work on any version of perl still receiving updates from
269             the Perl 5 Porters. This means it should work on any version of perl released
270             in the last two to three years. (That is, if the most recently released
271             version is v5.40, then this module should work on both v5.40 and v5.38.)
272              
273             Although it may work on older versions of perl, no guarantee is made that the
274             minimum required version will not be increased. The version may be increased
275             for any reason, and there is no promise that patches will be accepted to lower
276             the minimum required perl.
277              
278             =head1 ATTRIBUTES
279              
280             =head2 sequence_class
281              
282             This attribute stores the name of the class to be used for the assembler's
283             sequence. It defaults to Config::MVP::Sequence.
284              
285             =head2 section_class
286              
287             This attribute stores the name of the class to be used for sections created by
288             the assembler. It defaults to Config::MVP::Section.
289              
290             =head2 sequence
291              
292             This is the sequence that the assembler is assembling. It defaults to a new
293             instance of the assembler's C<sequence_class>.
294              
295             =head1 METHODS
296              
297             =head2 begin_section
298              
299             $assembler->begin_section($package_moniker, $name);
300              
301             $assembler->begin_section($package_moniker);
302              
303             $assembler->begin_section( \$package );
304              
305             This method tells the assembler that it should begin work on a new section with
306             the given identifier. If it is already working on a section, an error will be
307             raised. See C<L</change_section>> for a method to begin a new section, ending
308             the current one if needed.
309              
310             The package moniker is expanded by the C<L</expand_package>> method. The name,
311             if not given, defaults to the package moniker. These data are used to create a
312             new section and the section is added to the end of the sequence. If the
313             package argument is a reference, it is used as the literal value for the
314             package, and no expansion is performed. If it is a reference to undef, a
315             section with no package is created.
316              
317             =head2 end_section
318              
319             $assembler->end_section;
320              
321             This ends the current section. If there is no current section, an exception is
322             raised.
323              
324             =head2 change_section
325              
326             $assembler->change_section($package_moniker, $name);
327              
328             $assembler->change_section($package_moniker);
329              
330             This method calls C<begin_section>, first calling C<end_section> if needed.
331              
332             =head2 add_value
333              
334             $assembler->add_value( $name => $value );
335              
336             This method tells the assembler that it has encountered a named value and
337             should add it to the current section. If there is no current section, an
338             exception is raised. (If this is not the first time we've seen the name in the
339             section and it's not a multivalue property, the section class will raise an
340             exception on its own.)
341              
342             =head2 expand_package
343              
344             This method is passed a short identifier for a package and is expected to
345             return the full name of the module to load and package to interrogate. By
346             default it simply returns the name it was passed, meaning that package names
347             must be given whole to the C<change_section> method.
348              
349             =head2 current_section
350              
351             This returns the section object onto which the assembler is currently adding
352             values. If no section has yet been created, this method will return false.
353              
354             =head1 TYPICAL USE
355              
356             my $assembler = Config::MVP::Assembler->new;
357              
358             # Maybe you want a starting section:
359             my $starting_section = $assembler->section_class->new({ name => '_' });
360             $assembler->sequence->add_section($section_starting);
361              
362             # We'll add some values, which will go to the starting section:
363             $assembler->add_value(x => 10);
364             $assembler->add_value(y => 20);
365              
366             # Change to a new section...
367             $assembler->change_section($moniker);
368              
369             # ...and add values to that section.
370             $assembler->add_value(x => 100);
371             $assembler->add_value(y => 200);
372              
373             The code above creates an assembler and populates it step by step. In the end,
374             to get values, you could do something like this:
375              
376             my @output;
377              
378             for my $section ($assembler->sequence->sections) {
379             push @output, [ $section->name, $section->package, $section->payload ];
380             }
381              
382             When changing sections, the given section "moniker" is used for the new section
383             name. The result of passing that moniker to the assembler's
384             C<L</expand_package>> method is used as the section's package name. (By
385             default, this method does nothing.) The new section's C<multivalue_args> and
386             C<aliases> are determined by calling the C<mvp_multivalue_args> and
387             C<mvp_aliases> methods on the package.
388              
389             =head1 AUTHOR
390              
391             Ricardo Signes <cpan@semiotic.systems>
392              
393             =head1 COPYRIGHT AND LICENSE
394              
395             This software is copyright (c) 2022 by Ricardo Signes.
396              
397             This is free software; you can redistribute it and/or modify it under
398             the same terms as the Perl 5 programming language system itself.
399              
400             =cut