The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Devel::Maypole - support utilities for developing the Maypole stack

SYNOPSIS

    # =================================================
    # In a test script:
    
    use Test::More tests => 42;
    
    use Devel::Maypole qw/ :test /;
    
    my ( $database, $application );
    
    BEGIN { 
    
        $ENV{MAYPOLE_CONFIG}    = 'config/beerdb.simple.yaml';
        $ENV{MAYPOLE_TEMPLATES} = 't/templates';
    
        $database = database( ddl  => 'sql/ddl/beerdb.simple.sql',
                              data => 'sql/data/beerdb.simple.sql',
                              );
        
        $application = application( plugins => [ qw( Config::YAML AutoUntaint Relationship ) ],
                                    );    
    }
    
    use Test::WWW::Mechanize::Maypole $application, $database;
    
    # ----- BEGIN TESTING -----
    
    # frontpage
    {   
    
        my $mech = Test::WWW::Mechanize::Maypole->new;
        $mech->get_ok("http://localhost/beerdb/");
        
        $mech->content_contains( 'This is the frontpage' );
        
        is($mech->ct, "text/html");
        is($mech->status, 200);
    }
    
    
    # =================================================
    # In an installation script:
    
    use Maypole::Devel qw/ :install /;
    
    # optionally suppress interactive questions:
    # $ENV{MAYPOLE_RESOURCES_INSTALL_PREFIX} = '/usr/local/maypole';
    
    install_templates( 'Maypole::Plugin::Foo', 'distribution-templates-dir/set1', 'set1' );
    
    install_ddl( 'Maypole::Plugin::Foo', 'distribution-sql-dir/ddl/basic', 'basic' );
    install_ddl( 'Maypole::Plugin::Foo', 'distribution-sql-dir/ddl/advanced', 'advanced' );
            

    # =================================================
    # Somewhere else:
    
    package My::App;
    use Devel::Maypole qw/ :find /;
        
    my $templates = find_templates( 'Maypole::Plugin::Foo', 'set1' );

    my $ddl_sql_dir = find_ddl( 'Maypole::Plugin::Foo', 'advanced' );
    

DESCRIPTION

Builds a database and a simple application driver, ready to use in test scripts for Maypole plugins and components. Provides functions for installing resources (SQL files, configurations, templates), and for discovering the directories these resources were installed to at a later date.

EXPORTS

Nothing is exported by default. You can import individual functions, or groups of functions using these tags:

    tag         functions
    -----------------------------------------------------------
    :test       database application run_standard_tests
    :install    install install_templates install_yaml_config install_ddl install_data 
    :find       find    find_templates    find_yaml_config    find_ddl    find_data   

TESTING UTILITIES

database

Builds and populates an SQLite database in a temporary file, and returns a DBI connection string to the database.

Suitable SQL files will be (ASAP) included in the distribution to build a reasonably complex version of the beer database.

Returns a DBI connection string.

Options:

ddl

Path to the SQL DDL (schema) file to use. A couple of suitable files are installed by this distribution - beerdb.default.sql and beerdb.simple.sql.

data

Path to the SQL data file to use. A couple of suitable files are installed by this distribution - beerdb.default.sql and beerdb.simple.sql.

Set false to not unlink the generated database file. Default true (unlink when script exits).

application

Builds a simple Maypole driver in a temporary file in the current directory.

Returns the package name of the application, which will be MaypoleTestApp_XXXXX where XXXXX are random characters.

Options:

plugins

Arrayref of plugin names, just as you would supply to use Maypole::Application.

See Custom driver code below.

config

Hashref of Maypole options, or a Maypole::Config object. See Configuration below.

unlink

Set false to not unlink the generated application file. Default true (unlink when script exits).

run_standard_tests( $application, $database, $templates )

A canned set of tests that should be runnable against most plugins.

NOT YET IMPLEMENTED.

Configuration

You can build up configuration data in your test script, either as a hashref or as a Maypole::Config object, and supply that as the config parameter to application().

Alternatively, include Maypole::Plugin::Config::YAML in the list of plugins, and set $ENV{MAYPOLE_CONFIG} to the path to a config file. See the Maypole::Plugin::Config::YAML docs for details.

This distribution includes a couple of canned config files, in the config subdirectory.

    $ENV{MAYPOLE_CONFIG} = 'path/to/config/beerdb.simple.yaml'; 
    $ENV{MAYPOLE_CONFIG} = 'path/to/config/beerdb.default.yaml'; 
    

You can considerably simplify your config by including Maypole::Plugin::AutoUntaint in the list of plugins - the supplied config files assume this.

The supplied configs also assume Maypole::Plugin::Relationship is included in the plugins.

Custom driver code

If you need to add custom code to the application, you could put the code in a plugin, and supply the name of the plugin to plugins.

Alternatively, eval code in your test script, e.g. from 01.simple.t:

    # For testing classmetadata
    eval <<CODE;
        sub $application\::Beer::classdata: Exported {};
        sub $application\::Beer::list_columns  { return qw/score name price style brewery url/};
    CODE

    die $@ if $@;
    

INSTALLATION SUPPORT

install_* functions

Import these functions with the :install tag.

