App-Framework-Lite

 view release on metacpan or  search on metacpan

lib/App/Framework/Lite.pm  view on Meta::CPAN

package App::Framework::Lite ;

=head1 NAME

App::Framework::Lite - A lightweight framework for creating applications

=head1 SYNOPSIS

  use App::Framework::Lite ;
  
  go() ;
  
  sub app
  {
	my ($app, $opts_href, $args_href) = @_ ;
	
	# options
	my %opts = $app->options() ;
    
	# aplication code here....  	
  }


=head1 DESCRIPTION

App::Framework::Lite is a framework for quickly developing application scripts, where the majority of the mundane script setup,
documentation jobs are performed by the framework (under direction from simple text definitions stored in the script). This leaves 
the developer to concentrate on the main job of implementing the application.

The module also provides the facility of embedding itself into a copy of the original script, creating a self-contained stand-alone
script (for further details see L</EMBEDDING>).

Note that this module provides a subset of the the facilities provided by L<App::Framework>, In particular, it provides the L<App::Framework::Features:Args>,   
L<App::Framework::Features:Options>, and L<App::Framework::Features:Data> features.

To jump straight in to developing applications, please see L<App::Framework::Lite::GetStarted>.

=head2 Capabilities

The application framework provides the following capabilities: 

=over 2

=item Options definition

Text definition of options in application, providing command line options, help pages, options checking. 

Also supports variables in options definition, the variables being replaced by other option values, application field values, 
or environment variables.

=item Arguments definition

Text definition of arguments in application, providing command line arguments, help pages, arguments checking, file/directory
creation, file/directory existence, file opening

Also supports variables in arguments definition, the variables being replaced by other argument values, option values, application field values, 
or environment variables.

=item Named data sections

Multiple named __DATA__ sections, the data being readily accessible by name from the application.

Variables can be used in the data definitions, the variables being replaced by command line option values, application field values, 
or environment variables.


=item Application directories

The framework automatically adds the location of the script (following any links) to the Perl search path. This means that perl modules
can be created in subdirectories under the application's script making the application self-contained.

The directories used for loading personalities/extensions/features also include the script install directory, meaning that new personalities/extensions/features
can also be provided with a script. 

=back


=head2 Using This Module 

The minimum you need is:

    use App::Framework::Lite ;

Optionally, you can specify arguments to the underlying features by appending a string to the 'use' pragma. For exanmple:

    use App::Framework::Lite '+Args(open=none)' ;


=head3 Creating Application Object

There are two ways of creating an application object and running it. The normal way is:

lib/App/Framework/Lite.pm  view on Meta::CPAN

The data text can contain variables, defined using the standard Perl format:

	$<name>
	${<name>}

When the data is used, the variable is expanded and replaced with a suitable value. The value will be looked up from a variety of possible sources:
object fields (where the variable name matches the field name) or environment variables.

The variable name is looked up in the following order, the first value found with a matching name is used:

=over 4

=item *

Option names - the values of any command line options may be used as variables

=item *

Arguments names - the values of any command line arguments may be used as variables

=item *

Application fields - any fields of the $app object may be used as variables

=item *

Environment variables - if no application fields match the variable name, then the environment variables are used

=back 

=head3 Data Comments

Any lines starting with:

    __#

are treated as comment lines and not included in the data.




=head2 Directories

The framework sets up various directory paths automatically, as described below.

=head3 @INC path

App::Framework automatically pushes some extra directories at the start of the Perl include library path. This allows you to 'use' application-specific
modules without having to install them globally on a system. The path of the executing Perl application is found by following any links until
an actually Perl file is found. The @INC array has the following added:

	* $progpath
	* $progpath/lib
	
i.e. The directory that the script resides in, and a sub-directory 'lib' will be searched for application-specific modules.

Note that this is the path also used when the framework loads in the core personality, and any optional extensions.

	

=head2 EMBEDDING

A script may be developed and debugged using the App::Framework::Lite module installed on a system, and then turned into a standalone Perl
script by embedding the App::Framework::Lite module into the script file. Also, a developer may choose to also embed any user library modules
related to this script (or may just deliver them in their dubdirectory along with the standalone script).

=head3 Embedding Procedure

When a script is using the App::Framework::Lite module, some developer command line options are automatically added to the script. The developer
uses these options in the embedding process:

=over 4

=item -alf-embed

Causes the script to create a standalone version of itself

=item -alf-embed-lib

By default, the script also embeds any user library modules (i.e. any 'use'd modules that are located under $progpath/ or $progpath/lib/).

Specifying this option set to 0 prevents these modules from being embedded.

=item -alf-compress

By default the embedded modules are stored in a compressed format (whitespace and comments removed).

Specifying this option set to 0 prevents these modules from being compressed. If you have any problems with the embedded modules not working, then try setting
this option to 0 and check the resulting script.

=back 

=head3 Examples

If you have a script test.pl that uses App::Framework::Lite and a user module MyLib.pm (stored in the same directory as test.pl), then you
would create a new, stand-alone script alf-test.pl by running any of the following:

=head4 Embded compressed App::Framework::Lite and user modules

	perl test.pl -alf-embed alf-test.pl

Results in alf-test.pl having the App::Framework::Lite module and MyLib.pm embedded in a compressed version. The script is then completely stand-alone.

=head4 Embded compressed App::Framework::Lite

	perl test.pl -alf-embed alf-test.pl -alf-embed-lib 0

Results in alf-test.pl having the App::Framework::Lite module embedded in a compressed version, but the user module MyLib.pm would need to be
delivered along with the script for it to work.

=head4 Embded readable App::Framework::Lite and user modules

	perl test.pl -alf-embed alf-test.pl -alf-compress 0

Results in alf-test.pl having the App::Framework::Lite module and MyLib.pm embedded in a readable version. The script is completely stand-alone,
but much larger than if the modules had been compressed. This is useful for debugging module problems (especially with a debugger!).

=head2 FIELDS

The following fields should be defined either in the call to 'new()' or as part of the application configuration in the __DATA__ section:

 * name = Program name (default is name of program)
 * summary = Program summary text
 * synopsis = Synopsis text (default is program name and usage)
 * description = Program description text
 * history = Release history information
 * version = Program version (default is value of 'our $VERSION')

 * app_start_fn = Function called before app() function (default is application-defined 'app_start' subroutine if available)
 * app_fn = Function called to execute program (default is application-defined 'app' subroutine if available)
 * app_end_fn = Function called after app() function (default is application-defined 'app_end' subroutine if available)
 * usage_fn = Function called to display usage information (default is application-defined 'usage' subroutine if available)

During program execution, the following values can be accessed:

 * package = Name of the application package (usually main::)
 * filename = Full filename path to the application (after following any links)
 * progname = Name of the program (without path or extension)
 * progpath = Pathname to program
 * progext = Extension of program

=cut

use 5.008004;

use strict ;


our $VERSION = "1.09" ;


#============================================================================================
# USES
#============================================================================================
use Carp ;
use Cwd ;
use Getopt::Long qw(:config no_ignore_case) ;
use Pod::Usage ;
use File::Basename ;
use File::Path ;
use File::Temp ;
use File::Spec ;
use File::DosGlob 'glob' ;
#use File::Which ;


#============================================================================================
# OBJECT HIERARCHY
#============================================================================================
our @ISA ; 

#============================================================================================
# GLOBALS
#============================================================================================

my $class_debug ;

# default to state that the module is embedded (overwritten inside BEGIN block)
my $EMBEDDED = 0 ;

# Maximum line length when embedding (e.g. ensures Clearcase doesn't think file is binary!)
my $MAX_LINE_LEN = 5000 ;

# Keep track of import info
my $import_args ;

# Run error action
our $ON_ERROR_DEFAULT = 'fatal' ;

