I’ve been meaning to give HTML::FormHandler a try for quite some time now. Over the weekend I converted a form from some home-grown form validation and html generation to use HTML::FormHandler instead. HTML::FormHandler was inspired by, and quasi-forked from, Form::Processor. There are many options for processing forms, such as FormValidator::Simple (Catalyst’s default) and Data::FormValidator, to name a few.
Up until now I’ve always thought that the existing form handling/validating modules on CPAN were incredibly lacking. They all have these crazy and inconsistent APIs that in the end limit what you are able to do. I don’t expect a form validation API to handle every possible situation, but I do expect it to provide a consistent API that is well documented and allows me to easily extend if I need additional functionality. Plus none of them use Moose (AFAIK), which isn’t just me being a Moose evangelist, its also practical because Moose provides mechanisms to extend functionality via roles and to provide reusable validation with MooseX::Types.
HTML::FormHandler solves these issues. HTML::FormHandler can be a complete end-to-end solution for declaring form fields and types, applying server-side validation, generating the HTML for a form, and applying form submits to the database. Almost everything and the kitchen sink is there, except client-side form validation, which can be added separately of course. Normally a “do everything” package scares me, especially for something like form validation. But, Gerda Shank (the author of HTML::FormHandler) did a great job splitting out the various pieces of HTML::FormHandler so that any piece of the form handling process can be subverted to your own needs, and there is copious amounts of documentation explaining how to do so.
The HTML::FormHandler::Manual::Intro does a great job introducing HTML::FormHandler.
There are a few downsides to this module’s current state. It is relatively new and is still stabilizing. For example, just recently the results of form validation were separated from the form object itself. This is a huge fundamental change, but a necessary one. Also, the concept of widgets was recently introduced. Widgets are a great way to extend and customize the display of form fields, and the form as a whole. But, this was just introduced and is still experimental, and has some rough edges. Despite this, the widget design is very promising and I ended up using it myself (thanks to HTML::FormHandler::Manual::Rendering) as my preferred rendering method.
Enjoy.
The CPAN (Comprehensive Perl Archive Network) distribution, Path::Class, provides a simple OO API alternative to the built-in perl file IO functions such as open, opendir, stat, etc. Code written to use Path::Class tends to be smaller (concise, easier to maintain, etc) than similar code that uses the standard built-in functions.
Here is an example of how a small utility might be written without Path::Class:
print "Find all files that are less than 10k in size...\n";
my $dir = '/some/dir';
opendir( DIR, $dir );
my @files = readdir( DIR );
closedir( DIR );
foreach my $file (@files) {
next if !-f "$dir/$file";
if ( (stat "$dir/$file")[7] < 1024 * 10) {
print "$dir/$file\n";
}
}
And here it is re-written to use Path::Class:
use Path::Class;
print "Find all files that are less than 10k in size...\n";
my $dir = dir('/some/dir');
while (my $file = $dir->next()) {
next if $file->is_dir();
if ($file->stat->size() < 1024 * 10) {
print "$file\n";
}
}
Using Path::Class is a no-brainer when you compare <code lang="perl" inline="1">(stat "$dir/$file")[7]</code> with <code lang="perl" inline="1">$file->stat->size()</code>.
One of Path::Class’s greatest stengths is that it is very simple and many of the features it provides are done so by glueing with other well-tested modules that have been on CPAN for a long time. These modules include IO::Dir, IO::File, File::Path, and File::stat.
Some other highlights include:
I’ve been using this module at $work for a while now and am very happy with it and have had coworkers tell me, after they gave it a try, how much cleaner their code ended up being because they used this module.
Enjoy!
Update: Catalyst now comes with an upgrade document: http://search.cpan.org/perldoc?Catalyst::Upgrading
Over the last few days at $work I’ve been upgrading Catalyst from 5.7 to 5.8. This new version of catalyst, released in April, has been completely overhauled to use Moose for it’s OO system, among other enhancements and bug fixes.
I found that the Catalyst::Manual::CatalystAndMoose manual was a great starting point to using Moose with Catalyst. Also, Catalyst::Upgrading has specific points to consider when upgrading to 5.8. During the initial install of Catalyst 5.8 I was told that I had a few related modules installed, that while not Catalyst prerequisites, I should upgrade them as they were known to fail on the new Catalyst. I was lucky that I caught this message, as cpan did not pause to wait for me to read it and went on its marry way building, testing, and installing Catalyst.
Once installed I went ahead and also upgraded some related modules we use such as Catalyst::Plugin::Authentication, DBIx::Class, Catalyst::Plugin::Session, Catalyst::Authentication::Store::DBIx::Class, etc. This way I could test all these upgrades all at once instead of one at a time.
Next I modified all my custom plugins to use MRO::Compat instead of Class::C3 per the documentation in Class::C3::Adopt::NEXT. But, then I realized I might as well just rewrite all my plugins as Moose::Roles as its pretty easy to convert a plugin to a role and you end up with much cleaner code.
I started Apache up and things mostly worked. There were a few backwards incompatibilities that I wasn’t aware of, such as there were a few cases where developers had done this:
sub index : Local { }
Instead of:
sub index : Private { }
In our previous version of catalyst (5.7) using :Local, while not documented, worked just like :Private if the subroutine was named index. In 5.8 only :Private will cause a handler named index to be the default handler if the URL resolves to the controller only. This was an easy fix. There were a couple other small issues, but nothing major. I found and fixed a bug in Catalyst::Model::DBIC::Schema and added the support for the compute() method to Catalyst::Plugin::Cache. Both of these changes reduced the amount of changes I needed to make to my own code.
A couple hours in to it I was upgraded and everything was working. Of course I spent the next few days upgrading/enhancing/rewriting Catalyst bits that could be done much more elegantly, and with less code, using Moose.
On a side note, during the upgrade I found this great quote in Catalyst::Model::Adaptor:
Catalyst is glue, not a way of life!
Amen! Most people, myself included, start out by writing all their business logic in their Controllers because that is what MVC told them to do. Don’t do it! Catalyst is meant to be a GLUE - most of your application should be written in a way that it doesn’t give a flying crap how, or by whom, it is used. Put your business logic in modules that optionally expose themselves as Models (perhaps via Catalyst::Model::Adaptor) or just by adding a use statement in your controller that then uses that module. Your controllers should be little more than code to glue a data source with the view and provide some input validation.
I’ve been waiting a long time for this Moosification and its been a joy working with this new Catalyst. Next up to bat will be the Moosification of DBIx::Class! Think we’ll get it by Christmas? :)
Don’t forget to buy the new Catalyst Book! If purchasing from Amazon make sure you follow this link to ensure that the Enlightened Perl Organisation gets a cut of the sale.
I’ve been authoring CPAN distribution since early 2003 with my first module being Geo::Distance. Since then I’ve authored several other distributions and do my best to keep my RT bug list small and fix FAILs. This process of authoring and maintaining modules on CPAN is in my top-10 list of all-time most educational experiences of my career.
I strongly encourage anyone that wants to become a better developer to take on the task and create a useful distribution for CPAN and/or work on an existing distro. There are plenty of projects out there (such as Moose, DBIx::Class, and Catalyst, to name a few) that need help and can use all the hands that they can get.
Now, if you would instead like to create your own distribution on CPAN, go for it! Its really quite easy and fun. But, it is VERY important that you think long and hard about what you want to release on CPAN. It may seem that every possible task that someone might want to accomplish in Perl is already available on CPAN - this is false. I regularly find myself needing tools that aren’t yet on CPAN. Don’t just write something for the sake of getting something on CPAN - develop something that is useful, of high quality, has a decent suite of automated tests, and doesn’t duplicate a module that is already on CPAN.
Once you developed a module or two, and you are ready to get it out the door, you’ll want to do a couple things. First, create a directory to hold your distribution. Assuming your module is named Acme::Yesterday:
mkdir Acme-Yesterday
cd Acme-Yesterday
vi Makefile.PL
You’ll want to install Module::Install first, before you go any further as it provides a much easier to configure build/test/install process than the old-school ExtUtils::MakeMaker.
Now, your Makefile.PL will look something like this:
use inc::Module::Install::DSL 0.91;
all_from lib/Acme/Yesterday.pm
requires DateTime
test_requires Test::More 0.42
(Notice the first line DOES have a semicolon at the end, while the rest do not)
Next, create a directory to put your module(s):
mkdir lib
mkdir lib/Acme
vi lib/Acme/Yesterday.pm
Now, your module will need some minimum requirements such as declaring the version of the distribution, the license type, and a name. Also, most CPAN modules follow a common convention for documentation using POD. Here is the most minimal of a module:
package Acme::Yesterday;
use strict;
use warnings;
our $VERSION = 0.01;
=head1 NAME
Acme::Yesterday - Make time() return the same hour,
second, and even minute as exactly 24 hours ago!
=head1 SYNOPSIS
use Acme::Yesterday;
=head1 DESCRIPTION
I'm an interesting introduction to this module.
=cut
use DateTime;
=head1 METHODS
=head2 some_method
some_method();
Description of some_method().
=cut
sub some_method { ... }
1;
=head1 AUTHOR
Your Name <your@email>
=head1 COPYRIGHT
This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.
Alternatively you can use Module::Starter to create a shell of a module for you.
Now for automated tests. At a minimum you should write a test that verifies that your module can even be loaded. Here’s how to do that:
mkdir t
vi t/00_use.t
The content of 00_use.t should be something like:
#!/usr/bin/perl -w
use strict;
use warnings;
use Test::More tests => 1;
BEGIN{ use_ok('Acme::Yesterday'); }
You’ll need to make a couple more files:
echo 'perldoc Acme::Yesterday' > README
echo '0.01 - First release.' > Changes
OK, now just sit back and let Module::Install do its work:
perl Makefile.PL
make dist
make disttest
Assuming your tests pass when you run make disttest, you’re set to put your module on CPAN. The above steps would have now created for you a Acme-Yesterday-0.01.tar.gz. This is the file that you need to upload to CPAN. In order to upload a distribution to CPAN you’ll need to get a pause account. Once your request has been approved you can login to pause, go to the upload page, and upload your tarball. Within an hour or two your new distribution will be availabe on CPAN.
Once you’ve done this you’ll want to clean up your distribution directory as running the various make steps leaves around some files you don’t need:
make distclean
Good luck!
So, I’ve been hearing about this CPAN Top 100 list but nobody seems to want to provide a link to it in their posts. Well, I dug that out and come to find that one of my very own modules is in the top 100… FAIL list. Woot! Anyways, I fixed it up and all should be well in an hour or two when everything gets mirrored out/re-indexed/etc by CPAN. There are 3 different top 100 lists on the site, all of them are interesting, so go take a look. It tickles me that Reaction is in the 100 Heavy list. Reaction is mst’s wrapper around Catalyst that is still in very early development. It looks promising.
I’ve just uploaded Class::Measure 0.04 to CPAN. Get it here. The changes for this release are optimizations and cleanups in preparation for using Moose in the next release. Class::Measure provides a simple and intuitive way to treat units of measure as basic data types with support for mathematical operations.
use Class::Measure::Length qw( length );
my $width = (length(2, 'feet') + length(3, 'yards')) * 2.8;
print 'Inches: ' . $width->inches() . "\n";
Currently the only supported units of measure are length. Sometime in the next release or two I’ll be adding support for density, frequency, volume, etc.
If you’re used to regular OO perl you’ll recognize this:
package Dog;
use strict;
use warnings;
sub new {
my ($class, %args) = @_;
$args{name} ||= 'Spot';
return bless \%args, shift;
}
sub bark {
my ($self) = @_;
print $self->{name} . " barks!\n";
}
1;
With Moose this becomes drastically simpler and more powerful:
package Cat;
use Moose;
has 'name' => (is=>'ro', isa=>'Str', default=>'Fluffy');
sub meow {
my ($self) = @_;
print $self->name() . " meows!\n";
}
1;
You may hear that Moose is slow. Don’t let that throw you off, it only adds a fraction of a second to startup time, and is exceptionally fast during run-time. There are much more important things to be concerned about. Moose will make your code cleaner, easier to change and extend, and makes coding perl oo quite enjoyable.
I wrote this for the TO.pm meeting on June 10th: