File Coverage

blib/lib/App/Framework/Base.pm
Criterion Covered Total %
statement 127 138 92.0
branch 87 102 85.2
condition 10 12 83.3
subroutine 8 8 100.0
pod 3 3 100.0
total 235 263 89.3


line stmt bran cond sub pod time code
1             package App::Framework::Base ;
2              
3             =head1 NAME
4              
5             App::Framework::Base - Application feature
6              
7             =head1 SYNOPSIS
8              
9             use App::Framework::Base ;
10              
11              
12             =head1 DESCRIPTION
13              
14             Base object for all application objects (core/extensions/features etc)
15              
16             B
17              
18             =cut
19              
20 29     29   150 use strict ;
  29         51  
  29         899  
21 29     29   157 use Carp ;
  29         46  
  29         2060  
22              
23             our $VERSION = "1.100" ;
24              
25             #============================================================================================
26             # USES
27             #============================================================================================
28 29     29   21064 use App::Framework::Base::Object::ErrorHandle ;
  29         86  
  29         51550  
29              
30             #============================================================================================
31             # OBJECT HIERARCHY
32             #============================================================================================
33             our @ISA = qw(App::Framework::Base::Object::ErrorHandle) ;
34              
35             #============================================================================================
36             # GLOBALS
37             #============================================================================================
38              
39             our $PRIORITY_CORE = 10 ;
40             our $PRIORITY_SYSTEM = 100 ;
41             our $PRIORITY_USER = 1000 ;
42             our $PRIORITY_DEFAULT = 32767 ;
43              
44             our $class_debug = 0 ;
45              
46             =head2 FIELDS
47              
48             The following fields should be defined either in the call to 'new()', as part of a 'set()' call, or called by their accessor method
49             (which is the same name as the field):
50              
51              
52             =over 4
53              
54             =item B - list of required modules
55              
56             ARRAY ref of a list of module names that are required to be loaded by this object.
57              
58             =item B - list of which modules have been loaded
59              
60             HASH containing the modules loaded (used as key), with the value set to 1 if the module loaded ok; 0 otherwise
61              
62             =item B - all required modules are ok
63              
64             Flag that is set if all required modules loaded correctly
65              
66             =back
67              
68             =cut
69              
70             my %FIELDS = (
71             'priority' => $PRIORITY_DEFAULT,
72             'requires' => [],
73            
74             'loaded' => {}, # list of which modules have been loaded
75             'requires_ok' => 0, # all required modules are ok
76             );
77              
78             #============================================================================================
79              
80             =head2 CONSTRUCTOR
81              
82             =over 4
83              
84             =cut
85              
86             #============================================================================================
87              
88             =item B< new([%args]) >
89              
90             Create a new feature.
91              
92             The %args are specified as they would be in the B method.
93              
94             =cut
95              
96             sub new
97             {
98 157     157 1 2640 my ($obj, %args) = @_ ;
99              
100 157   33     2552 my $class = ref($obj) || $obj ;
101              
102 157 100       972 print "App::Framework::Base->new() class=$class\n" if $class_debug ;
103              
104             # Create object
105 157         2551 my $this = $class->SUPER::new(%args) ;
106              
107             ## Check for any required modules
108 157         391 my $ok = 1 ;
109 157         486 my %loaded ;
110 157         298 foreach my $module (@{$this->requires})
  157         5715  
111             {
112 2     1   274 eval "package $class; use $module;" ;
  1     1   10  
  1         2  
  1         104  
  1         27  
  1         9  
  1         91  
113 2 50       14 if ($@)
114             {
115 0         0 $loaded{$module} = 0 ;
116 0         0 $ok = 0 ;
117             }
118             else
119             {
120 2         9 $loaded{$module} = 1 ;
121             }
122             }
123 157         4872 $this->requires_ok($ok) ;
124 157         5992 $this->loaded(\%loaded) ;
125              
126             ## First check that all required modules loaded correcly
127 157 50       4765 if (!$this->requires_ok)
128             {
129 0         0 my $loaded_href = $class->loaded ;
130 0         0 my $failed_modules = join ', ', grep {$loaded_href->{$_}} keys %$loaded_href ;
  0         0  
131 0         0 $this->throw_fatal("Failed to load: $failed_modules") ;
132             }
133              
134 157 100       7678 print "App::Framework::Base->new() - END\n" if $class_debug ;
135              
136 157         1227 return($this) ;
137             }
138              
139              
140              
141             #============================================================================================
142              
143             =back
144              
145             =head2 CLASS METHODS
146              
147             =over 4
148              
149             =cut
150              
151             #============================================================================================
152              
153             #-----------------------------------------------------------------------------
154              
155             =item B< init_class([%args]) >
156              
157             Initialises the object class variables.
158              
159             =cut
160              
161             sub init_class
162             {
163 157     157 1 643 my $class = shift ;
164 157         978 my (%args) = @_ ;
165              
166             # Add extra fields
167 157         897 $class->add_fields(\%FIELDS, \%args) ;
168              
169             # init class
170 157         2363 $class->SUPER::init_class(%args) ;
171              
172             }
173              
174             #----------------------------------------------------------------------------
175              
176             =item B
177              
178             Processes all of the HASH values, replacing any variables with their contents. The variable
179             values are taken from the ARRAY ref I<$vars_aref>, which is an array of hashes. Each hash
180             containing variable name / variable value pairs.
181              
182             The HASH values being expanded can be either scalar, or an ARRAY ref. In the case of the ARRAY ref each
183             ARRAY entry must be a scalar (e.g. an array of file lines).
184              
185             =cut
186              
187             sub expand_keys
188             {
189 86     86 1 170 my $class = shift ;
190 86         187 my ($hash_ref, $vars_aref, $_state_href, $_to_expand) = @_ ;
191              
192 86 100       2125 print "expand_keys($hash_ref, $vars_aref)\n" if $class_debug;
193 86 100       325 $class->prt_data("vars=", $vars_aref, "hash=", $hash_ref) if $class_debug ;
194              
195 86 100       604 my %to_expand = $_to_expand ? (%$_to_expand) : (%$hash_ref) ;
196 86 100       312 if (!$_state_href)
197             {
198             ## Top-level
199 78         118 my %data_ref ;
200            
201             # create state HASH
202 78         154 $_state_href = {} ;
203            
204             # scan through hash looking for variables
205 78         158 %to_expand = () ;
206 78         319 foreach my $key (keys %$hash_ref)
207             {
208 251         259 my @vals ;
209 251 100       757 if (ref($hash_ref->{$key}) eq 'ARRAY')
    50          
210             {
211 75         91 @vals = @{$hash_ref->{$key}} ;
  75         372  
212             }
213             elsif (!ref($hash_ref->{$key}))
214             {
215 176         269 push @vals, $hash_ref->{$key} ;
216             }
217            
218             ## Set up state - provide a level of indirection so that we can handle the case where multiple keys point to the same data
219 251   100     593 my $ref = $hash_ref->{$key} || '' ;
220 251 100 100     1087 if ($ref && exists($data_ref{"$ref"}))
221             {
222 39 100       116 print " + already seen data for key=$key\n" if $class_debug>=2;
223             # already got created a state for this data, point to it
224 39         133 $_state_href->{$key} = $data_ref{"$ref"} ;
225             }
226             else
227             {
228 212 100       1537 print " + new state key=$key\n" if $class_debug>=2;
229 212         282 my $state = 'expanded' ;
230 212         401 $_state_href->{$key} = \$state ;
231             }
232              
233             # save data reference
234 251 100       1100 $data_ref{"$ref"} = $_state_href->{$key} if $ref ;
235            
236 251 100       1568 print " + check for expansion...\n" if $class_debug>=2;
237 251         354 foreach my $val (@vals)
238             {
239 1420 100       2190 next unless $val ;
240              
241 1160 100       3901 print " + + val=$val\n" if $class_debug>=2;
242              
243 1160 100       2655 if (index($val, '$') >= 0)
244             {
245 35 100       1017 print " + + + needs expanding\n" if $class_debug>=2;
246 35         82 $to_expand{$key}++ ;
247 35         46 ${$_state_href->{$key}} = 'to_expand' ;
  35         85  
248 35         145 last ;
249             }
250             }
251             }
252             }
253              
254 86 100       339 $class->prt_data("to expand=", \%to_expand) if $class_debug;
255              
256 86 100       293 $class->prt_data("Hash=", $hash_ref) if $class_debug;
257              
258             ## Expand them
259 86         943 foreach my $key (keys %to_expand)
260             {
261 43 100       141 print " # Key=$key State=${$_state_href->{$key}}\n" if $class_debug;
  20         461  
262            
263             # skip if not valid (if called recursively with a variable that is not in the hash)
264 43 50       127 next unless exists($hash_ref->{$key}) ;
265              
266             # Do replacement iff required
267 43 100       60 next if ${$_state_href->{$key}} eq 'expanded' ;
  43         240  
268              
269 28         53 my @vals ;
270 28 100       129 if (ref($hash_ref->{$key}) eq 'ARRAY')
    50          
271             {
272 7         13 foreach my $val (@{$hash_ref->{$key}})
  7         21  
273             {
274 76         112 push @vals, \$val ;
275             }
276             }
277             elsif (!ref($hash_ref->{$key}))
278             {
279 21         68 push @vals, \$hash_ref->{$key} ;
280             }
281            
282             # mark as expanding
283 28         45 ${$_state_href->{$key}} = 'expanding' ;
  28         71  
284              
285 28 100       167 $class->prt_data("Vals to expand=", \@vals) if $class_debug;
286              
287             #use re 'debugcolor' ;
288              
289 28         59 foreach my $val_ref (@vals)
290             {
291              
292 97 100       760 print " # Expand \"$$val_ref\" ...\n" if $class_debug;
293              
294 97         663 $$val_ref =~ s{
295             (?:
296             [\\\$]\$ # escaped dollar
297             \{{0,1} # optional brace
298             (\w+) # find a "word" and store it in $1
299             \}{0,1} # optional brace
300             )
301             |
302             (?:
303             \$ # find a literal dollar sign
304             \{{0,1} # optional brace
305             (\w+) # find a "word" and store it in $1
306             \}{0,1} # optional brace
307             )
308             }{
309 68         105 my $prefix = '' ;
310 68         218 my ($escaped, $var) = ($1, $2) ;
311            
312 68   100     251 $escaped ||= '' ;
313 68   100     196 $var ||= '' ;
314            
315 68 100       821 print " # esc=\"$escaped\", prefix=\"$prefix\", var=\"$var\"\n" if $class_debug;
316            
317 68         97 my $replace='' ;
318 68 100       127 if ($escaped)
319             {
320 16         23 $prefix = '$' ;
321 16         24 $replace = $escaped ;
322 16 50       98 print " ## escaped prefix=$prefix replace=$replace\n" if $class_debug;
323 16 50       86 print " ## DONE\n" if $class_debug;
324             }
325             else
326             {
327             ## use current HASH values before vars
328 52 100       150 if (defined $hash_ref->{$var})
329             {
330 30 100       114 print " ## var=$var current state=${$_state_href->{$var}}\n" if $class_debug;
  13         277  
331 30 100       44 if (${$_state_href->{$var}} eq 'to_expand')
  30         97  
332             {
333 8 100       282 print " ## var=$var call expand..\n" if $class_debug;
334             # go expand it first
335 8         151 $class->expand_keys($hash_ref, $vars_aref, $_state_href, {$var => 1}) ;
336             }
337 30 50       50 if (${$_state_href->{$var}} eq 'expanded')
  30         94  
338             {
339 30 100       231 print " ## var=$var already expanded\n" if $class_debug;
340 30         68 $replace = $hash_ref->{$var}; # expand variable
341 30 50       93 $replace = join("\n", @{$hash_ref->{$var}}) if (ref($hash_ref->{$var}) eq 'ARRAY') ;
  0         0  
342             }
343             }
344 52 100       656 print " ## var=$var can replace from hash=$replace\n" if $class_debug;
345            
346             ## If not found, use vars
347 52 100       118 if (!$replace)
348             {
349             ## use vars
350 22         40 foreach my $href (@$vars_aref)
351             {
352 46 100       112 if (defined $href->{$var})
353             {
354 22         40 $replace = $href->{$var}; # expand variable
355 22 50       58 $replace = join("\n", @{$hash_ref->{$var}}) if (ref($href->{$var}) eq 'ARRAY') ;
  0         0  
356 22 100       314 print " ## found var=$var replace=$replace\n" if $class_debug;
357 22         42 last ;
358             }
359             }
360             }
361 52 100       410 print " ## var=$var can replace now=$replace\n" if $class_debug;
362              
363 52 50       140 if (!$replace)
364             {
365 0         0 $replace = "" ;
366 0 0       0 print " ## no replacement\n" if $class_debug;
367 0 0       0 print " ## DONE\n" if $class_debug;
368             }
369             }
370            
371 68 100       522 print " ## ALL DONE $key: $escaped$var = \"$prefix$replace\"\n\n" if $class_debug;
372 68         333 "$prefix$replace" ;
373             }egxm; ## NOTE: /m is for multiline anchors; /s is for multiline dots
374             }
375              
376 28 100       138 $class->prt_data("Hash now=", $hash_ref) if $class_debug>=2;
377              
378             # mark as expanded
379 28         39 ${$_state_href->{$key}} = 'expanded' ;
  28         73  
380              
381 28 100       145 $class->prt_data("State now=", $_state_href) if $class_debug>=2;
382             }
383             }
384              
385              
386              
387             ##============================================================================================
388             #
389             #=back
390             #
391             #=head2 OBJECT DATA METHODS
392             #
393             #=over 4
394             #
395             #=cut
396             #
397             ##============================================================================================
398              
399              
400             ##============================================================================================
401             #
402             #=back
403             #
404             #=head2 OBJECT METHODS
405             #
406             #=over 4
407             #
408             #=cut
409             #
410             ##============================================================================================
411              
412              
413             #============================================================================================
414             #
415             # PRIVATE
416             #
417             #============================================================================================
418              
419             # ============================================================================================
420             # END OF PACKAGE
421              
422             =back
423              
424             =head1 DIAGNOSTICS
425              
426             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
427              
428             =head1 AUTHOR
429              
430             Steve Price C<< >>
431              
432             =head1 BUGS
433              
434             None that I know of!
435              
436             =cut
437              
438              
439             1;
440              
441             __END__