++ed by:
2 non-PAUSE users
Author image Michael Peters
and 1 contributors

NAME

CGI::Application::Dispatch - Class used to dispatch request to CGI::Application based objects

SYNOPSIS

Under mod_perl

    <Location /app>
        SetHandler perl-script
        PerlHandler CGI::Application::Dispatch
    </Location>

Under normal cgi

    #!/usr/bin/perl
    use strict;
    use CGI::Application::Dispatch;

    CGI::Application::Dispatch->dispatch();

DESCRIPTION

This module provides a way (as a mod_perl handler or running under vanilla CGI) to look at the path ($r->path_info or $ENV{PATH_INFO}) of the incoming request, parse off the desired module and it's run mode, create an instance of that module and run it.

It currently supports both generations of mod_perl (1.x and 2.x). Although, for simplicity, all examples involving apache configuration and mod_perl code will be shown using mod_perl 1.x. This may change as mp2 usage increases.

In addition, the portion of the PATH_INFO that is used to derive the module name is also passed to the PARAMS of the modules <new()> as CGIAPP_DISPATCH_PATH. This can be useful if you are programatically generating URLs.

It will translate a URI like this (under mod_perl):

        /app/module_name/run_mode

or this (vanilla cgi)

        /app/index.cgi/module_name/run_mode

into something that will be functionally similar to this

        my $app = Module::Name->new(..);
        $app->mode_param(sub {'run_mode'}); #this will set the run mode

And in both cases the CGIAPP_DISPATCH_PATH value will be 'module_name' so that you can generate a self referential URL by doing something like the following inside of your application module:

    my $url = 'http://mysite.com/app/' . $self->param('CGIAPP_DISPATCH_PATH');

MOTIVATION

To be honest I got tired of writing lots of individual instance scripts, one for each application module, under the traditional style of CGI::Application programming. Then when I switched to running my CGI::Application modules as straight mod_perl handlers I got tired of having to change my httpd.conf file for every module I introduced and having my configuration file full of Location sections. Since I had moved all of my configuration variables into config files and was not passing any values into the PARAMS hash upon module creation I decided not to write the same code over and over.

I guess it comes down to me just being lazy. :)

OPTIONS

