Using ModPerl::ParamBuilder to make custom Apache directives

One of the neatest features of mod_perl 2 is that it makes it easy to create custom Apache configuration directives. Custom directives allow you enourmous flexibility in how you configure your mod_perl applications and it allows you to keep all of your application's configuration in your httpd.conf file.

ModPerl::ParamBuilder is a CPAN module I developed that wraps the mod_perl 2 APIs to make it even easier to use custom directives. Keeping with the Perl philosphy of "Make the simple things simpile, and the hard things possible", ModPerl::ParamBuilder make the typical day-to-day use of custom directives easy, and leaves the more complicated options available via the core mod_perl APIs.

Traditional mod_perl application configuration

Traditionally mod_perl applications are configured in one of three ways:

  1. Configuration information is read from a config file at server start up and retained for the life of the Apache child.
  2. Configuration information is passed into the mod_perl application's environment using PerlSetEnv or PerlPassEnv.
  3. Configuration information is passed in via PerlSetVars to the appliaction.

Prior to switching to mod_perl 2.0, I always used PerlSetVars to pass configuration information to my applications. As a personal preference I try to avoid using environment variables whenever possible, because with environment variables the configuration is not closely tied to the app.

While I can understand the reasons one would use an external configuration file, this too can lead to problems, especially if you need the ability to run multiple instances of your app on the same system and don't plan for it in advance.

Why use custom directives?

There are three main reasons to have your application use custom directives:

  1. They give your application a more professional and polished appearance for the end user and/or admin.
  2. Where PerlSetVars, PerlSetEnvs, etc. are evaluated for each and every request, custom directives are only evaluated when the Apache child is started. While this will probably not give you a noticeable increase in performance, in some environments every little bit counts.
  3. They are just damn cool!

Using ModPerl::ParamBuilder

The way you use ModPerl::ParamBuilder in your applications is to create a configuration module that inherits from it. For the purposes of this example let's assume you are building a mod_perl application that lives in the MyApp namespace and you need to to pass in the filename of the Template Toolkit template the app should use.

You would then create a config module named, say MyApp::Params that would look something like this:

package MyApp::Params;

    use strict;
    use warnings;
    use ModPerl::ParamBuilder;

    # Inherit from ModPerl::ParamBuilder
    use base qw( ModPerl::ParamBuilder );

    # Create our builder
    my $builder = ModPerl::ParamBuilder->new( __PACKAGE__ );

    # Create a single argument parameters named Template
    $builder->( 'Template' );

    # Actually load these directives
    $builder->load;

    1;
    

To begin using this module all you need to do is include the following in Apache's httpd.conf file:

PerlLoadModule MyApp::Params

It is very important that you use PerlLoadModule and not the more commonly used PerlModule here. This instructs Apache to load your configuration module early in its startup. Otherwise it would not know how to read your custom directives, they would look like synatx errors. Now that you have this loaded in you can use the custom directive we just created. Now you can make your configuration look like:

PerlLoadModule MyApp::Params

    <Location /myapp>
      SetHandler perl-script
      Template foo.tt
      PerlResponseHandler MyApp::Main
    </Location>
    

This would set the template for MyApp::Main to be foo.tt. This is all fine, but how do you retrieve this value from within MyApp::Main? Like everything else, this is as painless as possible. In MyApp::Main you only need to do the following:

package MyApp::Main;

    use strict;
    use warnings;

    # Use our param module
    use MyApp::Params;

    # include other various modules here....

    sub handler {
        my $r = shift;

        # Create an instance of our param object
        my $params = MyApp::Params->new;

        # Retrieve a hash ref of our custom configuration values
        my $config = $params->get_config( $r );

        # Retrieve the value for Template
        my $template = $$config{'Template'};

    }

    1;
    

Hopefully you can see how easy it is to take advantage of this in your applications.

More complex configurations...

While ModPerl::ParamBuilder makes it easy to use single argument directives, there are several other options available to you.

Calling $builder->on_off('AutoCommit'); will build a directive that takes either On or Off as its argument. I typically use this for directives like AutoCommit or Caching.

Much like the on_off() option, there is also the yes_no() method. This creates a directive that takes Yes or No as the argument.

With more complicated directives you pass a hash of options to the param() method. This will let you have more control over how your directive behaves. The options you can pass are:

  • name — The directive itself as it should be used in httpd.conf
  • key — The hash key to use to store this directive, this defaults to name
  • err — This lets you define a custom error message that Apache will emit if the argument(s) to the directive are incorrect. If no error message is given, Apache uses the default for the take defined below.
  • func — You can pass in a function reference to use to process and validate the data from httpd.conf instead of using the default functions provided by ModPerl::ParamBuilder.
  • take — This tells Apache how many arguments to take, how to process them, and which arguments are mandatory.

take has many options, below is a table that describes them. Option is what is passed to take and Keys are the hash keys used to store the arguments from httpd.conf.

Option Description Keys
1 or one Take on argument Directive ARG same as name
2 or two Take two arguments Directive ARG1 ARG2 arg1 => ARG1, arg2 => ARG2
3 or three Take three arguments Directive ARG1 ARG2 ARG3 arg1 => ARG1, arg2 => ARG2, arg3 => ARG3
12 or one_plus Take one mandatory argument with an optional second Directive ARG1 [ARG2] arg1 => ARG1, arg2 => ARG2
23 or two_plus Take two mandatory arguments with an optional third Directive ARG1 ARG2 [ARG3] arg1 => ARG1, arg2 => ARG2, arg3 => ARG3
123 or one_plus_two Take one mandatory argument with an optional second and third Directive ARG1 [ARG2] [ARG3] arg1 => ARG1, arg2 => ARG2, arg3 => ARG3
list Take an arbitrarily long list of arguments that are space separated Directive ARG1 ARG2 ... ARGN same as name, but returns a list
one_plus_list Take a mandatory argument followed by an arbitrarily long list of additional arguments. Directive ARG1 ARG2 .. ARGN same as name, but returns a list

The first six types should be fairly self explanatory, but here is a short example of how to use a take 123.

In MyApp::Params add....

# A silly directive that takes database user, pass, and dbname
    $builder->param( {
                       name => 'UPD',
                       key  => 'user_pass_dbname',
                       err  => 'UPD [username [password] [dbname]',
                       take => '123',
    });
    

Retrieve the values in MyApp::Main....

# Retrieve the user, pass, and dbname
    my $params = MyApp::Params->new;
    my $config = $params->get_config;

    my $user   = $$config{'user_pass_dbname'}{'arg1'};
    my $pass   = $$config{'user_pass_dbname'}{'arg2'};
    my $dbname = $$config{'user_pass_dbname'}{'arg3'};
    

As you can see the arguments are stored in a hash which can be retrieved via the name given to param().

Using take list

Using take list is essentially the same as all of the other examples we have used, except that instead of having a single value or a hash of values, it returns an array. So in MyApp::Main you would retrieve a take list directive named 'Foo' like so:

my @foos = $$config{'Foo'};

Using take one_plus_list

one_plus_list is a little bit different in that it returns a list based upon the first argument passed to the directive. So if you had a directive defined like:

MyOnePlusListDirective foo bar baz

It would be retrieve like this:

my @one_plus_list = $$config{'MyOnePlusListDirective'}{'foo'};

This allows you to define parameters like the built in Apache directive AddHandler.

Conclusion

Hopefully this short tutorial has shown you how easy it is to use ModPerl::ParamBuilder when needing a custom Apache directive. If you have any further questions or find an error this page please do not hesitate to contact me directly.