[cgiapp] refactoring Cascade with best practices

2002-12-24 Thread Mark Stosberg

Hello,

Some of you may already be familiar with Cascade, a CGI::Application
based project that I wrote and distribute:

http://summersault.com/software/cascade/

I have some interest in re-factoring this application to be an example
of CGI::Application best practices. First, I'd like some feedback on
exactly what these are. :) Here's my own starter list. Many of these
ideas have been gathered from other folks through discussions on this
list.

First, the application should adhere to common best practices that
apply to Perl programming in general. Some guidelines are provided in
perldoc perlstyle, and perldoc perlsec. Additionally I might add,
minimum use of global variables.

Now my CGI::App specific ideas:

 - Sperate Authentication Layer. This means that authentication is as
   sperate from the rest of application as possible, while still
   providing all the needed services to the application. Architecture
   decisions here could include:
- Using the web server to help with Authentication by using one or
  more of the following techniques:
  - writing your own Apache authentication handler in mod_perl
  - using an existing Apache auth handler, such as mod_pg_auth
  - organizing your instance scripts to make Apache's built-in
authentication system work for you, such as putting all your
scripts that need administrative authorization in a /admin
directory

- Right-sized modules. Each CGI::App module should contain a number of
  closely related functions. One giant monolith module for a large
  project would be Bad, as would be a one-function-per-module design.
  This kind of structure can help with re-usability when one module can
  used sperate from the rest of the functions. Related to this, each
  sperate module should have it's on own instance script.

- Separate Run Modes from Data Access. When multiple run modes need to
  access the same data structures, it is appropriate to create a
  separate module to handle this that all the run modes can access. By
  simply dealing in data processing, this design increases re-usability.
  Jesse discusses this more here in terms of the MVC model:
  http://tinyurl.com/3t4u

- Separate run mode handling from form processing. I find it to be a
  useful construct to separate the run mode definition from the form
  variables by gathering it from the PATH_INFO. Jesse suggested this
  long ago. One reason I think it works well is because URLs are often
  more bookmark-able, and a browser reload is more likely to do
  something reasonable. Here's how this might look:
  /script.cgi/run_mode_name?param_1=organic;param_2=locally_grown

- Keep configuration variables in a separate file. While instance
  scripts make sense for storing variables specific to how that instance
  script works in the project, for many project wide variables it makes
  sense to store them in a separate configuration file. This allows you
  to access some of your application functionality from a cron script
  more easily.

- Use HTML and CSS that validates against the W3C standards.

Feedback? What else?

   Mark

http://mark.stosberg.com/

-
Web Archive:  http://www.mail-archive.com/cgiapp@lists.erlbaum.net/
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]




Re: [cgiapp] Global configuration (fwd)

2002-12-24 Thread Mark Stosberg

 In the recent best practices document that Mark started, he mentions
 configuration information that is global, i.e. is needed by more than
 one cgi script. What CPAN configuration modules are people using in
 their CGI::Apps?

John,

I think the optimal answer here depends on what kind of user you are
targeting to configure the application.

I use a simple .pm module that contains regular Perl with comments.
This works great for me. Usually I'm just defining key = value
relations in a %CFG hash. However, in a few cases where I want to do
something more complicated, I have the full power of Perl to use, and I
know the syntax already. :)

Sometimes I dream of having a web-based configuration tool for
applications, which could necessitate having some other config file
format, or perhaps storing everything in a database (except the databse
connection options...).

  -mark

http://mark.stosberg.com/

-
Web Archive:  http://www.mail-archive.com/cgiapp@lists.erlbaum.net/
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]




Re: [cgiapp] Global configuration

2002-12-24 Thread Thilo Planz
In the recent best practices document that Mark started, he mentions
configuration information that is global, i.e. is needed by more than
one cgi script. What CPAN configuration modules are people using in
their CGI::Apps?


Hi,

I am not using any CPAN module, but wrote a small module myself.
You can see it below (comments appreciated).

Features:

- configuration files usually are just Perl files, returning a hashref

	example:
		{ tmpl_path = '/path/to/templates',
		  log_level = 3,
		}

	using Perl files gives maximum flexibility,
	with security problems pointed out elsewhere.
	(I am thinking of using the Safe module to check these)

- configuration files can be in other formats by providing an
	input processor, which is just a coderef
	(see method get_raw below for an example)

- the data read from the files is cached, but reloaded if the file is 
changed
	(with a 5 second delay to minimize stat() calls)
	this makes it very efficient in mod_perl


Synopsis:

	my $config = My::Configuration-get('/path/to/config.pl');
	
	$config is a hashref with parameters.


Cheers,

Thilo


== CODE BELOW ===

package My::Configuration;
use strict;
use FileHandle;

# global config file cache
my %CACHE = ();

# modification times of the data in the caches
my %MOD_TIMES = ();


# get the raw contents of a file
# as an array(ref) of lines
sub get_raw{
	my ($class, $filename) = @_;
	return My::Configuration-get($filename,
		sub{
			my $fh = shift;
			my @result = $fh;
			$fh-close;
			return \@result;
		}
	);
}


# get the contents of a configuration file
#
# the file will be 'done', unless an optional
# processor subref is specified, in which case
# the processor gets to read the file
# and return a reference to something

sub get{
	my ($class, $filename, $processor) = @_;
	
	unless (defined $filename){
		warn cannot load configuration without a filename!;
		return;
	}
	my $ref;
	# try to use the cached version
	my $cache_age = $MOD_TIMES{$filename};
	if ($cache_age and time - $cache_age  6){
		# check only every five seconds
		return $CACHE{$filename};
	}
	
	my $mod = (stat($filename))[9];	
	# file exists
	if ($mod){
		if ($cache_age and $mod == $cache_age){
			# cached data has not changed
			return $CACHE{$filename};
		}
	}
	else{
		warn missing configuration file $filename \n;
	}
	
	$ref = ref $processor eq 'CODE' ?
		$processor-(new FileHandle $filename, 'r') :
		do $filename;
		
	unless (ref $ref){
		$ref = defined $ref ? '$ref' : 'undef';
		my $message=broken configuration file $filename:\n;
		$message .= parse error: $@\n if $@;
		$message .= load error: $!\n if $!;
		$message .= returned not a ref, but $ref\n unless $@ || $!;
		# die if no previous configuration
		exists $CACHE{$filename} ?
			warn ($message) : die ($message);
		return $CACHE{$filename};
	}
	$CACHE{$filename} = $ref;
	$MOD_TIMES{$filename} = $mod;
	return $ref;
}


-
Web Archive:  http://www.mail-archive.com/cgiapp@lists.erlbaum.net/
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]