File Coverage

blib/lib/App/Framework.pm
Criterion Covered Total %
statement 78 131 59.5
branch 19 48 39.5
condition 12 25 48.0
subroutine 10 12 83.3
pod 2 2 100.0
total 121 218 55.5


line stmt bran cond sub pod time code
1             package App::Framework;
2              
3             =head1 NAME
4              
5             App::Framework - A framework for creating applications
6              
7             =head1 SYNOPSIS
8              
9             use App::Framework ;
10            
11             App::Framework->new()->go() ;
12            
13             sub app
14             {
15             my ($app, $opts_href, $args_href) = @_ ;
16            
17             # options
18             my %opts = $app->options() ;
19            
20             # aplication code here....
21             }
22              
23              
24             =head1 DESCRIPTION
25              
26             App::Framework is a framework for quickly developing application scripts, where the majority of the mundane script setup,
27             documentation etc. jobs are performed by the framework (usually under direction from simple text definitions stored in the script).
28              
29             This leaves the developer to concentrate on the main job of implementing the application.
30              
31             To jump straight in to developing applications, please see L.
32              
33             =head2 Capabilities
34              
35             The application framework provides the following capabilities:
36              
37             =over 2
38              
39             =item Options definition
40              
41             Text definition of options in application, providing command line options, help pages, options checking.
42              
43             Also supports variables in options definition, the variables being replaced by other option values, application field values,
44             or environment variables.
45              
46             =item Arguments definition
47              
48             Text definition of arguments in application, providing command line arguments, help pages, arguments checking, file/directory
49             creation, file/directory existence, file opening
50              
51             Also supports variables in arguments definition, the variables being replaced by other argument values, option values, application field values,
52             or environment variables.
53              
54             =item Named data sections
55              
56             Multiple named __DATA__ sections, the data being readily accessible by name from the application.
57              
58             Variables can be used in the data definitions, the variables being replaced by command line option values, application field values,
59             or environment variables.
60              
61             =item Personalities
62              
63             Single line selection of the base application type (i.e. command line script, Tk application, POE application etc).
64              
65             Modular application framework allows for separate installation of new personalities in the installed Perl library space, or locally under
66             an application-specific directory.
67              
68             =item Extensions
69              
70             Single line selection of one or more application extension plugins which modify the selected personality behaviour.
71              
72             Modular application framework allows for separate installation of new extensions in the installed Perl library space, or locally under
73             an application-specific directory.
74              
75             Example extensions (may not be installed on your system):
76              
77             =over 4
78              
79             =item Daemon
80              
81             Selecting this extension converts the command line script into a daemon (see L)
82              
83             =item Filter
84              
85             Sets up the application for file filtering, the framework doing most of the work in the background (see L).
86              
87             =item Find
88              
89             Sets up the application for file finding, the framework doing most of the work in the background
90              
91             =back
92              
93             =item Features
94              
95             Single line selection of one or more application feature plugins which provide application targetted functionality (for example Sql support,
96             mail handling etc).
97              
98             Modular application framework allows for separate installation of new features in the installed Perl library space, or locally under
99             an application-specific directory.
100              
101             Example features (may not be installed on your system):
102              
103             =over 4
104              
105             =item Config
106              
107             Provides the application with configuration file support. Automatically uses the configuration file for all command line option
108             settings (see L).
109              
110             =item Sql
111              
112             Provides a simplified interface to MySQL. Provides easy set up for Sql operations delete, update, select etc (see L).
113              
114             =item Mail
115              
116             Provides mail send support (including file attachment) (see L).
117              
118             =back
119              
120              
121             =item Application directories
122              
123             The framework automatically adds the location of the script (following any links) to the Perl search path. This means that perl modules
124             can be created in subdirectories under the application's script making the application self-contained.
125              
126             The directories used for loading personalities/extensions/features also include the script install directory, meaning that new personalities/extensions/features
127             can also be provided with a script.
128              
129             =back
130              
131              
132             =head2 Framework Components
133              
134             The diagram below shows the relationship between the application framework object (Framework) and the other components:
135              
136             +--------------+
137             | Core |
138             +--------------+
139             ^
140             |
141             |
142             +--------------+
143             | Personality | Script, POE etc
144             +--------------+
145             ^
146             |
147             |
148             ................
149             : Extension(s) :.. Filter, Daemon etc
150             ................ :
151             :...............
152             ^
153             |
154             |
155             +--------------+ +--------------+
156             | Framework |--------------->| Features |-+ Args, Options, Pod etc
157             +--------------+ +--------------+ |
158             +--------------+
159              
160              
161             =head3 Core and personalities
162              
163             An application is built by creating an App::Framework object that is derived from the application core, and also contains 0 or more feature
164             objects. The application core (L) is not directly deriveable, you actually derive from a "personality" module that provides
165             the base essentials for this selected type of application (for example 'Script' for a command line script).
166              
167             The personality is selected in the App::Framework 'use' command as:
168              
169             use App::Framework ':'
170              
171             For example:
172              
173             use App::Framework ':Script'
174              
175             Personalities add specific methods, options, arguments to the core application.
176              
177             All of the methods defined in the selected personality add to the core methods and are available to the application object ($app).
178              
179             (See L for your currently installed personalities)
180              
181              
182             =head3 Extensions
183              
184             When creating the App::Framework object, you can optionally select to derive it from one (or more) 'extensions'. An extension can modify how the
185             application routine is called, add extra command line options, and so on. For example, the 'filter' extension sets up the application
186             for file filtering (calling the aplication subroutine with each line of an input file so that the file contents may be filtered).
187              
188             Extensions are added in the App::Framework 'use' command as:
189              
190             use App::Framework '::'
191              
192             For example:
193              
194             use App::Framework '::Daemon ::Filter'
195              
196             (See L for your currently installed extensions)
197              
198             Like the personality, all of the methods defined in the selected extensions add to the core methods and are available to the application
199             object ($app).
200              
201             =head3 Features
202              
203             Features provide additional application capabilities, optional modifying what the application framework does depedning on the feature. A feature
204             may also simply be an application-specific collection of useful methods.
205              
206             Unlike core/personality/extension, features are not part of the application object - they are kept in a "feature list" that the application can
207             access to use a feature's methods. For convenience, all features provide an accessor method that is aliased as an application method
208             with the same name as the feature. This access method provides the most commonly used functionality for that feature. For example, the 'data'
209             feature provides access to named data sections as:
210              
211             my $data = $app->data('named_section') ;
212              
213             Alternatively, the data feature object can be retrieved and used:
214              
215             my $data_feature = $app->feature('data') ;
216             my $data = $data_feature->data('named_section') ;
217              
218             Features are added in the App::Framework 'use' command as:
219              
220             use App::Framework '+'
221              
222             For example:
223              
224             use App::Framework '+Args +Data +Mail +Config'
225              
226             (See L for your currently installed extensions)
227              
228              
229             =head2 Using This Module
230              
231             To create an application you need to declare: the personality to use, any optional extensions, and which features you wish to use.
232              
233             You do all this in the 'use' pragma for the module, for example:
234              
235             use App::Framework ':Script ::Filter +Mail +Config' ;
236              
237             By default, the 'Script' personality is assumed (and so need not be declared), and the framework ensures that all of the features it requires are always loaded (so you don't
238             need to declare +Args, +Options, +Data, +Pod, +Run). So, the minimum is:
239              
240             use App::Framework ;
241              
242             =head3 Creating Application Object
243              
244             There are two ways of creating an application object and running it. The normal way is:
245              
246             # Create application and run it
247             App::Framework->new()->go() ;
248              
249             As an alternative, the framework creates a subroutine in the calling namespace called B which does the same thing:
250              
251             # Create application and run it
252             go() ;
253              
254             You can use whatever takes your fancy. Either way, the application object will end up calling the user-defined application subroutines
255              
256              
257              
258             =head3 Application Subroutines
259              
260             Once the application object has been created it can then be run by calling the 'go()' method. go() calls the application's registered functions
261             in turn:
262              
263             =over 2
264              
265             =item * app_start()
266              
267             Called at the start of the application. You can use this for any additional set up (usually of more use to extension developers)
268              
269             =item * app()
270              
271             Called once all of the arguments and options have been processed
272              
273             =item * app_end()
274              
275             Called when B terminates or returns (usually of more use to extension developers)
276              
277             =back
278              
279             The framework looks for these 3 functions to be defined in the script file. The functions B and B are optional, but it is expected that B will be defined
280             (otherwise nothing happens!).
281              
282             =head3 Setup
283              
284             The application settings are entered into the __DATA__ section at the end of the file. All program settings are grouped under sections which are introduced by '[section]' style headings. There are many
285             different settings that can be set using this mechanism, but the framework sets most of them to useful defaults. The most common sections are described below.
286              
287             =head4 Summary
288              
289             This should be a single line, concise summary of what the script does. It's used in the terse man page created by pod2man.
290              
291             =head4 Description
292              
293             As you'd expect, this should be a full description, user-guide etc. on what the script does and how to do it. Notice that this example
294             has used one (of many) of the variables available: $name (which expands to the script name, without any path or extension).
295              
296             =head4 Options
297              
298             Command line options are defined in this section in the format:
299              
300             -=
301            
302            
303              
304             For example:
305              
306             -table|tbl|sql_table=s Table [default=listings2]
307              
308             For full details, see L.
309              
310             =head4 Arguments
311              
312             The command line arguments specification are similar to the options specification. In this case we use '*' to signify the
313             start of a new argument definition.
314              
315             Arguments are defined in the format:
316              
317             * =
318            
319            
320              
321             For full details, see L.
322              
323             =head4 Example
324              
325             An example script setup is:
326              
327             __DATA__
328            
329             [SUMMARY]
330            
331             An example of using the application framework
332            
333             [ARGS]
334            
335             * infile=f Input file
336            
337             Should be set to the input file
338            
339             * indir=d Input dir
340            
341             Should be set to the input dir
342            
343             [OPTIONS]
344            
345             -table=s Table [default=listings2]
346            
347             Sql table name
348            
349             -database=s Database [default=tvguide]
350            
351             Sql database name
352            
353            
354             [DESCRIPTION]
355            
356             B<$name> is an example script.
357              
358              
359              
360             =head3 Data
361              
362             After the settings (described above), one or more extra data areas can be created by starting that area with a new __DATA__ line.
363              
364             If the new data area is defined simply with '__DATA__' then the area is automatically named as 'data1', 'data2' etc. Alternatively, the
365             data section can be arbitrarily named by appending a text name after __DATA__. For example, the definition:
366              
367             __DATA__
368            
369             [DESCRIPTION]
370             An example
371            
372             __DATA__ test.txt
373            
374             some text
375            
376             __DATA__ a_bit_of_sql.sql
377            
378             DROP TABLE IF EXISTS `listings2`;
379            
380             Creates 2 extra data areas 'test.txt' and 'a_bit_of_sql.sql'. These data area contents can be accessed using:
381              
382             my $contents1 = $app->data('text.txt') ;
383             # or
384             $contents1 = $app->data('data1') ;
385              
386             my $contents2 = $app->data('a_bit_of_sql.sql') ;
387             # or
388             $contents2 = $app->data('data2') ;
389              
390              
391             See L for further details.
392              
393              
394             =head2 Directories
395              
396             The framework sets up various directory paths automatically, as described below.
397              
398             =head3 @INC path
399              
400             App::Framework automatically pushes some extra directories at the start of the Perl include library path. This allows you to 'use' application-specific
401             modules without having to install them globally on a system. The path of the executing Perl application is found by following any links until
402             an actually Perl file is found. The @INC array has the following added:
403              
404             * $progpath
405             * $progpath/lib
406            
407             i.e. The directory that the script resides in, and a sub-directory 'lib' will be searched for application-specific modules.
408              
409             Note that this is the path also used when the framework loads in the core personality, and any optional extensions.
410              
411             =head3 Feature modules
412              
413             When the application framework loads in the various required and user-specified features, then it attempts to load the following feature modules until one is sucessfully loaded:
414              
415             * App::Framework::Feature::${personality}::${feature}
416             * App::Framework::Feature::${feature}
417              
418             Where ${feature} is the name of the feature being loaded (e.g. Config), and ${personality} is the specified core personality (e.g. Script). Note that it does this using the L, so
419             an application can bundle it's own feature's in under it's own install directory.
420              
421              
422             =head2 Settings
423              
424             App::Framework loads some settings from L. This may be modified on a site basis as required
425             (in a similar fashion to CPAN Config.pm).
426              
427              
428             =head2 Loaded modules
429              
430             App::Framework pre-loads the user namespace with some common modules. See L for the complete list.
431              
432            
433              
434             =head2 FIELDS
435              
436             The following fields should be defined either in the call to 'new()' or as part of the application configuration in the __DATA__ section:
437              
438             * name = Program name (default is name of program)
439             * summary = Program summary text
440             * synopsis = Synopsis text (default is program name and usage)
441             * description = Program description text
442             * history = Release history information
443             * version = Program version (default is value of 'our $VERSION')
444              
445             * app_start_fn = Function called before app() function (default is application-defined 'app_start' subroutine if available)
446             * app_fn = Function called to execute program (default is application-defined 'app' subroutine if available)
447             * app_end_fn = Function called after app() function (default is application-defined 'app_end' subroutine if available)
448             * usage_fn = Function called to display usage information (default is application-defined 'usage' subroutine if available)
449              
450             During program execution, the following values can be accessed:
451              
452             * package = Name of the application package (usually main::)
453             * filename = Full filename path to the application (after following any links)
454             * progname = Name of the program (without path or extension)
455             * progpath = Pathname to program
456             * progext = Extension of program
457              
458             =cut
459              
460 29     29   419487 use 5.008004;
  29         71  