These functions are intended to be called from a command line script, preferably a Build.PL script. They will interactively ask the user to confirm install locations, unless $ENV{MAYPOLE_RESOURCES_INSTALL_PREFIX} is defined, in which case that location will be used as the root of the install location.

Returns the path in which the material was installed. You should store this in the config package for the module being installed, so that other packages can find your templates. In Build.PL, you can say

    use Devel::Maypole qw/:install/;
    
    my $cfg = $builder->config_data( 'templates' ) || {};
    
    $cfg->{set1} = install_templates( 'Maypole::Plugin::Foo', 'dist-templates-dir', 'set1' );
        
    $builder->config_data( templates => $cfg ); 
    
    # similarly for config files, databases etc.
    

The call to config_data will cause Module::Build to create and install the config package for you.

You can retrieve the install location directly from Maypole::Plugin::Foo::ConfigData, or you can use the find_* functions (see below), which will use Maypole::Plugin::Foo::ConfigData under the hood.

On Unix-like systems, the default install root is /usr/local/maypole/$what, where $what identifies the type of material (i.e. templates, yaml_config, ddl or data).

On Windows, the default is C:/Program Files/Maypole/$what.

$for should be the name of a package. This will be converted into a subdirectory, e.g. Maypole::Plugin::Foo becomes Maypole/Plugin/Foo.

$from is the relative path to the resource directory in your distribution, e.g. templates or sql/ddl.

$set is an optional subdirectory to install to. So if you say

    install_templates( 'Maypole::Plugin::Foo', 'templates/set1', 'set1' )
    

the templates will be installed in /usr/local/maypole/templates/Maypole/Plugin/Foo/set1.

$set defaults to 'default'.

install_templates( $for, $from, [ $set ] )
install_ddl( $for, $from, [ $set ] )
install_data( $for, $from, [ $set ] )
install_yaml_config( $for, $from, [ $set ] )
install( $what, $for, $from, [ $set ] )

The install_* functions all delegate to install, passing $what as the first parameter. Use this function to install other types of material (e.g. install( 'images', 'Maypole::Plugin::Pretty', 'img/themes/sunny', 'sunny' )), or for cycling through several types.

RESOURCE DISCOVERY

find_* functions

Import these functions with the :find tag.

Each of these methods loads the appropriate *::ConfigData package for the package $for, and queries it for the location of the $set set of material.

$set defaults to 'default'.

    use Devel::Maypole qw/ :find /;
    
    my $templates_dir = find_templates( $for, $set );
    
    my $ddl_dir = find_ddl( $for, $set );
    

It is up to you to extract the list of files in the directory.

find_templates( $for, [ $set ] )
find_ddl( $for, [ $set ] )
find_data( $for, [ $set ] )
find_yaml_config( $for, [ $set ] )
find( $what, $for, [ $set ] )

The find_* functions all delegate to find, passing $what as the first parameter. Use this function to locate other types of material (see install()), or for cycling through several types.

RESOURCES INSTALLED BY Devel::Maypole

These are installed under the resource root selected during installation.

templates
    templates/default/*
    
DDL SQL
    ddl/default/beerdb.default.sql
    ddl/default/beerdb.simple.sql
    
data SQL
    data/default/beerdb.default.sql
    data/default/beerdb.simple.sql
    
configs
    yaml_config/default/beerdb.simple.yaml
    

Putting it all together

This is how you might start a test script in the distribution of a Maypole add-on.

Include Devel::Maypole in the requirements for your module. During its installation, the Devel::Maypole resource kit(s), as well as persistent config data recording the installation locations of these resources, will be set up.

    # in a test script for Maypole::Plugin::Foo
    use Test::More tests => 42;
    use Devel::Maypole qw/ :test :find /;
    
    my ( $application, $database );
    
    BEGIN {
    
        my $mp_cfg      = find_yaml_config( 'Devel::Maypole', 'default' );
        my $templates   = find_templates  ( 'Devel::Maypole', 'default' );
        my $ddl         = find_ddl        ( 'Devel::Maypole', 'default' );
        my $data        = find_data       ( 'Devel::Maypole', 'default' );
    
        $ENV{MAYPOLE_CONFIG}    = "$mp_cfg/beerdb.simple.yaml";
        $ENV{MAYPOLE_TEMPLATES} = $templates;
    
        $database = database( ddl  => "$ddl/beerdb.simple.sql",
                              data => "$data/beerdb.simple.sql",
                              );
        
        $application = application( plugins => [ qw( Foo Config::YAML AutoUntaint Relationship ) ],
                                    );    
    }
    
    use Test::WWW::Mechanize::Maypole $application, $database;
    
    # run some canned tests from Devel::Maypole - not implemented yet...
    run_standard_tests( $application );       
    
    # now run some custom tests!
    

In time, more resource kits will be added (in addition to 'default' ), and/or more files will be added to the default resource kit, and canned sets of tests will be added.

TODO

Canned tests

Complex schema, with sufficient data for paging.

More template sets.

Support for other RDBMS's (easy enough to implement, patches welcome).

AUTHOR

David Baird, <cpan@riverside-cms.co.uk>

BUGS

Please report any bugs or feature requests to bug-devel-maypole@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Devel-Maypole. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

ACKNOWLEDGEMENTS

COPYRIGHT & LICENSE

Copyright 2005 David Baird, All Rights Reserved.

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