File Coverage

blib/lib/App/Framework/Core.pm
Criterion Covered Total %
statement 542 665 81.5
branch 89 174 51.1
condition 19 32 59.3
subroutine 96 106 90.5
pod 24 24 100.0
total 770 1001 76.9


line stmt bran cond sub pod time code
1             package App::Framework::Core ;
2              
3             =head1 NAME
4              
5             App::Framework::Core - Base application object
6              
7             =head1 SYNOPSIS
8              
9              
10             use App::Framework::Core ;
11            
12             our @ISA = qw(App::Framework::Core) ;
13              
14              
15             =head1 DESCRIPTION
16              
17             B
18              
19             Base class for applications. Expected to be derived from by an implementable class (like App::Framework::Core::Script).
20              
21             =cut
22              
23 31     31   284 use strict ;
  30         37  
  30         673  
24 30     30   92 use Carp ;
  30         34  
  30         1604  
25              
26             our $VERSION = "1.015" ;
27              
28              
29             #============================================================================================
30             # USES
31             #============================================================================================
32 30     30   10659 use App::Framework::Base ;
  30         51  
  30         1569  
33 30     30   13860 use App::Framework::Settings ;
  30         49  
  30         745  
34              
35 30     30   131 use App::Framework::Base::Object::DumpObj ;
  30         33  
  30         1025  
36              
37 30     30   114 use File::Basename ;
  30         33  
  30         3063  
38 30     30   121 use File::Spec ;
  30         605  
  30         1303  
39 30     30   2486 use File::Path ;
  30         669  
  30         2059  
40 30     30   16879 use File::Copy ;
  30         95322  
  30         1345  
41              
42 30     30   136 use Cwd ;
  30         30  
  30         18902  
