File Coverage

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