This section describes the different options that are available to customize how you dispatch your requests. All of these options can either be set using 'PerlSetVar' (if you're running under mod_perl) or passed directly as name-value pairs to the "dispatch()" method. When passing them directly as name-value pairs to the "dispatch()" method you may omit the 'CGIAPP_DISPATCH_' prefix on the name of each option. So, CGIAPP_DISPATCH_PREFIX can become simply PREFIX. You can't however use both. We have examples so don't worry too much.

CGIAPP_DISPATCH_PREFIX

This option will set the string that will be prepended to the name of the application module before it is loaded and created. So to use our previous example request of

    /app/index.cgi/module_name/run_mode

This would by default load and create a module named 'Module::Name'. But let's say that you have all of your application specific modules under the 'My' namespace. If you set this option to 'My' then it would instead load the 'My::Module::Name' application module instead.

CGIAPP_DISPATCH_RM

This option, if false, will tell C::A::Dispatch to not set the run mode for the application. By default it is true.

CGIAPP_DISPATCH_DEFAULT

This option will set a default value if there is no $ENV{PATH_INFO}. It will be parsed to obtain the module name and run mode (if you don't have CGIAPP_DISPATCH_RM set to false.

CGIAPP_DISPATCH_TABLE

This option will tell CGI::Application::Dispatch to use either a provided hash or subroutine to translate the $ENV{PATH_INFO} into the module name. The retrieved value will also be combined with the CGIAPP_DISPATCH_PREFIX value if it exists.

  • TABLE with mod_perl

    If you are using this under mod_perl and enjoy using PerlAddVar directives, then your httpd.conf might look something like this (under mod_perl 1)

      <Location /app>
        SetHandler perl-script
        PerlHandler CGI::Application::Dispatch
        PerlSetVar CGIAPP_DISPATCH_PREFIX MyApp
        PerlSetVar CGIAPP_DISPATCH_RM Off
    
        PerlSetVar CGIAPP_DISPATCH_TABLE foo
        PerlAddVar CGIAPP_DISPATCH_TABLE Some::Name
        PerlAddVar CGIAPP_DISPATCH_TABLE bar
        PerlAddVar CGIAPP_DISPATCH_TABLE Some::OtherName
        PerlAddVar CGIAPP_DISPATCH_TABLE baz
        PerlAddVar CGIAPP_DISPATCH_TABLE Yet::AnotherName
      </Location>

    And then Dispatch will turn the all those PerlSetVar and PerlAddVars into a hash.

  • TABLE with vanilla CGI

    Or if you are using Dispatch under vanilla cgi then an equivalent .cgi script would be:

      #!/usr/bin/perl
      use strict;
      use CGI::Application::Dispatch;
    
      CGI::Application::Dispatch->dispatch(
        PREFIX  => 'MyApp',
        RM      => 0,
        TABLE   => {
            'foo'     => 'Some::Name',
            'bar'     => 'Some::OtherName',
            'baz'     => 'Yet::AnotherName',
        },
      );

In all these cases a url or '/foo/rm2' will be translated into the module 'MyApp::Some::Name' and run mode 'rm2'. This will allow more flexibility in PATH_INFO to module name translation and also provide more security for those who want to restrict what options are available for translation. If the PATH_INFO contains a value that is not a key in your table hash, then Dispatch will t

METHODS

handler()

This method is used so that this module can be run as a mod_perl handler. When it creates the application module it passes the $r argument into the PARAMS hash of new()

    <Location /app>
        SetHandler perl-script
        PerlHandler CGI::Application::Dispatch
        PerlSetVar  CGIAPP_DISPATCH_PREFIX  MyApp
        PerlSetVar  CGIAPP_DISPATCH_RM      Off 
        PerlSetVar  CGIAPP_DISPATCH_DEFAULT /module_name
    </Location>

The above example would tell apache that any url beginning with /app will be handled by CGI::Application::Dispatch. It also sets the prefix used to create the application module to 'MyApp' and it tells CGI::Application::Dispatch that it shouldn't set the run mode but that it will be determined by the application module as usual (through the query string). It also sets a default application module to be used if there is no PATH_INFO. So, a url of /app/module_name would create an instance of MyApp::Module::Name.

dispatch()

This method is primarily used in a non mod_perl setting in an small cgi script to dispatch requests. You can pass this method the same name value pairs that you would set for the "handler()" method using the same options mentioned above.

    #!/usr/bin/perl
    use strict;
    use CGI::Application::Dispatch;

    CGI::Application::Dispatch->dispatch(
            PREFIX  => 'MyApp',
            RM      => 0,
            DEFAULT => 'module_name',
        );

This example would do the same thing that the previous example of how to use the "handler()" method would do. The only difference is that it is done in a script and not in the apache configuration file.

The benefit to using CGI::Application::Dispatch in a non mod_perl environment instead of the traditional instance scripts would only be seen in an application that has many instance scripts. It would mean your application would only need one script and many application modules. Since the dispatch script is so simple you just write it once and forget about it and turn your attention to your modules and templates.

Any extra params to dispatch() will be passed on to the new() method of the CGI::Application module being called.

get_module_name($path_info, $prefix, [$table])

This method is used to control how the module name is generated from the PATH_INFO. Please see "PATH_INFO Parsing" for more details on how this method performs it's job. The main reason that this method exists is so that it can be overridden if it doesn't do exactly what you want.

This method will recieve three arguments in the following order:

  • $path_info

    The PATH_INFO string

  • $prefix

    The value of the CGIAPP_DISPATCH_PREFIX parameter.

  • $table

    A hash reference containing the value of the CGIAPP_DISPATCH_TABLE parameter.

This method will return the name of the module to create. Actually it returns a list of items, the first being the name of the module, the second being the specific substring of the PATH_INFO that was used to create the module name. If you decide to override this method to customize the PATH_INFO-to-module-name-creation then you must also return this section of the PATH_INFO that you used if it's not the same as the default. Otherwise the value of CGIAPP_DISPATCH_PREFIX will be undef.

get_runmode($path_info)

This method is used to control how the run mode is generated from the PATH_INFO. Please see "PATH_INFO Parsing" for more details on how this method performs it's job. The main reason that this method exists is so that it is overridden if it doesn't do exactly what you want.

You shouldn't actually call this method yourself, just override it if necessary.

require_module($module_name)

This class method is used internally by CGI::Application::Dispatch to take a module name (supplied by get_module_name) and require it in a secure fashion. It is provided as a public class method so that if you override other functionality of this module, you can still safely require user specified modules. If there are any problems requiring the named module, the $CGI::Application::Dispatch::Error variable will be set.

    CGI::Application::Dispatch->require_module('MyApp::Module::Name');

    if( $CGI::Application::Dispatch::Error ) {
        die "Could not require module MyApp::Module::Name "
            . $CGI::Application::Dispatch::Error
    }

PATH_INFO Parsing

This section will describe how the application module and run mode are determined from the PATH_INFO and what options you have to customize the process.

Getting the module name

To put it simply, if you don't use a CGIAPP_DISPATCH_TABLE then the PATH_INFO is split on backslahes (/). The second element of the returned list is used to create the application module. So if we have a path info of

    /module_name/mode1

Then the string 'module_name' is used. Underscores (_) are turned into double colons (::) and each word is passed through ucfirst so that the first letter of each word is captialized.

Then the CGIAPP_DISPATCH_PREFIX is added to the beginning of this new module name with a double colon :: separating the two.

If you don't like the exact way that this is done, don't fret you do have a couple of options. First, you can specify a CGIAPP_DISPATCH_TABLE on a project-by-project basis to explicitly perform the PATH_INFO to module-name translation. If you are looking for something more generic that you can later reuse, you can subclass Dispatch and override the get_module_name() to do whatever you wish.

Getting the run mode

Just like the module name is retrieved from splitting the PATH_INFO on backslashes, so is the run mode. Only instead of using the second element of the resulting list, we use the third as the run mode. So, using the same example, if we have a path info of

    /module_name/mode1

Then the string 'mode1' is used as the run mode unless the CGIAPP_DISPATCH_RM is set to false. As with the module name this behavior can be changed by overriding the get_runmode() sub.

MISC NOTES

  • CGI query strings

    CGI query strings are unaffected by the use of PATH_INFO to obtain the module name and run mode. This means that any other modules you use to get access to you query argument (ie, CGI, Apache::Request) should not be affected. But, since the run mode may be determined by CGI::Application::Dispatch having a query argument named 'rm' will be ignored by your application module (unless your CGIAPP_DISPATCH_RM is false).

AUTHOR

Michael Peters <mpeters@plusthree.com>

Thanks to Plus Three, LP (http://www.plusthree.com) for sponsoring my work on this module

COMMUNITY

This module is a part of the larger CGI::Application community. If you have questions or comments about this module then please join us on the cgiapp mailing list by sending a blank message to "cgiapp-subscribe@lists.erlbaum.net". There is also a community wiki located at http://www.cgi-app.org/

CONTRIBUTORS

  • Drew Taylor <drew@drewtaylor.com>

  • James Freeman <james.freeman@smartsurf.org>

  • Michael Graham <magog@the-wire.com>

  • Cees Hek <ceeshek@gmail.com>

SECURITY

Since C::A::Dispatch will dynamically choose which modules to use as the content generators, it may give someone the ability to execute random modules on your system if those modules can be found in you path. Of course those modules would have to behave like CGI::Application based modules, but that still opens up the door more than most want. This should only be a problem if you don't use the CGIAPP_DISPATCH_PREFIX (or simple PREFIX in "dispatch()") option. By using this option you are only allowing the url to pick from a certain directory (namespace) of applications to run.

SEE ALSO

CGI::Application, Apache::Dispatch

LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.