43              
44              
45             #============================================================================================
46             # OBJECT HIERARCHY
47             #============================================================================================
48             our @ISA = qw(App::Framework::Base) ;
49              
50             #============================================================================================
51             # GLOBALS
52             #============================================================================================
53              
54             =head2 FIELDS
55              
56             The following fields should be defined either in the call to 'new()' or as part of the application configuration in the __DATA__ section:
57              
58             * name = Program name (default is name of program)
59             * summary = Program summary text
60             * synopsis = Synopsis text (default is program name and usage)
61             * description = Program description text
62             * history = Release history information
63             * version = Program version (default is value of 'our $VERSION')
64              
65             * feature_config = HASH ref containing setup information for any installed features. Each feature must have it's own
66             HASH of values, keyed by the feature name
67            
68             * app_start_fn = Function called before app() function (default is application-defined 'app_start' subroutine if available)
69             * app_fn = Function called to execute program (default is application-defined 'app' subroutine if available)
70             * app_end_fn = Function called after app() function (default is application-defined 'app_end' subroutine if available)
71             * usage_fn = Function called to display usage information (default is application-defined 'usage' subroutine if available)
72              
73             During program execution, the following values can be accessed:
74              
75             * package = Name of the application package (usually main::)
76             * filename = Full filename path to the application (after following any links)
77             * progname = Name of the program (without path or extension)
78             * progpath = Pathname to program
79             * progext = Extension of program
80            
81              
82             =over 4
83              
84             =cut
85              
86             my %FIELDS = (
87             ## Object Data
88            
89             # User-specified
90             'name' => '',
91             'summary' => '',
92             'synopsis' => '',
93             'description' => '',
94             'history' => '',
95             'version' => undef,
96             'feature_config'=> {},
97            
98             'app_start_fn' => undef,
99             'app_fn' => undef,
100             'app_end_fn' => undef,
101             'usage_fn' => undef,
102            
103             'exit_type' => 'exit',
104            
105             # Created during init
106             'package' => undef,
107             'filename' => undef,
108             'progname' => undef,
109             'progpath' => undef,
110             'progext' => undef,
111              
112             'feature_list' => [], # all registered feature names, sorted by priority
113             '_feature_list' => {}, # all registered features
114             '_feature_methods' => {}, # HASH or ARRAYs of any methods registered to a feature
115            
116             '_required_features' => [qw/Data Options Args Pod/],
117              
118             'personality' => undef,
119             'extensions' => [],
120             ) ;
121              
122             # Set of default options
123             my @BASE_OPTIONS = (
124             ['debug=i', 'Set debug level', 'Set the debug level value', ],
125             ) ;
126              
127             our %LOADED_MODULES ;
128              
129             our $class_debug = 0 ;
130              
131              
132             #============================================================================================
133              
134             =back
135              
136             =head2 CONSTRUCTOR METHODS
137              
138             =over 4
139              
140             =cut
141              
142             #============================================================================================
143              
144             =item B
145              
146             Create a new App::Framework::Core.
147              
148             The %args are specified as they would be in the B method, for example:
149              
150             'mmap_handler' => $mmap_handler
151              
152             The full list of possible arguments are :
153              
154             'fields' => Either ARRAY list of valid field names, or HASH of field names with default values
155              
156             =cut
157              
158             sub new
159             {
160 26     26 1 81 my ($obj, %args) = @_ ;
161              
162 26   33     148 my $class = ref($obj) || $obj ;
163              
164             ## stop 'app' entry from being displayed in Features
165 26         128 App::Framework::Base::Object::DumpObj::exclude('app') ;
166            
167 26 50       98 print "App::Framework::Core->new() class=$class\n" if $class_debug ;
168            
169 26   33     102 my $caller_info_aref = delete $args{'_caller_info'} || croak "$class must be called via App::Framework" ;
170              
171             # Create object
172 26         211 my $this = $class->SUPER::new(%args) ;
173            
174             # Set up error handler
175 26     0   196 $this->set('catch_fn' => sub {$this->catch_error(@_);} ) ;
  0         0  
176              
177             ## Get caller information
178 26         107 my ($package, $filename, $line, $subr, $has_args, $wantarray) = @$caller_info_aref ;
179 26         83 $this->set(
180             'package' => $package,
181             'filename' => $filename,
182             ) ;
183              
184             ## now import packages into the caller's namespace
185 26         174 $this->_import() ;
186              
187              
188             ## Set program info
189 26         2620 $this->set_paths($filename) ;
190            
191             ## set up functions
192             # foreach my $fn (qw/app_start app app_end usage/)
193 26         235 foreach my $fn_aref (
194             # prefered
195             ['app_start', 'app_start'],
196             ['app', 'app'],
197             ['app_end', 'app_end'],
198             ['usage', 'usage'],
199              
200             # alternates
201             ['app_begin', 'app_start'],
202             ['app_enter', 'app_start'],
203             ['app_init', 'app_start'],
204             ['app_finish', 'app_end'],
205             ['app_exit', 'app_end'],
206             ['app_term', 'app_end'],
207             )
208             {
209 260         328 my ($fn, $alias) = @$fn_aref ;
210            
211             # Only add function if it's not already been specified
212 260         526 $this->_register_fn($fn, $alias) ;
213             }
214              
215             ## Get version
216 26         198 $this->_register_scalar('VERSION', 'version') ;
217              
218             ## Ensure name set
219 26 50       569 if (!$this->name())
220             {
221 26         475 $this->name($this->progname() ) ;
222             }
223              
224              
225             ## Set up default timezone
226 26 50       96 if (exists($LOADED_MODULES{'Date::Manip'}))
227             {
228 26   50     161 my $tz = $App::Frameowrk::Settings::DATE_TZ || 'GMT' ;
229 26   50     137 my $fmt = $App::Frameowrk::Settings::DATE_FORMAT || 'non-US' ;
230 26         60 eval {
231 26         207 my $date = new Date::Manip::Date;
232 26         130712 $date->config("setdate", "zone,$tz") ;
233            
234             #&Date_Init("TZ=$tz", "DateFormat=$fmt") ;
235             } ;
236             }
237              
238             ## Install required features
239 26         15130 $this->install_features($this->_required_features) ;
240            
241             ## Need to do some init of required features
242 26         249 $this->feature('Options')->append_options(\@BASE_OPTIONS) ;
243              
244 26 50       102 print "App::Framework::Core->new() - END\n" if $class_debug ;
245              
246 26         406 return($this) ;
247             }
248              
249              
250              
251             #============================================================================================
252              
253             =back
254              
255             =head2 CLASS METHODS
256              
257             =over 4
258              
259             =cut
260              
261             #============================================================================================
262              
263             #-----------------------------------------------------------------------------
264              
265             =item B
266              
267             Initialises the App::Framework::Core object class variables.
268              
269             =cut
270              
271             sub init_class
272             {
273 26     26 1 52 my $class = shift ;
274 26         74 my (%args) = @_ ;
275              
276             # Add extra fields
277 26         103 $class->add_fields(\%FIELDS, \%args) ;
278              
279             # init class
280 26         185 $class->SUPER::init_class(%args) ;
281              
282             }
283              
284             #----------------------------------------------------------------------------
285              
286             =item B
287              
288             Class instance object is not allowed
289            
290             =cut
291              
292             sub allowed_class_instance
293             {
294 0     0 1 0 return 0 ;
295             }
296              
297             #----------------------------------------------------------------------------
298              
299             =item B
300              
301             Attempt to load the module into the specified package I<$pkg> (or load it into a temporary space).
302              
303             Then checks that the load was ok by checking the module's version number.
304              
305             Returns 1 on success; 0 on failure.
306            
307             =cut
308              
309             sub dynamic_load
310             {
311 460     460 1 598 my $class = shift ;
312 460         594 my ($module, $pkg) = @_ ;
313              
314 460         453 my $loaded = 0 ;
315            
316             # for windoze....
317 460 50       1280 if ($^O =~ /MSWin32/i)
318             {
319 0 0       0 return 0 unless $class->find_lib($module) ;
320             }
321            
322 460   100     1631 $pkg ||= 'temp_app_pkg' ;
323            
324 460 50       720 print "dynamic_load($module) into $pkg\n" if $class_debug ;
325              
326 460         377 my $version ;
327 27     27   12723 eval "
  26     26   46  
  26     25   547  
  26     25   9915  
  25     25   69  
  25     25   780  
  25     25   6086  
  1     25   3  
  1     25   29  
  25     25   4585  
  1     25   1  
  1     25   17  
  25     25   13153  
  25     25   47  
  25     25   665  
  25     25   6044  
  0     25   0  
  0     17   0  
  25         4772  
  0         0  
  0         0  
  25         16064  
  25         56  
  25         758  
  25         6054  
  0         0  
  0         0  
  25         4725  
  0         0  
  0         0  
  25         17153  
  25         50  
  25         771  
  25         5683  
  0         0  
  0         0  
  25         5101  
  0         0  
  0         0  
  25         14586  
  25         55  
  25         706  
  25         6012  
  0         0  
  0         0  
  25         4794  
  0         0  
  0         0  
  25         6731  
  10         18  
  10         239  
  17         10881  
  16         64  
  16         527  
  460         28545  
328             package $pkg;
329             use $module;
330             \$version = \$${module}::VERSION ;
331             " ;
332             #print "Version = $version\n" ;
333 460 100       1776 if ($@)
    50          
334             {
335 275 50       774 print "dynamic_load($module, $pkg) : error : $@\nAborting dynamic_load.\n" if $class_debug ;
336             }
337             elsif (defined($version))
338             {
339 185         284 $loaded = 1 ;
340             }
341 460 50       823 print "dynamic_load($module, $pkg) : loaded = $loaded.\n" if $class_debug ;
342              
343 460         1426 return $loaded ;
344             }
345              
346             #----------------------------------------------------------------------------
347              
348             =item B
349              
350             Load the module into the caller's namespace then set it's @ISA ready for that
351             module to call it's parent's new() method
352            
353             =cut
354              
355             sub dynamic_isa
356             {
357 54     54 1 68 my $class = shift ;
358 54         72 my ($module, $pkg) = @_ ;
359              
360 54 50       115 unless ($pkg)
361             {
362 0         0 my @callinfo = caller(0);
363 0         0 $pkg = $callinfo[0] ;
364             }
365 54         125 my $loaded = $class->dynamic_load($module, $pkg) ;
366              
367 54 50       140 if ($loaded)
368             {
369 30     30   137 no strict 'refs' ;
  30         28  
  30         10361  
370            
371             ## Create ourself as if we're an object of the required type (but only if ISA is not already set)
372 54 100       73 if (!scalar(@{"${pkg}::ISA"}))
  54         308  
373             {
374 51 50       122 print "dynamic_isa() $pkg set ISA=$module\n" if $class_debug ;
375 51         71 @{"${pkg}::ISA"} = ( $module ) ;
  51         727  
376             }
377             else
378             {
379 3 50       9 print "dynamic_isa() - $pkg already got ISA=",@{"${pkg}::ISA"}," (wanted to set $module)\n" if $class_debug ;
  0         0  
380             }
381              
382             }
383              
384 54         191 return $loaded ;
385             }
386              
387              
388             #-----------------------------------------------------------------------------
389              
390             =item B< inherit($caller_class, [%args]) >
391              
392             Initialises the object class variables.
393              
394             =cut
395              
396             sub inherit
397             {
398 27     27 1 62 my $class = shift ;
399 27         74 my ($caller_class, %args) = @_ ;
400              
401             ## get calling package
402 27         126 my $caller_pkg = (caller(0))[0] ;
403              
404 27 50       89 print "\n\n----------------------------------------\n" if $class_debug ;
405 27 50       68 print "Core:inherit() caller=$caller_pkg\n" if $class_debug ;
406            
407             ## get inheritence stack, grab this object's class, restore list
408 27   50     92 my $inheritence = delete $args{'_inheritence'} || [] ;
409              
410 27 50       61 print " + inherit=\n\t".join("\n\t", @$inheritence)."\n" if $class_debug ;
411              
412             ## Get parent and restore new list
413 27         53 my $parent = shift @$inheritence ;
414 27         48 $args{'_inheritence'} = $inheritence ;
415              
416 27 50       68 print "Core: $caller_class parent=$parent inherit=@$inheritence\n" if $class_debug ;
417              
418             ## load in base objects
419 27         39 my $_caller = $parent ;
420 27         71 foreach my $_parent (@$inheritence)
421             {
422 1 50       2 print " + Preloading: load $_parent into $_caller\n" if $class_debug ;
423              
424             ## Dynamic load this parent into this caller
425 1         2 my $loaded = App::Framework::Core->dynamic_isa($_parent, $_caller) ;
426 1 50       4 croak "Sorry, failed to load \"$_parent\"" unless $loaded ;
427              
428 1 50       2 App::Framework::Core::_dumpvar($_caller) if $class_debug ;
429 1 50       3 App::Framework::Core::_dumpvar($_parent) if $class_debug ;
430              
431             # update caller for next time round
432 1         3 $_caller = $_parent ;
433             }
434              
435 27 50       66 print " + Loading: load $parent into $caller_pkg\n" if $class_debug ;
436              
437             ## Dynamic load this object
438 27         70 my $loaded = App::Framework::Core->dynamic_isa($parent, $caller_pkg) ;
439 27 50       101 croak "Sorry, failed to load \"$parent\"" unless $loaded ;
440              
441 27 50       90 App::Framework::Core::_dumpvar($caller_pkg) if $class_debug ;
442 27 50       76 App::Framework::Core::_dumpvar($parent) if $class_debug ;
443              
444 27 50       85 print "Core: calling $caller_pkg -> $parent ::new()\n" if $class_debug ;
445 27 50       82 App::Framework::Core::_dumpisa($caller_pkg) if $class_debug ;
446              
447             ## Create object
448 27         44 my $this ;
449             {
450 30     30   124 no strict 'refs' ;
  30         31  
  30         9616  
  27         43  
451              
452 27         109 $this = &{"${parent}::new"}(
  27         154  
453             $caller_class,
454             %args,
455             ) ;
456            
457             }
458              
459 27 50       128 print "Core:inherit() - END\n" if $class_debug ;
460 27 50       92 print "----------------------------------------\n\n" if $class_debug ;
461            
462 27         279 return $this ;
463             }
464              
465              
466             #----------------------------------------------------------------------------
467              
468             =item B< find_lib($module) >
469              
470             Looks for the named module in the @INC path. If found, checks the package name inside the file
471             to ensure that it really matches the capitalisation.
472              
473             (Mainly for Microsoft Windows use!)
474              
475             =cut
476              
477             sub find_lib
478             {
479 0     0 1 0 my $class = shift ;
480 0         0 my ($module) = @_ ;
481              
482 0         0 my @module_dirs = split /::/, $module ;
483 0         0 my $pm = pop @module_dirs ;
484              
485             #print "find_lib($module)\n" ;
486            
487 0         0 my $found ;
488 0         0 foreach my $dir (@INC)
489             {
490 0         0 my $file = File::Spec->catfile($dir, @module_dirs, "$pm.pm") ;
491              
492             #print " + checking $file\n" ;
493 0 0       0 if (-f $file)
494             {
495 0 0       0 if (open my $fh, "<$file")
496             {
497 0         0 my $line ;
498 0         0 while (defined($line = <$fh>))
499             {
500 0         0 chomp $line ;
501 0 0       0 if ($line =~ m/^\s*package\s+$module\s*;/)
502             {
503 0         0 $found = $module ;
504 0         0 last ;
505             }
506             }
507 0         0 close $fh ;
508             }
509 0 0       0 last if $found ;
510             }
511             }
512              
513             #print "find_lib() = $found\n" ;
514              
515 0         0 return $found ;
516             }
517              
518             #----------------------------------------------------------------------------
519              
520             =item B< lib_glob($module_path) >
521              
522             Looks for any perl modules contained under the module path. Looks at all possible locations
523             in the @INC path, returning the first found.
524              
525             Returns a HASH contains the module name as key and the full filename path as the value.
526            
527             =cut
528              
529             sub lib_glob
530             {
531 0     0 1 0 my $class = shift ;
532 0         0 my ($module_path) = @_ ;
533              
534 0         0 my %libs ;
535 0         0 foreach my $dir (@INC)
536             {
537 0         0 my $module_path = File::Spec->catfile($dir, $module_path, "*.pm") ;
538 0         0 my @files = glob($module_path) ;
539 0         0 foreach my $file (@files)
540             {
541 0         0 my ($base, $path, $ext) = fileparse($file, '\..*') ;
542 0 0       0 if (!exists($libs{$base}))
543             {
544 0         0 $libs{$base} = $file ;
545             }
546             }
547             }
548              
549 0         0 return %libs ;
550             }
551              
552             #----------------------------------------------------------------------------
553              
554             =item B
555              
556             Starting at I, return a HASH ref in the form of a tree of it's parents. They keys are the parent module
557             names, and the values are HASH refs of their parents and so on. Value is undef when last parent
558             is reached.
559              
560             =cut
561              
562             sub isa_tree
563             {
564 30     30   130 no strict "vars" ;
  30         57  
  30         724  
565 30     30   96 no strict "refs" ;
  30         51  
  30         16090  
566              
567 0     0 1 0 my $class = shift ;
568 0         0 my ($packageName) = @_;
569            
570 0         0 my $tree_href = {} ;
571            
572            
573 0         0 foreach my $isa (@{"${packageName}::ISA"})
  0         0  
574             {
575 0         0 $tree_href->{$isa} = $class->isa_tree($isa) ;
576             }
577            
578 0         0 return $tree_href ;
579             }
580              
581             #============================================================================================
582              
583             =back
584              
585             =head2 OBJECT METHODS
586              
587             =over 4
588              
589             =cut
590              
591             #============================================================================================
592              
593             #----------------------------------------------------------------------------
594              
595             =item B
596              
597             Get the full path to this application (follows links where required)
598              
599             =cut
600              
601             sub set_paths
602             {
603 54     54 1 106 my $this = shift ;
604 54         95 my ($filename) = @_ ;
605              
606             # Follow links
607 54         1831 $filename = File::Spec->rel2abs($filename) ;
608 54         1619 while ( -l $filename)
609             {
610 0         0 $filename = readlink $filename ;
611             }
612            
613             # Get info
614 54         2602 my ($progname, $progpath, $progext) = fileparse($filename, '\.[^\.]+') ;
615 54 100       191 if (ref($this))
616             {
617             # set if not class call
618 26         380 $this->set(
619             'progname' => $progname,
620             'progpath' => $progpath,
621             'progext' => $progext,
622             ) ;
623             }
624              
625             # Set up include path to add script home + script home /lib subdir
626 54         116 my %inc = map {$_=>1} @INC ;
  700         1208  
627 54         198 foreach my $path ($progpath, "$progpath/lib")
628             {
629             # add new paths
630 108 100       317 unshift(@INC,$path) unless exists $inc{$path} ;
631 108         169 $inc{$path} = 1 ;
632 108 50       380 push @INC, $path unless exists $inc{$path} ;
633             }
634             }
635              
636             #----------------------------------------------------------------------------
637              
638             =item B
639              
640             Function that gets called on errors. $error is as defined in L
641              
642             =cut
643              
644             sub catch_error
645             {
646 1     1 1 2 my $this = shift ;
647 1         1 my ($error) = @_ ;
648              
649             # Does nothing!
650              
651 1         4 $this->_dispatch_entry_features($error) ;
652            
653 1         2 $this->_dispatch_exit_features($error) ;
654              
655             }
656              
657              
658             #----------------------------------------------------------------------------
659              
660             =item B
661              
662             Add the listed features to the application. List is an ARRAY ref list of feature names.
663              
664             Note: names need correct capitalisation (e.g. Sql not sql) - or just use first char capitalised(?)
665              
666             Method/feature name will be all lowercase
667              
668             Optionally, can specify I<$feature_args> HASH ref. Each feature name in I<$feature_list> should be a key
669             in the HASH, the value of which is an arguments string (which is a list of feature arguments separated by space and/or
670             commas)
671              
672             =cut
673              
674             sub install_features
675             {
676 51     51 1 91 my $this = shift ;
677 51         89 my ($feature_list, $feature_args_href) = @_ ;
678              
679 51   100     248 $feature_args_href ||= {} ;
680            
681 51         1116 my $features_href = $this->_feature_list() ;
682              
683             ## make a list of features
684 51         161 my @features = @$feature_list ;
685            
686 51         261 $this->_dbg_prt(["install_features()", \@features, "features args=", $feature_args_href]) ;
687 51 50       169 $class_debug = $this->debug if $this->debug >= 5 ;
688              
689            
690             ## Now try to install them
691 51         110 foreach my $feature (@features)
692             {
693 130   100     645 my $feature_args = $feature_args_href->{$feature} || "" ;
694            
695 130         150 my $loaded ;
696 130         380 my $feature_guess = ucfirst(lc($feature)) ;
697            
698             ## skip if already loaded
699 130 50 33     575 if (exists($features_href->{$feature}) || exists($features_href->{$feature_guess}))
700             {
701             ## Just need to see if we've got any new args
702 0         0 foreach my $feat ($feature, $feature_guess)
703             {
704 0 0       0 if (exists($feature_args_href->{$feat}))
705             {
706             ## override args
707 0         0 my $feature_obj = $features_href->{$feature}{'object'} ;
708 0         0 $feature_obj->feature_args($feature_args_href->{$feat}) ;
709             }
710             }
711 0         0 next ;
712             }
713              
714             # build list of module names to attempt. If personality name is set, try looking for feature
715             # under personality subdir first. This allows for personality override of feature (e.g. POE:app overrides Script:app)
716             #
717 130         152 my @tries ;
718 130         3170 my $personality = $this->personality ;
719 130         171 my $root = "App::Framework::Feature" ;
720 130 50       296 if ($personality)
721             {
722 130         351 push @tries, "${root}::${personality}::$feature" ;
723 130         297 push @tries, "${root}::${personality}::$feature_guess" ;
724             }
725 130         282 push @tries, "${root}::$feature" ;
726 130         206 push @tries, "${root}::$feature_guess" ;
727            
728 130         226 foreach my $module (@tries)
729             {
730 405 100       1421 if ($this->dynamic_load($module))
731             {
732 130         181 $loaded = $module ;
733 130         233 last ;
734             }
735             }
736              
737 130         325297 my $cwd = cwd() ;
738 130 50       1049 $this->_dbg_prt(["Feature: $feature - unable to load. CWD=$cwd.\n", "Tried=", \@tries, "\n\@INC=", \@INC]) unless ($loaded) ;
739              
740 130 50       470 croak "Feature \"$feature\" not supported" unless ($loaded) ;
741              
742 130         3186 $this->_dbg_prt(["Feature: $feature - loaded=$loaded\n"]) ;
743            
744 130 50       982 if ($loaded)
745             {
746             # save in list
747 130         290 my $module = $loaded ;
748 130         284 my $specified_name = $feature ;
749 130         509 $feature = lc $feature ;
750              
751 130         1991 $features_href->{$feature} = {
752             'module' => $module, # loaded module name
753             'specified' => $specified_name, # as specified by user
754             'name' => $feature, # name used as a method
755             'object' => undef,
756             'priority' => $App::Framework::Base::PRIORITY_DEFAULT,
757             } ;
758            
759             # see if we have some extra init values to pass to the feature
760 130         1770 my $feature_init_href = $this->_feature_init($feature) ;
761            
762             # create feature
763             my $feature_obj = $module->new(
764             %$feature_init_href,
765            
766             'app' => $this,
767             'name' => $feature, # ensure it matches with what the app expects
768             'feature_args' => $feature_args,
769              
770             # Set up error handler
771 1     1   11 'catch_fn' => sub {$this->catch_error(@_);},
772              
773 130         1787 ) ;
774              
775             # add to list (may already have been done if feature registers any methods)
776 130         363 $features_href->{$feature}{'object'} = $feature_obj ;
777 130         2504 $features_href->{$feature}{'priority'} = $feature_obj->priority ;
778            
779             # set up alias
780             {
781 30     30   132 no warnings 'redefine';
  30         28  
  30         1020  
  130         149  
782 30     30   102 no strict 'refs';
  30         38  
  30         51103  
783            
784             ## alias ()
785 130         373 my $alias = lc $feature ;
786 130         1922 *{"App::Framework::Core::${alias}"} = sub {
787 533     533   29053 my $this = shift ;
788 533         1564 return $feature_obj->$alias(@_) ;
789 130         770 };
790              
791             ## alias ()
792 130         378 $alias = ucfirst $feature ;
793 130         1975 *{"App::Framework::Core::${alias}"} = sub {
794 52     52   10736 my $this = shift ;
795 52         194 return $feature_obj->$alias(@_) ;
796 130         600 };
797             }
798             }
799             }
800              
801              
802             ## Ensure list is sorted by priority
803 51         389 $this->feature_list( [ sort {$features_href->{$a}{'priority'} <=> $features_href->{$b}{'priority'}} keys %$features_href ] ) ;
  286         1638  
804              
805            
806 51         441 $this->_dbg_prt(["installed features = ", $features_href]) ;
807            
808             }
809              
810              
811             #----------------------------------------------------------------------------
812             #
813             #=item B<_feature_init($feature)>
814             #
815             #Get any initialisation values for this feature. Returns an empty HASH ref if no
816             #init specified
817             #
818             #=cut
819             #
820             sub _feature_init
821             {
822 130     130   210 my $this = shift ;
823 130         357 my ($feature) = @_ ;
824            
825 130         381 my $feature_init_href = {} ;
826              
827             ## May have some initialisation values for the feature
828 130         6468 my $feature_config_href = $this->feature_config ;
829              
830             ## See if we can find a name match
831 130         623 foreach my $name (keys %$feature_config_href)
832             {
833 52 100       220 if (lc $name eq lc $feature)
834             {
835 14         66 $feature_init_href = $feature_config_href->{$name} ;
836             #$this->prt_data("_feature_init($feature)=", $feature_init_href) ;
837 14         60 last ;
838             }
839             }
840            
841 130         261 return $feature_init_href ;
842             }
843              
844             ##----------------------------------------------------------------------------
845             #
846             #=item B
847             #
848             #Return list of installed features
849             #
850             #=cut
851             #
852             #sub feature_list
853             #{
854             # my $this = shift ;
855             #
856             # my $features_href = $this->_feature_list() ;
857             # my @list = map {$features_href->{$_}{'specified'}} keys %$features_href ;
858             # return @list ;
859             #}
860              
861             ##----------------------------------------------------------------------------
862             #
863             #=item B<_feature_info($name)>
864             #
865             #Return HASH ref of feature information for this feature.
866             #
867             #=cut
868             #
869             sub _feature_info
870             {
871 978     978   811 my $this = shift ;
872 978         1064 my ($name, %args) = @_ ;
873              
874 978         17941 my $features_href = $this->_feature_list() ;
875 978         1280 $name = lc $name ;
876            
877 978         727 my $info_href ;
878 978 50       1479 if (exists($features_href->{$name}))
879             {
880 978         1160 $info_href = $features_href->{$name} ;
881             }
882             else
883             {
884 0         0 $this->throw_fatal("Feature \"$name\" not found") ;
885             }
886              
887 978         1205 return $info_href ;
888             }
889              
890             #----------------------------------------------------------------------------
891              
892             =item B
893              
894             Return named feature object if the feature is installed; otherwise returns undef.
895              
896             =cut
897              
898             sub feature_installed
899             {
900 15     15 1 18 my $this = shift ;
901 15         18 my ($name) = @_ ;
902              
903 15         369 my $features_href = $this->_feature_list() ;
904 15         28 $name = lc $name ;
905            
906 15         14 my $feature = undef ;
907 15 100       31 if (exists($features_href->{$name}))
908             {
909 5         5 my $feature_href = $features_href->{$name} ;
910 5         5 $feature = $feature_href->{'object'} ;
911             }
912              
913 15         27 return $feature ;
914             }
915              
916              
917              
918             #----------------------------------------------------------------------------
919              
920             =item B
921              
922             Return named feature object. Alternative interface to just calling the feature's 'get/set' method.
923              
924             For example, 'sql' feature can be accessed either as:
925              
926             my $sql = $app->feature("sql") ;
927            
928             or:
929              
930             my $sql = $app->sql() ;
931            
932              
933             =cut
934              
935             sub feature
936             {
937 891     891 1 11241 my $this = shift ;
938 891         1663 my ($name, %args) = @_ ;
939              
940 891         1393 my $feature_href = $this->_feature_info($name) ;
941              
942 891         969 my $feature = $feature_href->{'object'} ;
943 891 50       1440 if (%args)
944             {
945 0         0 $feature->set(%args) ;
946             }
947              
948 891         3631 return $feature ;
949             }
950              
951              
952             #----------------------------------------------------------------------------
953              
954             =item B
955              
956             API for feature objects. Used so that they can register their methods to be called
957             at the start and end of the registered functions.
958              
959             Function list is a list of strings where the string is in the format:
960              
961             _entry
962             _exit
963              
964             To register a call at the start of the method and/or at the end of the method.
965              
966             This is usually called when the feature is being created (which is usually because this Core object
967             is installing the feature). To ensure the core's lists are up to date, this function sets the feature object
968             and priority.
969              
970             =cut
971              
972             sub feature_register
973             {
974 57     57 1 82 my $this = shift ;
975 57         152 my ($feature, $feature_obj, @function_list) = @_ ;
976            
977             ## Add methods
978 57         1214 my $feature_methods_href = $this->_feature_methods() ;
979 57         180 foreach my $method (@function_list)
980             {
981 87         486 my $feature_href = $this->_feature_info($feature) ;
982              
983             # update info (ensure's core has latest info)
984 87         131 $feature_href->{'object'} = $feature_obj ;
985 87         1690 $feature_href->{'priority'} = $feature_obj->priority ;
986              
987             #$this->prt_data("Feature info=", $feature_href);
988            
989 87   100     422 $feature_methods_href->{$method} ||= [] ;
990 87         595 push @{$feature_methods_href->{$method}}, {
991             'feature' => $feature,
992             'obj' => $feature_href->{'object'},
993 87         100 'priority' => $feature_href->{'priority'},
994             }
995            
996             }
997              
998             #$this->prt_data("Raw feature list=", $feature_methods_href);
999              
1000             ## Ensure all lists are sorted by priority
1001 57         142 foreach my $method (@function_list)
1002             {
1003 87         97 $feature_methods_href->{$method} = [ sort {$a->{'priority'} <=> $b->{'priority'}} @{$feature_methods_href->{$method}} ] ;
  40         244  
  87         572  
1004             }
1005              
1006             #$this->prt_data("Sorted feature list=", $feature_methods_href);
1007              
1008             }
1009              
1010              
1011             #----------------------------------------------------------------------------
1012             #
1013             #=item B<_dispatch_features($method, 'entry|exit')>
1014             #
1015             #INTERNAL: For the specified method, run any features that registered for this method.
1016             #
1017             #=cut
1018             #
1019             sub _dispatch_features
1020             {
1021 898     898   722 my $this = shift ;
1022 898         1113 my ($method, $status, @args) = @_ ;
1023              
1024 898 100       1502 @args = () unless @args ;
1025 898         2611 $this->_dbg_prt(["_dispatch_features(method=$method, status=$status) : args=", \@args]) ;
1026            
1027             # remove package name (if specified)
1028 898         3075 $method =~ s/^(.*)::// ;
1029            
1030 898         16512 my $feature_methods_href = $this->_feature_methods() ;
1031 898         1189 my $fn = "${method}_${status}" ;
1032 898         2244 $this->_dbg_prt([" + method=$method fn=$fn\n"]) ;
1033              
1034 898 100       3138 if (exists($feature_methods_href->{$fn}))
1035             {
1036 74         81 foreach my $feature_entry (@{$feature_methods_href->{$fn}})
  74         231  
1037             {
1038 114         476 $this->_dbg_prt([" + dispatching fn=$fn feature=$feature_entry->{feature}\n"]) ;
1039 114         314 $this->_dbg_prt(["++ entry=", $feature_entry], 2) ;
1040              
1041 114         156 my $feature_obj = $feature_entry->{'obj'} ;
1042 114         564 $feature_obj->$fn(@args) ;
1043             }
1044             }
1045            
1046             }
1047              
1048             #----------------------------------------------------------------------------
1049             #
1050             #=item B<_dispatch_entry_features(@args)>
1051             #
1052             #INTERNAL: Calls _dispatch_features with the correct method name, and $status='entry'
1053             #
1054             #=cut
1055             #
1056             sub _dispatch_entry_features
1057             {
1058 187     187   241 my $this = shift ;
1059 187         258 my (@args) = @_ ;
1060            
1061 187         799 my $method = (caller(1))[3] ;
1062 187         593 return $this->_dispatch_features($method, 'entry', @_) ;
1063             }
1064              
1065              
1066             #----------------------------------------------------------------------------
1067             #
1068             #=item B<_dispatch_exit_features(@args)>
1069             #
1070             #INTERNAL: Calls _dispatch_features with the correct method name, and $status='exit'
1071             #
1072             #=cut
1073             #
1074             sub _dispatch_exit_features
1075             {
1076 171     171   192 my $this = shift ;
1077              
1078 171         708 my $method = (caller(1))[3] ;
1079 171         415 return $this->_dispatch_features($method, 'exit', @_) ;
1080             }
1081              
1082              
1083             #----------------------------------------------------------------------------
1084             #
1085             #=item B<_dispatch_label_entry_features($label, @args)>
1086             #
1087             #INTERNAL: Calls _dispatch_features with the correct method name, and $status='entry'
1088             #
1089             #=cut
1090             #
1091             sub _dispatch_label_entry_features
1092             {
1093 270     270   192 my $this = shift ;
1094 270         302 my ($label, @args) = @_ ;
1095            
1096 270         652 my $method = (caller(1))[3] ;
1097 270 50       490 $method .= "_$label" if $label ;
1098 270         341 return $this->_dispatch_features($method, 'entry', @args) ;
1099             }
1100              
1101              
1102             #----------------------------------------------------------------------------
1103             #
1104             #=item B<_dispatch_label_exit_features($label, @args)>
1105             #
1106             #INTERNAL: Calls _dispatch_features with the correct method name, and $status='exit'
1107             #
1108             #=cut
1109             #
1110             sub _dispatch_label_exit_features
1111             {
1112 270     270   200 my $this = shift ;
1113 270         316 my ($label, @args) = @_ ;
1114              
1115 270         693 my $method = (caller(1))[3] ;
1116 270 50       469 $method .= "_$label" if $label ;
1117 270         351 return $this->_dispatch_features($method, 'exit', @args) ;
1118             }
1119              
1120              
1121              
1122             #= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
1123              
1124             =back
1125              
1126             =head3 Application execution methods
1127              
1128             =over 4
1129              
1130             =cut
1131              
1132              
1133              
1134              
1135             #----------------------------------------------------------------------------
1136              
1137             =item B
1138              
1139             Execute the application.
1140              
1141             Calls the following methods in turn:
1142              
1143             * app_start
1144             * application
1145             * app_end
1146             * exit
1147            
1148             =cut
1149              
1150              
1151             sub go
1152             {
1153 36     36 1 13406 my $this = shift ;
1154              
1155 36         337 $this->_dispatch_entry_features() ;
1156              
1157 36         249 $this->app_start() ;
1158 36         246 $this->application() ;
1159 28         264 $this->app_end() ;
1160              
1161 28         70 $this->_dispatch_exit_features() ;
1162              
1163 28         332 $this->exit(0) ;
1164             }
1165              
1166             #----------------------------------------------------------------------------
1167              
1168             =item B
1169              
1170             Convert the (already processed) options list into settings.
1171              
1172             Returns result of calling GetOptions
1173              
1174             =cut
1175              
1176             sub getopts
1177             {
1178 36     36 1 76 my $this = shift ;
1179              
1180 36         123 $this->_dispatch_entry_features() ;
1181              
1182             # Parse options using GetOpts
1183 36         122 my $opt = $this->feature('Options') ;
1184 36         116 my $args = $this->feature('Args') ;
1185            
1186 36         183 my $ok = $opt->get_options() ;
1187              
1188             # If ok, get any specified filenames
1189 36 50       105 if ($ok)
1190             {
1191             # Get args
1192 36         244 my $arglist = $args->get_args() ;
1193              
1194 36         154 $this->_dbg_prt(["getopts() : arglist=", $arglist], 2) ;
1195             }
1196            
1197             ## Expand vars
1198 36         71 my %values ;
1199 36         175 my ($opt_values_href, $opt_defaults_href) = $opt->option_values_hash() ;
1200 36         157 my ($args_values_href) = $args->args_values_hash() ;
1201            
1202 36         117 %values = (%$opt_values_href) ;
1203 36         63 my %args_clash ;
1204 36         100 foreach my $key (keys %$args_values_href)
1205             {
1206 68 50       92 if (exists($values{$key}))
1207             {
1208 0         0 $args_clash{$key} = $args_values_href->{$key} ;
1209             }
1210             else
1211             {
1212 68         89 $values{$key} = $args_values_href->{$key} ;
1213             }
1214             }
1215              
1216 36         49 my @vars ;
1217 36         122 my %app_vars = $this->vars ;
1218 36         156 push @vars, \%app_vars ;
1219 36         71 push @vars, \%ENV ;
1220              
1221             ## expand all vars
1222 36         368 $this->expand_keys(\%values, \@vars) ;
1223            
1224             # set new values
1225 36         83 foreach my $key (keys %$opt_values_href)
1226             {
1227 85         91 $opt_values_href->{$key} = $values{$key} ;
1228             }
1229 36         83 foreach my $key (keys %$args_values_href)
1230             {
1231 68         365 $args_values_href->{$key} = $values{$key} ;
1232             }
1233              
1234             ## handle any name clash
1235 36 50       118 if (keys %args_clash)
1236             {
1237 0         0 unshift @vars, \%values ;
1238 0         0 $this->expand_keys(\%args_clash, \@vars) ;
1239              
1240             # set new values
1241 0         0 foreach my $key (keys %args_clash)
1242             {
1243 0         0 $args_values_href->{$key} = $args_clash{$key} ;
1244             }
1245             }
1246              
1247             ## update settings
1248 36         184 $opt->option_values_set($opt_values_href, $opt_defaults_href) ;
1249 36         156 $args->args_values_set($args_values_href) ;
1250              
1251 36         248 $this->_dispatch_exit_features() ;
1252              
1253 36         311 return $ok ;
1254             }
1255              
1256              
1257             #----------------------------------------------------------------------------
1258              
1259             =item B
1260              
1261             Set up before running the application.
1262              
1263             Calls the following methods in turn:
1264              
1265             * getopts
1266             * [internal _expand_vars method]
1267             * options
1268             * (Application registered 'app_start' function)
1269            
1270             =cut
1271              
1272              
1273             sub app_start
1274             {
1275 36     36 1 62 my $this = shift ;
1276              
1277 36         86 $this->_dispatch_entry_features() ;
1278              
1279             ## process the data
1280 36         191 $this->feature('data')->process() ;
1281            
1282             ## allow features to add their options
1283 36         747 my $features_aref = $this->feature_list() ;
1284 36         145 foreach my $feature (@$features_aref)
1285             {
1286 166         337 my $feature_obj = $this->feature($feature) ;
1287 166         3369 my $feature_options_aref = $feature_obj->feature_options() ;
1288 166 100       401 if (@$feature_options_aref)
1289             {
1290 76         158 $this->feature('Options')->append_options($feature_options_aref, $feature_obj->class) ;
1291             }
1292             }
1293              
1294             ## Add user-defined options last
1295 36         122 $this->feature('Data')->append_user_options() ;
1296              
1297              
1298             ## Get options
1299             # NOTE: Need to do this here so that derived objects work properly
1300 36         364 my $ret = $this->getopts() ;
1301            
1302             ## Expand any variables in the data
1303 36         275 $this->_expand_vars() ;
1304              
1305             # Handle options errors here after expanding variables
1306 36 50       114 unless ($ret)
1307             {
1308 0         0 $this->usage('opt') ;
1309 0         0 $this->exit(1) ;
1310             }
1311              
1312             # get options
1313 36         102 my %options = $this->options() ;
1314            
1315             ## function
1316 36         324 $this->_exec_fn('app_start', $this, \%options) ;
1317            
1318 36         2623 $this->_dispatch_exit_features() ;
1319            
1320             }
1321              
1322              
1323             #----------------------------------------------------------------------------
1324              
1325             =item B
1326              
1327             Execute the application.
1328            
1329             Calls the following methods in turn:
1330              
1331             * (Application registered 'app' function)
1332            
1333              
1334             =cut
1335              
1336              
1337             sub application
1338             {
1339 36     36 1 62 my $this = shift ;
1340              
1341 36         105 $this->_dispatch_entry_features() ;
1342              
1343             ## Execute function
1344 34         94 my %options = $this->options() ;
1345              
1346             ## Check args here (do this AFTER allowing derived objects/features a chance to check the options etc)
1347 34         136 $this->feature("Args")->check_args() ;
1348            
1349             # get args
1350 28         94 my %args = $this->feature("Args")->arg_hash() ;
1351              
1352             ## Run application function
1353 28         119 $this->_exec_fn('app', $this, \%options, \%args) ;
1354              
1355             ## Close any open arguments
1356 28         1201830 $this->feature("Args")->close_args() ;
1357            
1358              
1359 28         102 $this->_dispatch_exit_features() ;
1360              
1361             }
1362              
1363             #----------------------------------------------------------------------------
1364              
1365             =item B
1366              
1367             Tidy up after the application.
1368              
1369             Calls the following methods in turn:
1370              
1371             * (Application registered 'app_end' function)
1372            
1373              
1374             =cut
1375              
1376              
1377             sub app_end
1378             {
1379 28     28 1 56 my $this = shift ;
1380              
1381 28         91 $this->_dispatch_entry_features() ;
1382              
1383             # get options
1384 28         469 my %options = $this->options() ;
1385              
1386             ## Execute function
1387 28         366 $this->_exec_fn('app_end', $this, \%options) ;
1388              
1389 28         3098 $this->_dispatch_exit_features() ;
1390             }
1391              
1392              
1393              
1394             #----------------------------------------------------------------------------
1395              
1396             =item B
1397              
1398             Exit the application.
1399            
1400             =cut
1401              
1402              
1403             sub exit
1404             {
1405 0     0 1 0 my $this = shift ;
1406 0         0 my ($exit_code) = @_ ;
1407              
1408 0         0 die "Expected generic exit to be overridden: exit code=$exit_code" ;
1409             }
1410              
1411             #----------------------------------------------------------------------------
1412              
1413             =item B
1414              
1415             Show usage
1416              
1417             =cut
1418              
1419             sub usage
1420             {
1421 8     8 1 19 my $this = shift ;
1422 8         11 my ($level) = @_ ;
1423              
1424 8         24 $this->_dispatch_entry_features($level) ;
1425 8         21 $this->_exec_fn('usage', $this, $level) ;
1426 8         655 $this->_dispatch_exit_features($level) ;
1427              
1428             }
1429              
1430             #= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
1431              
1432             =back
1433              
1434             =head3 Utility methods
1435              
1436             =over 4
1437              
1438             =cut
1439              
1440              
1441              
1442              
1443              
1444             #----------------------------------------------------------------------------
1445              
1446             =item B
1447              
1448             Utility method
1449              
1450             Parses the filename and returns the full path, basename, and extension.
1451              
1452             Effectively does:
1453              
1454             $fname = File::Spec->rel2abs($fname) ;
1455             ($path, $base, $ext) = fileparse($fname, '\.[^\.]+') ;
1456             return ($path, $base, $ext) ;
1457              
1458             =cut
1459              
1460             sub file_split
1461             {
1462 0     0 1 0 my $this = shift ;
1463 0         0 my ($fname) = @_ ;
1464              
1465 0         0 $fname = File::Spec->rel2abs($fname) ;
1466 0         0 my ($path, $base, $ext) = fileparse($fname, '\.[^\.]+') ;
1467 0         0 return ($path, $base, $ext) ;
1468             }
1469              
1470              
1471             ## ============================================================================================
1472             #
1473             #=back
1474             #
1475             #=head2 PRIVATE METHODS
1476             #
1477             #=over 4
1478             #
1479             #=cut
1480             #
1481             ## ============================================================================================
1482              
1483              
1484             #----------------------------------------------------------------------------
1485             #
1486             #=item B<_exec_fn($function, @args)>
1487             #
1488             #Execute the registered function (if one is registered). Passes @args to the function.
1489             #
1490             #=cut
1491             #
1492             sub _exec_fn
1493             {
1494 100     100   122 my $this = shift ;
1495 100         182 my ($fn, @args) = @_ ;
1496              
1497             # Append _fn to function name, get the function, and call it if it's defined
1498 100         180 my $fn_name = "${fn}_fn" ;
1499 100   100     1963 my $sub = $this->$fn_name() || '' ;
1500              
1501 100         601 $this->_dbg_prt(["_exec_fn($fn) this=$this fn=$fn_name sub=$sub\n"], 2) ;
1502             #$this->prt_data("_exec_fn($fn) args[1]=", \$args[1], "args[2]=",\$args[2]) ;
1503             #if $this->debug()>=2 ;
1504              
1505 100 100       435 &$sub(@args) if $sub ;
1506             }
1507              
1508             #----------------------------------------------------------------------------
1509             #
1510             #=item B<_import()>
1511             #
1512             #Load modules into caller package namespace.
1513             #
1514             #=cut
1515             #
1516             sub _import
1517             {
1518 26     26   51 my $this = shift ;
1519              
1520 26         538 my $package = $this->package() ;
1521            
1522             # Debug
1523 26 50       97 if ($this->debug())
1524             {
1525 0 0       0 unless ($package eq 'main')
1526             {
1527 0         0 print "\n $package symbols:\n"; dumpvar($package) ;
  0         0  
1528             }
1529             }
1530              
1531             ## Load useful modules into caller package
1532 26         43 my $code ;
1533            
1534             # Set of useful modules
1535 26         72 foreach my $mod (@App::Framework::Settings::MODULES)
1536             {
1537 286         360 $code .= "use $mod;" ;
1538             }
1539            
1540             # Get modules into this namespace
1541 26         59 foreach my $mod (@App::Framework::Settings::MODULES)
1542             {
1543 26     26   161 eval "use $mod;" ;
  26     26   27  
  26     26   1179  
  26     26   418  
  25     26   31  
  25     26   1007  
  26     26   284  
  25     26   30  
  25     26   845  
  26     26   97  
  26     26   36  
  26         1514  
  26         381  
  25         31  
  25         312  
  26         303  
  25         31  
  25         1638  
  26         106  
  26         29  
  26         835  
  26         15414  
  25         36238  
  25         124  
  26         289  
  25         29  
  25         1761  
  26         10281  
  26         2428640  
  26         3855  
  26         19418  
  25         194070  
  25         103  
  286         14424  
1544 286 50       5277 if ($@)
1545             {
1546 0         0 warn "Unable to load module $mod\n" ;
1547             }
1548             else
1549             {
1550 286         715 ++$LOADED_MODULES{$mod} ;
1551             }
1552             }
1553              
1554             # Get modules into caller package namespace
1555 26     26   304 eval "package $package;\n$code\n" ;
  25     26   31  
  25     25   1249  
  26     25   111  
  26     25   32  
  26     25   1223  
  25     25   102  
  25     25   35  
  25     25   938  
  25     25   94  
  25     25   34  
  25         1341  
  25         92  
  25         33  
  25         429  
  25         89  
  25         29  
  25         912  
  25         118  
  25         34  
  25         899  
  25         89  
  25         33  
  25         193  
  25         1218  
  25         36  
  25         2023  
  25         106  
  25         34  
  25         2755  
  25         95  
  25         37  
  25         81  
  26         2034  
1556             # if ($@)
1557             # {
1558             # warn "Unable to load modules : $@\n" ;
1559             # }
1560             }
1561              
1562              
1563             #----------------------------------------------------------------------------
1564             #
1565             #=item B<_register_fn()>
1566             #
1567             #Register a function provided as a subroutine in the caller package as an app method
1568             #in this object.
1569             #
1570             #Will only set the field value if it's not already set.
1571             #
1572             #=cut
1573             #
1574             sub _register_fn
1575             {
1576 260     260   208 my $this = shift ;
1577 260         223 my ($function, $alias) = @_ ;
1578            
1579 260   33     353 $alias ||= $function ;
1580 260         279 my $field ="${alias}_fn" ;
1581              
1582 260 100       4956 $this->_register_var('CODE', $function, $field) unless $this->$field() ;
1583             }
1584              
1585             #----------------------------------------------------------------------------
1586             #
1587             #=item B<_register_scalar($external_name, $field_name)>
1588             #
1589             #Read the value of a variable in the caller package and copy that value as a data field
1590             #in this object.
1591             #
1592             #Will only set the field value if it's not already set.
1593             #
1594             #=cut
1595             #
1596             sub _register_scalar
1597             {
1598 26     26   51 my $this = shift ;
1599 26         60 my ($external_name, $field_name) = @_ ;
1600            
1601 26 50       569 $this->_register_var('SCALAR', $external_name, $field_name) unless $this->$field_name() ;
1602             }
1603              
1604             #----------------------------------------------------------------------------
1605             #
1606             #=item B<_register_var($type, $external_name, $field_name)>
1607             #
1608             #Read the value of a variable in the caller package and copy that value as a data field
1609             #in this object. $type specifies the variable type: 'SCALAR', 'ARRAY', 'HASH', 'CODE'
1610             #
1611             #NOTE: This method overwrites the field value irrespective of whether it's already set.
1612             #
1613             #=cut
1614             #
1615             sub _register_var
1616             {
1617 265     265   229 my $this = shift ;
1618 265         288 my ($type, $external_name, $field_name) = @_ ;
1619              
1620 265         4480 my $package = $this->package() ;
1621              
1622 265         415 local (*alias); # a local typeglob
1623              
1624 265         928 $this->_dbg_prt(["_register_var($type, $external_name, $field_name)\n"], 2) ;
1625              
1626             # We want to get access to the stash corresponding to the package
1627             # name
1628 30     30   169 no strict "vars" ;
  30         46  
  30         783  
1629 30     30   98 no strict "refs" ;
  30         43  
  30         7394  
1630 265         269 *stash = *{"${package}::"}; # Now %stash is the symbol table
  265         463  
1631              
1632 265 100       679 if (exists($stash{$external_name}))
1633             {
1634 63         171 *alias = $stash{$external_name} ;
1635              
1636 63         292 $this->_dbg_prt([" + found $external_name in $package\n"], 2) ;
1637              
1638 63 100       165 if ($type eq 'SCALAR')
1639             {
1640 26 100       84 if (defined($alias))
1641             {
1642 21         117 $this->set($field_name => $alias) ;
1643             }
1644             }
1645 63 50       141 if ($type eq 'ARRAY')
1646             {
1647             # was - if (defined(@alias)) - removed due to "deprecated" warning
1648 0 0       0 if (@alias)
1649             {
1650 0         0 $this->set($field_name => \@alias) ;
1651             }
1652             }
1653 63 50       236 if ($type eq 'HASH')
    100          
1654             {
1655 0 0       0 if (%alias)
1656             {
1657 0         0 $this->set($field_name => \%alias) ;
1658             }
1659             }
1660             elsif ($type eq 'CODE')
1661             {
1662 37 50       103 if (defined(&alias))
1663             {
1664 37         165 $this->_dbg_prt([" + + Set $type - $external_name as $field_name\n"], 2) ;
1665 37         168 $this->set($field_name => \&alias) ;
1666             }
1667             }
1668              
1669             }
1670             }
1671              
1672              
1673             #----------------------------------------------------------------------------
1674             #
1675             #=item B<_expand_vars()>
1676             #
1677             #Run through some of the application variables/fields and expand any instances of variables embedded
1678             #within the values.
1679             #
1680             #Example:
1681             #
1682             # __DATA_
1683             #
1684             # [SYNOPSIS]
1685             #
1686             # $name [options]
1687             #
1688             #Here the 'synopsis' field contains the $name field variable. This needs to be expanded to the value of $name.
1689             #
1690             #NOTE: Currently this will NOT cope with cross references (so, if in the above example $name also contains a variable
1691             #then that variable may or may not be expanded before the synopsis field is processed)
1692             #
1693             #
1694             #=cut
1695             #
1696             sub _expand_vars
1697             {
1698 36     36   60 my $this = shift ;
1699              
1700 36         146 $this->_dbg_prt(["_expand_vars() - START\n"], 2) ;
1701              
1702             # Get hash of fields
1703 36         118 my %fields = $this->vars() ;
1704              
1705             #$this->_dbg_prt([" + fields=", \%fields], 2) ;
1706            
1707             # work through each field, create a list of those that have changed
1708 36         128 my %changed ;
1709 36         497 foreach my $field (sort keys %fields)
1710             {
1711             # Skip non-scalars
1712 1218 100       1542 next if ref($fields{$field}) ;
1713            
1714             # First see if this contains a '$'
1715 717   100     1199 $fields{$field} ||= "" ;
1716 717         568 my $ix = index $fields{$field}, '$' ;
1717 717 100       982 if ($ix >= 0)
1718             {
1719 16         116 $this->_dbg_prt([" + + $field = $fields{$field} : index=$ix\n"], 3) ;
1720              
1721             # Do replacement
1722 16         110 $fields{$field} =~ s{
1723             \$ # find a literal dollar sign
1724             \{{0,1} # optional brace
1725             (\w+) # find a "word" and store it in $1
1726             \}{0,1} # optional brace
1727             }{
1728 30     30   136 no strict 'refs'; # for $$1 below
  30         39  
  30         5884  
1729 16 50       59 if (defined $fields{$1}) {
1730 16         59 $fields{$1}; # expand global variables only
1731             } else {
1732 0         0 "\${$1}"; # leave it
1733             }
1734             }egx;
1735              
1736              
1737 16         88 $this->_dbg_prt([" + + + new = $fields{$field}\n"], 3) ;
1738            
1739             # Add to list
1740 16         39 $changed{$field} = $fields{$field} ;
1741             }
1742             }
1743              
1744 36         234 $this->_dbg_prt([" + changed=", \%changed], 2) ;
1745            
1746             # If some have changed then set them
1747 36 100       126 if (keys %changed)
1748             {
1749 16         64 $this->_dbg_prt([" + + set changed\n"], 2) ;
1750 16         75 $this->set(%changed) ;
1751             }
1752              
1753 36         139 $this->_dbg_prt(["_expand_vars() - END\n"], 2) ;
1754             }
1755              
1756              
1757              
1758             #----------------------------------------------------------------------------
1759              
1760             =item B
1761              
1762             Print out the items in the $items_aref ARRAY ref iff the application's debug level is >0.
1763             If $min_debug is specified, will only print out items if the application's debug level is >= $min_debug.
1764              
1765             =cut
1766              
1767             sub debug_prt
1768             {
1769 0     0 1   my $this = shift ;
1770 0           my ($items_aref, $min_debug) = @_ ;
1771              
1772 0   0       $min_debug ||= 1 ;
1773            
1774             ## check debug level setting
1775 0 0         if ($this->options->option('debug') >= $min_debug)
1776             {
1777 0           $this->prt_data(@$items_aref) ;
1778             }
1779             }
1780              
1781              
1782              
1783             # ============================================================================================
1784             # PRIVATE FUNCTIONS
1785             # ============================================================================================
1786              
1787             #----------------------------------------------------------------------------
1788             #
1789             #=item B<_dumpisa(package)>
1790             #
1791             #Starting at I, show the parents
1792             #
1793             #=cut
1794             #
1795             sub _dumpisa
1796             {
1797 30     30   128 no strict "vars" ;
  30         37  
  30         735  
1798 30     30   131 no strict "refs" ;
  30         37  
  30         3256  
1799              
1800 0     0     my ($packageName, $level) = @_;
1801            
1802            
1803 0 0         if (!defined($level))
1804             {
1805 0           print "#### PACKAGE: $packageName ISA HIERARCHY ###########################\n" ;
1806             }
1807             else
1808             {
1809 0           print " "x$level ;
1810 0           print "$packageName\n" ;
1811             }
1812            
1813 0           foreach my $isa (@{"${packageName}::ISA"})
  0            
1814             {
1815 0           _dumpisa($isa, ++$level) ;
1816             }
1817            
1818            
1819 0 0         if (!defined($level))
1820             {
1821 0           print "######################################################\n" ;
1822             }
1823             }
1824              
1825             #----------------------------------------------------------------------------
1826             #
1827             #=item B<_dumpvar(package)>
1828             #
1829             #Dump out all of the symbols in package I
1830             #
1831             #=cut
1832             #
1833             sub _dumpvar
1834             {
1835 30     30   120 no strict "vars" ;
  30         34  
  30         761  
1836 30     30   100 no strict "refs" ;
  30         30  
  30         5610  
1837              
1838 0     0     my ($packageName) = @_;
1839            
1840 0           print "#### PACKAGE: $packageName ###########################\n" ;
1841            
1842 0           local (*alias); # a local typeglob
1843             # We want to get access to the stash corresponding to the package
1844             # name
1845 0           *stash = *{"${packageName}::"}; # Now %stash is the symbol table
  0            
1846 0           $, = " "; # Output separator for print
1847             # Iterate through the symbol table, which contains glob values
1848             # indexed by symbol names.
1849 0           while (($varName, $globValue) = each %stash) {
1850 0           print "$varName ============================= \n";
1851 0           *alias = $globValue;
1852 0 0         if (defined ($alias)) {
1853 0           print "\t \$$varName $alias \n";
1854             }
1855 0 0         if (@alias) {
1856 0           print "\t \@$varName @alias \n";
1857             }
1858 0 0         if (%alias) {
1859 0           print "\t \%$varName ",%alias," \n";
1860             }
1861 0 0         if (defined (&alias)) {
1862 0           print "\t \&$varName \n";
1863             }
1864             }
1865            
1866 0           print "######################################################\n" ;
1867            
1868             }
1869              
1870              
1871             # ============================================================================================
1872             # END OF PACKAGE
1873              
1874             =back
1875              
1876             =head1 DIAGNOSTICS
1877              
1878             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
1879              
1880             =head1 AUTHOR
1881              
1882             Steve Price C<< >>
1883              
1884             =head1 BUGS
1885              
1886             None that I know of!
1887              
1888             =cut
1889              
1890              
1891              
1892             1;
1893              
1894             __END__