461              
462 29     29   103 use strict ;
  29         38  
  29         490  
463 29     29   85 use Carp ;
  29         31  
  29         1786  
464              
465 29     29   13113 use App::Framework::Core ;
  29         54  
  29         3134  
466              
467              
468             our $VERSION = "1.07" ;
469              
470              
471             #============================================================================================
472             # OBJECT HIERARCHY
473             #============================================================================================
474             our @ISA ;
475              
476             #============================================================================================
477             # GLOBALS
478             #============================================================================================
479              
480             our $class_debug = 0 ;
481              
482             # Keep track of import info
483             my $import_args ;
484              
485              
486             #============================================================================================
487              
488             =head2 METHODS
489              
490             =over 4
491              
492             =cut
493              
494              
495             #============================================================================================
496              
497             # Set up module import
498             sub import
499             {
500 28     28   214 my $pkg = shift;
501            
502 28         72 $import_args = join ' ', @_ ;
503            
504             ## Set library paths
505 28         131 my ($package, $filename, $line, $subr, $has_args, $wantarray) = caller(0) ;
506 28         102 App::Framework::Core->set_paths($filename) ;
507              
508             ## Add a couple of useful function calls into the caller namespace
509             {
510 29     29   127 no warnings 'redefine';
  29         31  
  29         899  
  28         35  
511 29     29   89 no strict 'refs';
  29         27  
  29         32131  
512              
513 28         44 foreach my $fn (qw/go modpod/)
514             {
515 56         36115 *{"${package}::$fn"} = sub {
516 6     6   4392 my @callinfo = caller(0);
517 6         47 my $app = App::Framework->new(@_,
518             '_caller_info' => \@callinfo) ;
519 6         59 $app->$fn() ;
520 56         174 };
521             }
522             }
523            
524             }
525              
526             #----------------------------------------------------------------------------------------------
527              
528             =item B< new([%args]) >
529              
530             Create a new object.
531              
532             The %args passed down to the parent objects.
533              
534             The parameters are specific to App::Framework:
535              
536             =over 4
537              
538             =item B - Application definition
539              
540             Instead of specifying the application in the 'use App::Framework' line, you can just specify them in this
541             argument when creating the object. If this is specified it will overwrite any specification in the 'use' pragma.
542              
543             =back
544              
545              
546             =cut
547              
548             sub new
549             {
550 26     26 1 50170 my ($obj, %args) = @_ ;
551              
552 26   33     176 my $class = ref($obj) || $obj ;
553              
554 26         159 my @callinfo = caller(0);
555 26   100     148 $args{'_caller_info'} ||= \@callinfo ;
556              
557 26 50       83 print __PACKAGE__."->new() : caller=$args{'_caller_info'}->[0]\n" if $class_debug ;
558              
559 26 50       80 if (exists($args{'specification'}))
560             {
561 0         0 $import_args = delete $args{'specification'} ;
562             }
563              
564              
565              
566             ## Process the import command args
567 26         41 my $personality ;
568             my @features ;
569 0         0 my @extensions ;
570 0         0 my %extension_args ;
571 0         0 my %feature_args ;
572 26   100     105 $import_args ||= ':Script +run' ;
573            
574             # Expect something of the form:
575             # :Personality ::Extension ::Ext(option1 option2) +Feature +Feat(opt1, opt2)
576             #
577             # type name args
578 26         223 while ($import_args =~ /\s*([\:\+]{1,2})([\w_]+)\s*(?:\(([^\)]+)\)){0,1}/g)
579             {
580 45         157 my ($type, $name, $args) = ($1, $2, $3) ;
581 45 100       192 if ($type eq ':')
    100          
    50          
582             {
583 18 50       47 if ($personality)
584             {
585 0         0 croak "Sorry, App::Framework does not support multiple personalities (please see a psychiatrist!)" ;
586             }
587 18 50       53 if ($args)
588             {
589 0         0 warn "Sorry, personalities do not support arguments" ;
590             }
591 18         80 $personality = $name ;
592             }
593             elsif ($type eq '::')
594             {
595 1         2 push @extensions, $name ;
596 1   50     6 $extension_args{$name} = $args || "" ;
597             }
598             elsif ($type eq '+')
599             {
600 26         50 push @features, $name ;
601 26   100     166 $feature_args{$name} = $args || "" ;
602             }
603             else
604             {
605 0         0 croak "App::Framework does not understand the import string \"$import_args\" at \"$type\" " ;
606             }
607             }
608              
609             ## sort extension list
610 26         42 my @extension_modules ;
611             my %extensions ;
612 26         60 foreach my $extension (@extensions)
613             {
614 1         2 my $module = "App::Framework::Extension::$extension" ;
615              
616 1 50       3 print "Extension $extension - module $module\n" if $class_debug ;
617              
618             # only allow 1 instance of each extension
619 1 50       2 if (!exists($extensions{$module}))
620             {
621 1 50       8 if (App::Framework::Core->dynamic_load($module, __PACKAGE__))
622             {
623 1 50       5 print " + loaded\n" if $class_debug ;
624            
625 1         1 my $priority ;
626 1         40 eval "\$priority = \$${module}::PRIORITY ;" ;
627 1 0 33     4 print " + $@\n" if $@ && $class_debug ;
628            
629 1   33     6 $priority ||= $App::Framework::Base::PRIORITY_DEFAULT ;
630            
631 1 50       3 print " + priority=$priority\n" if $class_debug ;
632 1         3 push @extension_modules, [$extension, $module, $priority] ;
633             }
634             else
635             {
636 0         0 croak "App::Framework cannot load extension \"$extension\" " ;
637             }
638             }
639 1         3 $extensions{$module} = 1 ;
640             }
641 26         64 @extension_modules = sort { $a->[2] <=> $b->[2] } @extension_modules ;
  0         0  
