[cgiapp] refactoring Cascade with best practices
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)
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
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]