File Coverage

blib/lib/App/Fetchware/Config.pm
Criterion Covered Total %
statement 55 56 98.2
branch 25 28 89.2
condition n/a
subroutine 13 13 100.0
pod 5 5 100.0
total 98 102 96.0


line stmt bran cond sub pod time code
1             package App::Fetchware::Config;
2             our $VERSION = '1.016'; # VERSION: generated by DZP::OurPkgVersion
3             # ABSTRACT: Manages App::Fetchware's internal representation of Fetchwarefiles.
4             ###BUGALERT### Uses die instead of croak. croak is the preferred way of throwing
5             #exceptions in modules. croak says that the caller was the one who caused the
6             #error not the specific code that actually threw the error.
7 53     53   662841 use strict;
  53         66  
  53         1281  
8 53     53   163 use warnings;
  53         57  
  53         1114  
9              
10             # Enable Perl 6 knockoffs, and use 5.10.1, because smartmatching and other
11             # things in 5.10 were changed in 5.10.1+.
12 53     53   765 use 5.010001;
  53         117  
13              
14 53     53   172 use Carp 'carp';
  53         67  
  53         2727  
15 53     53   28029 use Data::Dumper;
  53         296663  
  53         3029  
16              
17              
18             # Set up Exporter to bring App::Fetchware's API to everyone who use's it
19             # including fetchware's ability to let you rip into its guts, and customize it
20             # as you need.
21 53     53   286 use Exporter qw( import );
  53         63  
  53         24433  