642 26         57 my @modules = map { $_->[1] } @extension_modules ;
  1         2  
643            
644             # extensions are based from App::Framework::Extension
645 26         54 push @modules, 'App::Framework::Extension' ;
646              
647 26 50       70 if ($class_debug)
648             {
649 0         0 print "Import: $import_args\n" ;
650 0         0 print "features: @features\n" ;
651 0         0 print "Extensions: @extensions\n" ;
652            
653 0         0 print "Extension Modules: @modules\n" ;
654             }
655              
656             ## load module
657 26   100     82 $personality ||= 'Script' ;
658 26         74 my $module = "App::Framework::Core::$personality" ;
659 26         38 push @modules, $module ;
660              
661 26 50       76 print "Framework Inheritence Modules:\n\t". join("\n\t",@modules)."\n" if $class_debug ;
662              
663              
664 26         39 $module = shift @modules ;
665            
666 26         203 my $loaded = App::Framework::Core->dynamic_isa($module, __PACKAGE__) ;
667 26 50       72 croak "Sorry, App::Framework does not support \"$module\"" unless $loaded ;
668              
669             # Create object
670             my $this = $class->SUPER::new(
671             %args,
672 26         239 '_caller_info' => $args{'_caller_info'},
673             '_inheritence' => \@modules,
674            
675             ## Pass down extra information
676             'personality' => $personality,
677             'extensions' => \@extensions,
678             ) ;
679             $this->set(
680 8     8   79 'usage_fn' => sub {$this->script_usage(@_);},
681 26         239 ) ;
682              
683             ## Load features
684 26 100       102 if (@features)
685             {
686             ## Install them
687 25         222 $this->install_features(\@features, \%feature_args) ;
688             }
689              
690              
691 26         793 return($this) ;
692             }
693              
694             #----------------------------------------------------------------------------------------------
695              
696             =item B< modpod() >
697              
698             Create/update module pod files. Creates/updates the pod for the module lists:
699             L,L,L
700              
701             Used during installation.
702              
703             =cut
704              
705             sub modpod
706             {
707 0     0 1   my $this = shift ;
708              
709 0           foreach my $name (qw/Core Extension Feature/)
710             {
711 0           my $podfile = "App/Framework/${name}Modules.pod" ;
712 0           my %modules = App::Framework::Core->lib_glob("App/Framework/$name") ;
713 0           my $template = $this->_template($name) ;
714              
715 0           print "$podfile ...\n" ;
716            
717 0           my @list ;
718 0           foreach my $module (sort keys %modules)
719             {
720 0 0         if ( open my $fh, "<$modules{$module}" )
721             {
722 0           my ($summary, $version, $line) ;
723 0           my $modname = "App::Framework::${name}::${module}" ;
724 0   0       while ( !($summary && $version) && defined($line = <$fh>) )
      0        
725             {
726 0           chomp $line ;
727              
728             # App::Framework::Feature::Args - Handle application command line arguments
729 0 0         if ($line =~ m/$modname\s*\-\s*(\S.*)/)
730             {
731 0           $summary = $1 ;
732             }
733              
734             # our $VERSION = "1.000" ;
735 0 0         if ($line =~ m/(?:our|my)\s+\$VERSION\s*=\s*["']([\d\.]+)["']/)
736             {
737 0           $version = $1 ;
738             }
739             }
740 0           close $fh ;
741            
742 0 0         if ($summary)
743             {
744 0           print " $modname\n" ;
745             push @list, {
746             'module' => $modname,
747 0           'file' => $modules{$module},
748             'summary' => $summary,
749             'version' => $version,
750             }
751             }
752             }
753             }
754            
755             ## Write file
756 0           my $blib_pod = "blib/lib/$podfile" ;
757 0 0         if (-f $blib_pod)
758             {
759 0           chmod 0755, $blib_pod ;
760             }
761 0 0         if (open my $fh, ">$blib_pod")
762             {
763 0           my $list ;
764 0           foreach my $href (@list)
765             {
766 0 0         my $version = $href->{version} ? "v$href->{version}" : "" ;
767 0           $list .= "=item * L<$href->{module}> $version\n\n" ;
768 0           $list .= "$href->{summary}\n\n" ;
769             }
770 0           $template =~ s//$list/m ;
771              
772 0           print $fh $template ;
773            
774 0           close $fh ;
775             }
776             else
777             {
778 0           die "Error: unable to write pod file $blib_pod : $!" ;
779             }
780             }
781              
782             }
783              
784              
785              
786             #============================================================================================
787             # PRIVATE
788             #============================================================================================
789              
790              
791             ##----------------------------------------------------------------------------------------------
792             ## Create a new App::Framework object, then call the specified method
793             #sub _new_and_call
794             #{
795             # my $class = shift ;
796             # my ($method, %args) = @_ ;
797             # my $this = new(%args) ;
798             # $this->$method(%args) ;
799             #}
800              
801             #----------------------------------------------------------------------------------------------
802             # Returns the pod file template for this named file
803             sub _template
804             {
805 0     0     my $class = shift ;
806 0           my ($name) = @_ ;
807 0           my $template ;
808              
809 0           my $eq = '=' ;
810 0           $template = <