## Set up variables
my	%FIELDS = (
		'name'				=> undef,
		'progname'			=> undef,
		'progpath'			=> undef,
		'progext'			=> undef,
		'package'			=> undef,
		'filename'			=> undef,
		'version'			=> undef,
		'app'				=> undef,

		'synopsis'			=> "",
		'description'		=> "",
		'summary'			=> "",
		
		'debug'				=> 0,
		
		'app_start_fn'	=> undef,	
		'app_fn'		=> undef,	
		'app_end_fn'	=> undef,
	
		## Data fields
		'_data'				=> [],
		'_data_hash'		=> {},
		
		## Options fields
		'_user_options'		=> [],
		'_options_list'		=> [],
		'_options'			=> {},
		'_get_options'		=> [],
		'_option_fields_hash'		=> {},
		'option_names'		=> [],
		'opts_feature_args'	=> '',
		
		## Args fields
		'user_args'			=> [],		# User-specified args
		'argv'				=> [],		# ref to @ARGV
		'arg_names'			=> [],		# List of arg names
		'_arg_list'			=> [],	# Final ARRAY ref of args - EXCLUDING any opened files
		'_args'				=> {},	# Final args HASH - key = arg name; value = arg value
		'_arg_names_hash'	=> {},	# List of HASHes, each hash contains details of an arg
		'_fh_list'			=> [],	# List of any opened file handles
		'args_feature_args'	=> '',
		
		## Exit
		'exit_type'			=> 'exit',

		## Run fields
		'cmd'		=> undef,
		'args'		=> undef,
		'timeout'	=> undef,
		'nice'		=> undef,
		'dryrun'	=> 0,
		
		'on_error'	=> $ON_ERROR_DEFAULT,
		'error_str'	=> "",
		'required'	=> {},
		
		'check_results'	=> undef,
		'progress'		=> undef,
		
		'status'	=> 0,
		'results'	=> [],
		
		'norun'		=> 0,


		'log'		=> {
			'all'		=> 0,
			'cmd'		=> 0,
			'results'	=> 0,
			'status'	=> 0,
		},

		## Logging
		'logfile'		=> undef,
		'mode'			=> 'truncate',
		'to_stdout'		=> 0,
		
		'_started'		=> 0,
		
	) ;

my $POD_HEAD =	"=head" ;
my $POD_OVER =	"=over" ;

my @DEFAULT_OPTS = (
	['debug=i',			'Set debug level', 	'Set the debug level value', ],
	['v|"verbose"',		'Verbose output',	'Make script output more verbose', ],
	['dryrun|"norun"',	'Dry run', 			'Do not execute anything that would alter the file system, just show the commands that would have executed'],
	['h|"help"',		'Print help', 		'Show brief help message then exit'],
	['man',				'Full documentation', 'Show full man page then exit' ],
	['man-dev',			'Full developer\'s documentation', 'Show full man page for the application developer then exit' ],
	['log=s',			'Log file', 		'Specify a log file', ],
	['dev:pod',			'Output full pod', 	'Show full man page as pod then exit' ],
	['dev:dbg-data',		'Debug option: Show __DATA__', 				'Show __DATA__ definition in script then exit' ],
	['dev:dbg-data-array',	'Debug option: Show all __DATA__ items', 	'Show all processed __DATA__ items then exit' ],
	['dev:alf-info',	'Module information', 	'Display information about the App::Framework::Lite module then exit' ],
	['dev:alf-debug=i',	 'Debug App::Framework::Lite', 	'Set the debug level value of the App::Framework::Lite module', ],
#@NO-EMBED BEGIN
	['dev:alf-embed=s',		'Embed module', 	'Embed the App::Framework::Lite module into script then exit. Specify the filename of the new script.' ],
	['dev:alf-embed-lib=i',	'Embed libraries', 	'(Only used when embedding). Embed user modules as well as the App::Framework::Lite module.', 1 ],
	['dev:alf-compress=i',	'Compress embedded', 	'(Only used when embedding). Compress the embedded modules.', 1 ],
#@NO-EMBED END
	) ;

our @USED = (
	'Carp',
	'Cwd',
	'Getopt::Long qw(:config no_ignore_case)',
	'Pod::Usage',
	'File::Basename',
	'File::Path',
	'File::Temp',
	'File::Spec',
	'File::DosGlob qw(glob)',
) ;

our @OPT_MOD = (
	'File::Which',
) ;
our %AVAILABLE_MOD ;


#============================================================================================
BEGIN {

#@NO-EMBED BEGIN
	# Clear flag for non-embedded
	$EMBEDDED = 1 ;
#@NO-EMBED END

	## Get caller information
	my ($package, $filename, $line, $subr, $has_args, $wantarray) = caller(0) ;

	## Add a couple of useful function calls into the caller namespace
	{
		no warnings 'redefine';
		no strict 'refs';

		foreach my $fn (qw/go/)	
		{
			*{"${package}::$fn"} = sub {  
			    my @callinfo = caller(0);
				my $app = App::Framework::Lite->new(@_,
					'_caller_info' => \@callinfo) ;
				$app->$fn() ;
			};
		}	
	}
	
	## Optional modules
	foreach my $mod (@OPT_MOD)
	{
		# see if we can load up the package
		if (eval "require $mod") 
		{
			$mod->import() ;
			++$AVAILABLE_MOD{$mod} ;

		}
	}
}

