Perl module is a common approach for providing custom sets of functions. But how exactly a module is created, developed, and maintained? In this article, I write down my experience of developing a tiny module named Camellia::Glue.

The scafford

There are several’ external modules for building your own module. The simplest is Module::Build::Tiny. It’s a simple-yet-powerful tool for building, testing and distributing your module.

In my case, I use Dist::Zilla for module management. It’s a widely used framework for Perl module development, with detailed tutorials.

Create the module

Dist::Zilla comes with its own command: dzil, which provides various subcommand to use. For example, to start a new module, say Camellia::Glue, use the command dzil new Camellia::Glue. It creates directory named Camellia-Glue with some default files in it.

$ tree Camellia-Glue/
Camellia-Glue/
├── dist.ini
└── lib
    └── Camellia
        └── Glue.pm

2 directories, 2 files

Two files exists in the newly-created workspace, dist.ini and Glue.pm. The former one stands for the configuration file for dzil to read, and the latter one is very root file of the module.

Export module’s subroutines

Perl uses a CPP-like namespace mechanism to hold all its symbols. To use the symbols defined in the module, one must get them exported first. Perl provides Exporter for this job.

For demonstration purpose, I create a hello subroutine in lib/Camellia/Glue.pm. The file is shown as follow.

# ABSTRACT: An abstract is required at the head of the file.

use strict;
use warnings;
package Camellia::Glue;   # Claim the package

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(hello);  # Export hello so it can be called outside.

sub hello {
  return "hello world";
}

1;    # 1 is required at the end of a module file

Write tests

It’s reasonable to write some tests for the module, to test if it’s implemented correctly.

Perl modules follow a fixed file hierarchy. Tests are put in the folder t/, besides lib/ at the root. All tests files use the suffix .t.

Here, I write a simple test to determine if hello is exported correctly.

# t/01_hello.t

use warnings;
use strict;

use Test::Simple tests => 2;
use Camellia::Glue;

ok(0 == ("hello world" cmp hello()));
ok(0 != ("world hello" cmp hello()));

ok() is a term of assertion used to determine if expression inside meets programmer’s desire.

Please pay attention to the line use Test::Simple tests => 2. This line imports a module Test::Simple that’s commonly used to write Perl tests. The term tests => 2 claims that there are 2 tests (or 2 ok()s, to be specific) to be checked before the test file runs to its end. An error is raised when less ok()s are run.

dzil provides the subcommand dzil test to run all the tests in t/ folder. By doing so, I receive the following result. It says all tests are passed.

$ dzil test
[DZ] building distribution under .build/w82pfJhgyz for installation
[DZ] beginning to build Camellia-Glue
[DZ] guessing dist's main_module is lib/Camellia/Glue.pm
[DZ] writing Camellia-Glue in .build/w82pfJhgyz
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Camellia::Glue
Writing MYMETA.yml and MYMETA.json
cp lib/Camellia/Glue.pm blib/lib/Camellia/Glue.pm
PERL_DL_NONLAZY=1 "/home/retrhelo/Programming/wheel/perl5/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/01_hellotest.t .. ok
All tests successful.
Files=1, Tests=2,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.02 cusr  0.00 csys =  0.03 CPU)
Result: PASS
[DZ] all's well; removing .build/w82pfJhgyz