22             # By default fetchware exports its configuration file like subroutines and
23             # fetchware().
24             #
25              
26             # These tags go with the override() subroutine, and together allow you to
27             # replace some or all of fetchware's default behavior to install unusual
28             # software.
29             our %EXPORT_TAGS = (
30             CONFIG => [qw(
31             config
32             config_iter
33             config_replace
34             config_delete
35             __clear_CONFIG
36             debug_CONFIG
37             )],
38             );
39             # *All* entries in @EXPORT_TAGS must also be in @EXPORT_OK.
40             our @EXPORT_OK = map {@{$_}} values %EXPORT_TAGS;
41              
42              
43             # Fetchware's internal representation of your Fetchwarefile.
44             my %CONFIG;
45              
46              
47              
48             sub config {
49 5933     5933 1 29527 my ($config_sub_name, $config_sub_value) = @_;
50              
51             # Only one argument just lookup and return it.
52 5933 100       9879 if (@_ == 1) {
    50          
53             ref $CONFIG{$config_sub_name} eq 'ARRAY'
54 25         103 ? return @{$CONFIG{$config_sub_name}}
55 4985 100       47992 : return $CONFIG{$config_sub_name};
56             # More than one argument store the provided values in %CONFIG.
57             # If more than one argument then the rest will be store in an ARRAY ref.
58             } elsif (@_ > 1) {
59 948 100       1647 if (ref $CONFIG{$config_sub_name} eq 'ARRAY') {
60             # If config() is provided with more than 2 args, then the second
61             # arg ($config_sub_value) and the third to $#_ args are also
62             # added to %CONFIG.
63 5 100       11 if (@_ > 2) {
64 1         1 push @{$CONFIG{$config_sub_name}}, $config_sub_value, @_[2..$#_];
  1         3  
65             } else {
66 4         2 push @{$CONFIG{$config_sub_name}}, $config_sub_value;
  4         11  
67             }
68             } else {
69             # If there is already a value in that %CONFIG entry then turn it
70             # into an ARRAY ref.
71 943 100       1278 if (defined($CONFIG{$config_sub_name})) {
72 2 100       7 if (@_ > 2) {
73             $CONFIG{$config_sub_name}
74             =
75 1         5 [ $CONFIG{$config_sub_name}, @_[1..$#_] ];
76             } else {
77             $CONFIG{$config_sub_name}
78             =
79 1         3 [$CONFIG{$config_sub_name}, $config_sub_value];
80             }
81             } else {
82 941 100       1200 if (@_ > 2) {
83 9         32 $CONFIG{$config_sub_name} = [ @_[1..$#_] ];
84             } else {
85 932         8697 $CONFIG{$config_sub_name} = $config_sub_value;
86             }
87             }
88             }
89             }
90             }
91              
92              
93              
94             sub config_iter {
95 3     3 1 294 my $config_sub_name = shift;
96              
97 3         3 my $iterator = 0;
98              
99             # Return the "iterator." Read MJD's kick ass HOP for more info about
100             # iterators: http://hop.perl.plover.com/book/pdf/04Iterators.pdf
101             return sub {
102              
103 21 100   21   53 if (ref $CONFIG{$config_sub_name} eq 'ARRAY') {
104             # Return undef if $iterator is greater than the last element index
105             # of the array ref.
106 20 100       11 return if $iterator > $#{$CONFIG{$config_sub_name}};
  20         48  
107              
108             # Simply access whatever number the iterator is at now.
109 19         20 my $retval = $CONFIG{$config_sub_name}->[$iterator];
110              
111             # Now increment $iterator so next call will access the next element
112             # of the arrayref.
113 19         13 $iterator++;
114              
115             # Return the $retval. This is done after $iterator is incremented,
116             # so we access the current element instead of the next one.
117 19         31 return $retval;
118              
119             # If $config_sub_name is not an ARRREF, then just return whatever its
120             # one value is on the first call ($iterator == 0), and return undef for
121             # every other call.
122             } else {
123 1 50       23 if ($iterator == 0) {
124 1         1 $iterator++;
125 1         2 return config($config_sub_name);
126             } else {
127 0         0 return;
128             }
129             }
130             }
131 3         13 }
132              
133              
134              
135             sub config_replace {
136 4     4 1 2584 my ($config_sub_name, $config_sub_value) = @_;
137              
138 4 100       26 if (@_ < 2) {
    100          
    50          
139 1         5 die <
140             App::Fetchware: run-time error. config_replace() was called with only one
141             argument, but it requres two arguments. Please add the other option. Please see
142             perldoc App::Fetchware.
143             EOD
144             } elsif (@_ == 2) {
145 2         13 $CONFIG{$config_sub_name} = $config_sub_value;
146             } elsif (@_ > 2) {
147 1         5 $CONFIG{$config_sub_name} = [$config_sub_value, @_[2..$#_]];
148             }
149             }
150              
151              
152              
153             sub config_delete {
154 7     7 1 4344 my $config_sub_name = shift;
155              
156 7         40 delete $CONFIG{$config_sub_name};
157             }
158              
159              
160              
161             sub __clear_CONFIG {
162 542     542   137412 %CONFIG = ();
163             }
164              
165              
166              
167             sub debug_CONFIG {
168             ###BUGALERT### Should print be a note() to avoid polluting stdout when
169             #testing??? But I don't really want to load Test::More, when I'm not
170             #testing. So, I could move this to Test::Fetchware, but that does not have
171             #access to %CONFIG.
172 3     3 1 15 print Dumper(\%CONFIG);
173             }
174              
175              
176              
177             1;
178              
179             =pod
180              
181             =head1 NAME
182              
183             App::Fetchware::Config - Manages App::Fetchware's internal representation of Fetchwarefiles.
184              
185             =head1 VERSION
186              
187             version 1.016
188              
189             =head1 SYNOPSIS
190              
191             use App::Fetchware::Config ':CONFIG';
192              
193             my $some_config_sub_value = config('some_config_sub');
194             $config_sub_value = config($config_sub_name, $config_sub_value);
195              
196             # You can also take advantage of config('config_sub_name') returning the
197             # value if it exists or returning false if it does not to make ifs testing
198             # if the value exists or not.
199             if (config('config_sub_name')) {
200             # config_sub_name exists in %CONFIG.
201             } else {
202             # config_sub_name does not exist in %CONFIG.
203             }
204              
205             config_replace($name, $value);
206              
207             config_delete($name);
208              
209             __clear_CONFIG();
210              
211             debug_CONFIG();
212              
213             =head1 DESCRIPTION
214              
215             App::Fetchware::Config maintains an abstraction layer between fetchware and
216             fetchware's internal Fetchwarefile represenation, which is inside C<%CONFIG>
217             inside App::Fetchware::Config.
218              
219             App::Fetchware::Config gives the user a small, flexible API for manipulating
220             fetchware's internal represenation of the user's Fetchwarefile. This API allows
221             the user to get (via config()), set (via config()), replace (via
222             config_replace()), delete (via config_delete()), delete all (via
223             __clear_CONFIG()), and even debug (via debug_CONFIG()) the internal
224             representation of the users Fetchwarefile.
225              
226             =over
227              
228             =item NOTICE
229             App::Fetchware::Config's represenation of your Fetchwarefile is per process. If
230             you parse a new Fetchwarefile it will conflict with the existing C<%CONFIG>, and
231             various exceptions may be thrown.
232              
233             C<%CONFIG> is a B per process variable! You B try to maniuplate
234             more than one Fetchwarefile in memory at one time! It will not work! You can
235             however use __clear_CONFIG() to clear the global %CONFIG, so that you can use it
236             again. This is mostly just done in fetchware's test suite, so this design
237             limitation is not such a big deal.
238              
239             =back
240              
241             =head1 CONFIG SUBROUTINES
242              
243             =head2 config()
244              
245             $config_sub_value = config($config_sub_name, $config_sub_value);
246              
247             config() stores all of the configuration options that are parsed (actually
248             executed) in your Fetchwarefile. They are stored in the %CONFIG variable that is
249             lexically only shared with the private __clear_CONFIG() subroutine, which when
250             executed simply clears %CONFIG for the next run of App::Fetchware in
251             bin/fetchware's upgrade_all() subroutine, which is the only place multiple
252             Fetchwarefiles may be parsed in on execution of bin/fetchware.
253              
254             If config() is given more than 2 args, then the second arg, and all of the other
255             arguments are stored in %CONFIG as an C ref. Also storing a second
256             argument where there was a previously defined() argument will cause that
257             element of %CONFIG to be promoted to being an C ref.
258              
259             =head2 config_iter()
260              
261             # Create a config "iterator."
262             my $mirror_iter = config_iter('mirror');
263              
264             # Use the iterator to return a new value of 'mirror' each time it is kicked,
265             # called.
266             my $mirror
267             while (defined($mirror = $mirror_iter->())) {
268             # Do something with this version of $mirror
269             # Next iteration will "kick" the iterator again
270             }
271              
272             config_iter() returns an iterator. An iterator is simply a subroutine reference
273             that when called (ex: C<$mirror_iter-E()>) will return the next value. And
274             the coolest part is that the iterator will keep track of where it is in the list
275             of values that configuration option has itself, so you don't have to yourself.
276              
277             Iterators returned from config_iter() will return one or more elements of the
278             configuration option that you specify has stored. After you exceed the length of
279             the internal array reference the iterator will return false (undef).
280              
281             =head2 config_replace()
282              
283             config_replace($name, $value);
284              
285             # Supports multiple values and arrays too.
286             config_replace($name, $val1, $val2, $val3);
287             config_replace($name, @values);
288              
289             Allows you to replace the $value of the specified ($name) existing element of
290             the %CONFIG internal hash. It supports multiple values and arrays, and will
291             store those multiple values or arrays with an arrayref.
292              
293             =head2 config_delete()
294              
295             config_delete($name);
296              
297             delete's $name from %CONFIG.
298              
299             =head2 __clear_CONFIG()
300              
301             __clear_CONFIG();
302              
303             Clears the %CONFIG globalish variable. Meant more for use in testing, then for
304             use in Fetchware itself, or in Fetchware extensions.
305              
306             =head2 debug_CONFIG()
307              
308             debug_CONFIG();
309              
310             Data::Dumper::Dumper()'s %CONFIG and prints it.
311              
312             =head1 ERRORS
313              
314             As with the rest of App::Fetchware, App::Fetchware::Config does not return any
315             error codes; instead, all errors are die()'d if it's App::Fetchware::Config's
316             error, or croak()'d if its the caller's fault.
317              
318             =head1 BUGS
319              
320             App::Fetchware::Config's represenation of your Fetchwarefile is per process. If
321             you parse a new Fetchwarefile it will conflict with the existing C<%CONFIG>, and
322             various exceptions may be thrown.
323              
324             C<%CONFIG> is a B per process variable! You B try to maniuplate
325             more than one Fetchwarefile in memory at one time! It will not work! You can
326             however use __clear_CONFIG() to clear the global %CONFIG, so that you can use it
327             again. This is mostly just done in fetchware's test suite, so this design
328             limitation is not such a big deal.
329              
330             =head1 AUTHOR
331              
332             David Yingling
333              
334             =head1 COPYRIGHT AND LICENSE
335              
336             This software is copyright (c) 2016 by David Yingling.
337              
338             This is free software; you can redistribute it and/or modify it under
339             the same terms as the Perl 5 programming language system itself.
340              
341             =cut
342              
343             __END__