#============================================================================================
# Set up module import
sub import 
{
    my $pkg     = shift;

	# save for later    
    $import_args = join ' ', @_ ;

	## Get caller information
	my ($package, $filename, $line, $subr, $has_args, $wantarray) = caller(0) ;

	## Set program info
	App::Framework::Lite->_set_paths($filename) ;
	

	## Import modules into caller space
	my $include = "package $package;\n" ;
	foreach my $use (@USED)
	{
		$include .= "use $use ;\n" ;
	}
	foreach my $use (keys %AVAILABLE_MOD)
	{
		if ($AVAILABLE_MOD{$use})
		{
			$include .= "use $use ;\n" ;

lib/App/Framework/Lite.pm  view on Meta::CPAN

	my %opts = $this->options() ;
	my $args_values_href = $this->args_values_hash() ;
	my $data_href = $this->{_data_hash} ;
	$this->expand_keys($data_href, [\%opts, $args_values_href, \%app_vars, \%ENV]) ;
}

#----------------------------------------------------------------------------

=item B<app_handle_opts()>

Handles the default options (for example -man, -help etc)
 

=cut


sub app_handle_opts
{
	my $this = shift ;

	## Get options
	my %options = $this->options() ;

	## Handle special options
	my %opts = $this->options() ;
	if ($opts{'man'} || $opts{'help'})
	{
		my $type = $opts{'man'} ? 'man' : 'help' ;
		$this->usage($type) ;
		$this->exit(0) ;
	}
	if ($opts{'man-dev'})
	{
		$this->usage('man-dev') ;
		$this->exit(0) ;
	}
	if ($opts{'pod'})
	{
		print $this->pod() ;
		$this->exit(0) ;
	}
	if ($opts{'alf-debug'})
	{
		$this->{debug} = $opts{'alf-debug'} ;
	}
	if ($opts{'dbg-data'})
	{
		$this->_show_data() ;
		$this->exit(0) ;
	}
	if ($opts{'dbg-data-array'})
	{
		$this->_show_data_array() ;
		$this->exit(0) ;
	}

	if ($opts{'alf-info'})
	{
		print "App::Framework::Lite info\n" ;
		print "  Version:  $VERSION\n" ;
		print "  Embedded: " . ($EMBEDDED ? "yes" : "no") . "\n" ;
		$this->exit(0) ;
	}

#@NO-EMBED BEGIN
	if ($opts{'alf-embed'})
	{
		my $src = $this->{'filename'} ;
#		my $dest = $this->{'progpath'} . '/' . "alf-" . $this->{'progname'} . $this->{'progext'} ;
		my $dest = $opts{'alf-embed'} ;
		my %libs = $this->embed($src, $dest, $opts{'alf-compress'}, $opts{'alf-embed-lib'}) ;
		print "Embedded App::Framework::Lite into $src. Stand-alone script saved as $dest.\n" ;
		print "Embedded the following library modules:\n" ;
		foreach my $mod (sort {$libs{$a}{'order'} <=> $libs{$b}{'order'} } keys %libs)
		{
			print "    $mod\n" ;
		}
		
		print "Have a nice life.\n" ;
		$this->exit(0) ;
	}
#@NO-EMBED END

	if ($opts{'log'})
	{
		$this->{logfile} = $opts{'log'} ;
	}

}


#----------------------------------------------------------------------------

=item B<application()>

Execute the application.
 
Calls the following methods in turn:

* (Application registered 'app' function)
 

=cut

sub application
{
	my $this = shift ;

	## Get options
	my %options = $this->options() ;

	## Check args here (do this AFTER allowing derived objects/features a chance to check the options etc)
	$this->check_args() ;
	
	# get args
	my %args = $this->arg_hash() ;

	## Run application function
	$this->_exec_fn('app', $this, \%options, \%args) ;

	## Close any open arguments
	$this->close_args() ;
}

#----------------------------------------------------------------------------

=item B<app_end()>

Tidy up after the application.

Calls the following methods in turn:

* (Application registered 'app_end' function)
 

=cut


sub app_end
{
	my $this = shift ;

lib/App/Framework/Lite.pm  view on Meta::CPAN

}

#----------------------------------------------------------------------------

=item B< find_lib($module) >

Looks for the named module in the @INC path. If found, checks the package name inside the file
to ensure that it really matches the capitalisation.

(Mainly for Microsoft Windows use!)

=cut

sub find_lib
{
	my $class = shift ;
	my ($module, $file_ref) = @_ ;

	my @module_dirs = split /::/, $module ;
	my $pm = pop @module_dirs ;

#print "find_lib($module)\n" ;
	
	my $found ;
	foreach my $dir (@INC)
	{
		my $file = File::Spec->catfile($dir, @module_dirs, "$pm.pm") ;

#print " + checking $file\n" ;
		if (-f $file)
		{
			if (open my $fh, "<$file")
			{
				my $line ;
				while (defined($line = <$fh>))
				{
					chomp $line ;
					if ($line =~ m/^\s*package\s+$module\s*;/)
					{
						if ($file_ref)
						{
							$file =~ s%\\%/%g ;
							$$file_ref = $file ;	
						}
						$found = $module ;
						last ;
					}
				}
				close $fh ;
			}
			last if $found ;
		}
	}

#print "find_lib() = $found\n" ;

	return $found ;
}


#@NO-EMBED BEGIN

#----------------------------------------------------------------------------
sub _module_to_embed
{
	my $this = shift @_ ;
	my ($module, $file, $embed_libs) = @_ ;
	
	my $embed_it = 0 ;

	## Always embed this module
	if ($module eq 'App::Framework::Lite')
	{
		$embed_it = 1 ;
	}
	elsif ($embed_libs)
	{
		# is this an App::Framework module
		if ($module =~ /^App::Framework::Lite/)
		{
			$embed_it = 1 ;
		}
		else
		{
			# is this module under the program directory? i.e. a user module
			my $regexp = qr($this->{'progpath'}) ;
			if ($file =~ $regexp)
			{
				$embed_it = 1 ;
			}
		}
	}

	return $embed_it ;
}

#----------------------------------------------------------------------------

=item B< embed($src, $dest, [$compress]) >

Embeds App::Framework::Lite into the script and writes the standalone script out

=cut

sub embed
{
	my $this = shift ;
	my ($src, $dest, $compress, $embed_libs) = @_ ;

	my %libs ;
	my %handled_libs ;
	my @main ;

print "embed($src, $dest, compress=$compress, embed_libs=$embed_libs)\n" if $this->{'debug'};

	## Handle source
	open my $in_fh, "<$src" or die "Error: Unable to read $src : $!" ;
	open my $out_fh, ">$dest" or die "Error: Unable to write $dest : $!" ;

	my $perl_line = "";
	my $strict = "";
	my $line ;
	while(defined($line = <$in_fh>))
	{
		chomp $line ;

print "LINE: $line\n" if $this->{'debug'};

		if ($line =~ /^__DATA__/)
		{
			print $out_fh <<EMBED_START;
$perl_line
##################################################################################
# Start of embedded modules - embedded by App::Framework::Lite
#
# Your original script is now at the end of the file.
#
##################################################################################
#
$strict
EMBED_START
			
			# Handle any other embedded modules
			foreach my $mod (sort {$libs{$a}{'order'} <=> $libs{$b}{'order'} } keys %libs)
			{
				my $module_str = $libs{$mod}{'content'} ;
				
				print $out_fh "\n## EMBEDDED $mod ##\n" ;
				print $out_fh "$module_str\n" ;
				print $out_fh "\## EMBEDDED $mod - END ##\n" ;
			}
			
			print $out_fh <<EMBED_END;
#
##################################################################################
# End of embedded modules - embedded by App::Framework::Lite
##################################################################################
package main;

EMBED_END

#			print $out_fh "\n$line\n" ;
			push @main, "\n$line\n" ;
		}
		else
		{
			if (!$perl_line)
			{
				# find first line (if specifed)
				if ($line =~ /^#!/)
				{
					$perl_line = $line ;
				}	
			}
			
			## Check for libs if required
			if ($line =~ /^\s*use\s+(\S+)(.*);/)
			{
				my ($module, $import, $file) = ($1, $2, undef) ;
				if ($module eq 'strict')
				{
					$strict = $line ;
				}
				$module = $this->find_lib($module, \$file) ;
				
				
				# If this is related to the program path then include it
				if ($this->_module_to_embed($module, $file, $embed_libs))
				{
					push @main, "$module->import($import) ;\n" ;

print " + get subs\n" if $this->{'debug'};
					## get any sub-modules
					my @new = ($module) ;
					my $new = 1 ;
					do
					{
						$new = 0 ;
						foreach my $mod (keys %libs)
						{
							if (!$handled_libs{$mod})
							{
								push @new, $mod ;
							}
						}
						foreach my $mod (@new)
						{
print " + + module str $mod\n" if $this->{'debug'};
							my $href = $this->_add_mod_lib($mod, \%libs) ;
							$href->{'content'} = $this->_module_str($mod, $compress, \%libs, $embed_libs) ;
							++$handled_libs{$mod} ;
							++$new ;
						}
						@new = () ;
					} while ($new) ;
print " + get subs done\n" if $this->{'debug'};
					
					next ;
				}
			}
#			print $out_fh "$line\n" ;
			push @main, "$line\n" ;
		}
	}
	close $in_fh ;
	
	# output script body
	foreach my $line (@main)
	{
		print $out_fh "$line" ;
	}	
	close $out_fh ;	
	
	return %libs ;
}
#@NO-EMBED END


#============================================================================================
# PRIVATE
#============================================================================================

#@NO-EMBED BEGIN

#---------------------------------------------------------------------
# Squash module down to a few lines of text
sub _module_str
{
	my $this = shift ;
	my ($module, $compress, $libs_href, $embed_libs) = @_ ;
	
	my $module_str = "" ;

print "_module_str($module, compress=$compress, embed_libs=$embed_libs)\n" if $this->{'debug'};
	
	## Find module file
	my $src ;
	$this->find_lib($module, \$src) ;
	
	## Squash module
	open my $in_fh, "<$src" or die "Error: Unable to read module $src : $!" ;
	my $use=0 ;
	my $begin=0 ;
	my $complete=0;
	my $pod=0 ;
	my $podnext=0 ;
	my $no_embed=0 ;
	my $asis=0 ;
	my $prev_semi=0;
	my $comment=0;
	my $varinit = 1 ;
	my $varsdef = "" ;
	my $line ;
	my $current_len = 0 ;
	
	$asis = '@@ALWAYS-ASIS@@' if !$compress ;
	
	while(defined($line = <$in_fh>))
	{
		chomp $line ;

print " : LINE: $line\n" if $this->{'debug'};
print " (varinit=$varinit, pod=$pod)\n" if $this->{'debug'};

		next if $complete ;

		if ($line =~ /\@NO\-EMBED (\w+)/)
		{
			if ($1 eq 'BEGIN')
			{
				$no_embed = 1 ;
			}
			else
			{
				$no_embed = 0 ;
			}
			next ;
		}
		next if $no_embed ;

		## pod
		$pod = $podnext ;
		if ($line =~ /^=(\w+)/)
		{
			if ($1 eq 'cut')
			{
				$podnext = 0 ;
			}
			else
			{
				$podnext = 1 ;
			}
			$pod = 1 ;
		}

			
		## using an embdeddable module?
		$use=0 ;
		if (!$pod)
		{
			if ($line =~ /^\s*use\s+(\S+)(.*);/)
			{
				my ($module, $import, $file) = ($1, $2, undef) ;
				$module = $this->find_lib($module, \$file) ;
				$use=1 ;
	
	print " + use $module ($import)\n" if $this->{'debug'};
				
				# If this is embeddable
				if ($this->_module_to_embed($module, $file, $embed_libs))
				{
	print " + + embed $module\n" if $this->{'debug'} ;
					$module_str .= "$module->import($import) ;\n" ;
					$current_len = 0 ;
					$this->_add_mod_lib($module, $libs_href) ;
					next
				}
			}
		}


		if ($asis)
		{
			## Look for end of as-is block
			if ($line =~ /^$asis/)
			{
				$asis = 0 ;

lib/App/Framework/Lite.pm  view on Meta::CPAN

			}
		}
		
		# skip end of module
		if ($line =~ /^__END__|^1;/)
		{
print " + + line skip END\n" if $this->{'debug'};

			## See if we've handled any variables
			if ($varsdef)
			{
print " + + ADD BEGIN:\n$varsdef\n" if $this->{'debug'};
				$module_str .= "\nBEGIN {\n" ;
				$module_str .= $varsdef ;
				$module_str .= "}\n" ;
				$varsdef = "" ;
				$current_len = 0 ;
			}
			$complete=1 ;
			next ;
		}

		# end of variables section
		if ($line =~ /^\s*sub/)
		{
			# end of variables section
			$varinit = 0 ;
		}

		# comments
		$comment=0;
		if ($line =~ /#/) 
		{
			$comment=1 ;
		}
		
		# gather variables
		if ($varinit)
		{
	print " + Gathering variables\n" if $this->{'debug'};
			$line =~ s/^\s+// ;
			$line =~ s/\s+$// ;
			
			# don't keep: empty lines, package def, comments, pod, or use defs
			if ($line && ($line !~ /^package/) && ($line !~ /^#/) && !$pod && !$use)
			{
				# strip off our/my
				my $var = $line ;
				$var =~ s/^\s*(my|our)\s+// ;
#				$varsdef .= "$var\n" ;
				$varsdef .= "$var " ;
				$varsdef .= "\n" if $comment || $asis ;

	print " + + add var: $var\n" if $this->{'debug'};
			}
		}
		
		## Special case for App::Framework::Lite
		
		# Set embedded flag
		$line =~ s/\$EMBEDDED = 0/\$EMBEDDED = 1/ ;

		}
		
		## ensure we're not at the line limit
		my $line_len = length $line ;
		if ($current_len + $line_len >= $MAX_LINE_LEN)
		{
			$module_str .= "\n" ;
			$current_len = 0 ;
		}
		
		## print it
print " + + line ok\n" if $this->{'debug'};
		$module_str .= "$line" ;
		$current_len += $line_len ;

		if ($asis || $comment)
		{
			$module_str .= "\n" ;
			$current_len = 0 ;
		}
		else
		{
			$module_str .= " " ;
			++$current_len ;
		}
	}
	close $in_fh ;

print "_module_str($module) - END\n" if $this->{'debug'};

	return $module_str ;
}


#---------------------------------------------------------------------
# Add a module to the HASH
#
# HASH is indexed by module name, each entry is a HASH:
#
# 'order'	=> number # order in which modules have been added
# 'content' => scalar # text of the module
#
sub _add_mod_lib
{
	my $this = shift ;
	my ($module, $libs_href) = @_ ;

	my $href ;
	if (exists($libs_href->{$module}))
	{
		# already in the list
		$href = $libs_href->{$module} ;
	}
	else
	{
		my $order = scalar(%$libs_href) + 1 ;
		$href = {
			'order'		=> $order,
			'content'	=> "",
		} ;
		$libs_href->{$module} = $href ;
	}

	return $href ;
}

#@NO-EMBED END


#---------------------------------------------------------------------
sub _setup_modules
{
	my $this = shift ;

	## Set up optional routines

	# Attempt to load Debug object
	if (_load_module('Debug::DumpObj'))
	{
		# Create local function
		*prt_data = sub {my $this = shift; Debug::DumpObj::prt_data(@_)} ;
	}
	else
	{
		# See if we've got Data Dummper
		if (_load_module('Data::Dumper'))
		{
			# Create local function
			*prt_data = sub {my $this = shift; print Dumper([@_])} ;
		}	
		else
		{
			# Create local function
			*prt_data = sub {my $this = shift; print @_, "\n"} ;
		}
	}

}


#---------------------------------------------------------------------
sub _load_module
{
	my ($mod) = @_ ;
	
	my $ok = 1 ;

	# see if we can load up the packages for thumbnail support
	if (eval "require $mod") 
	{
		$mod->import() ;
	}
	else 
	{
		# Can't load package
		$ok = 0 ;
	}
	return $ok ;
}



#----------------------------------------------------------------------------
#
#=item B<_register_fn()>
#
#Register a function provided as a subroutine in the caller package as an app method



( run in 0.829 second using v1.01-cache-2.11-cpan-71847e10f99 )