Class::CGI 0.12 to the CPAN

Ovid on 2006-04-21T02:12:27

I've just uploaded Class::CGI 0.12 to the CPAN. It's the same module I uploaded earlier today, but with some minor documentation errors fixed. I've also changed all handler names in the documentation away from Class::CGI::HandlerName to My::...::Handler because I don't want to encourage folks using the Class::CGI:: namespace.

The reason for this is straightforward: I want to encourage others to write general purpose handlers and upload them. I plan to write and release Class::CGI::DateTime. With this handler, you'll be able to specify drop-down fields like "day", "month" and "year" and get a DateTime object for them merely by doing this:

use Class::CGI
  handlers => {
    date       => 'Class::CGI::DateTime',
    order_date => 'Class::CGI::DateTime',
  };

my $cgi        = Class::CGI->new;
my $date       = $cgi->param('date');
my $order_date = $cgi->param('order_date');

if ( my %errors = $cgi->errors ) {
    ... handle errors
}

The "order_date" would be handled by expecting parameters named "order_day", "order_month" and "order_year". How this is done internally is an example in the Class::CGI docs.

I've also realized that I need to go ahead and incorporate a suggestion which allows me to specify which parameters will be read by a particular handler. This will allow one to write something like this:

use Class::CGI
  handlers => {
    md5 => { 
      handler => 'Class::CGI::MD5',
      params  => \@param_names,
      seed    => $seed,
    }
  };

my $cgi = Class::CGI->new;
my $md5 = $cgi->param('md5') 
  or checksum_did_not_match_param_values($cgi);

I'll have to give some more thought on how to do that, though. That's beginning to get a bit clumsy, so I don't want to go that route just yet. Making a clean, easy-to-use interface is far more important to me. I could make this work:

use Class::CGI
  handlers => { md5 => \&md5_handler_setup };

That would be easier and give more flexibility, but it might be confusing. Also, the profile file syntax couldn't support that easily, so I might have to implement the profile class idea. This would be useful for larger sites where taking a bit more time with up-front infrastructure is worth the trouble.

Ideas for other general-purpose handlers welcome.

Update: OK, I figured out a good solution for the "md5" problem listed above:

use Class::CGI
  handlers => { md5 => 'Class::CGI::MD5' };

my $cgi = Class::CGI->new;
$cgi->args( md5 => \%args );

my $md5 = $cgi->param('md5');

With that syntax, the hashref set with the &args method will be an extra argument passed to a handler constructor:

package Class::CGI::MD5;

sub new {
  my ($class, $cgi, $param, $arg_hashref) = @_;
  ...
}

And that would be effectively invoked as:

return Class::CGI::MD5->new($cgi, 'md5', $cgi->args('md5'));

It adds a bit more complexity internally, but not much. It also makes things very flexible without making unwarranted assumptions about what the user wants.


Boy that looks familiar :)

Alias on 2006-04-21T12:01:27

The "order_date" would be handled by expecting parameters named "order_day", "order_month" and "order_year". How this is done internally is an example in the Class::CGI docs.

Danger Will Robinson!

I did that too. It hurt.

It was after the second rewrite that I finally ended up with that namespacing and dot-seperators thing.

So...

order_date => 'Class::CGI::DateTime', ... should not break out of "order_date".

Rather, what works really well is that it expects order_date.day, order_date.month, order_date.year fields, and you use something like CGI::Tree to hand Class::CGI::DateTime constructor param of { day => 1, month => 2, year => 3 }.

It makes things really clean, because then Class::CGI doesn't need to care about the actual namespace, it just gets the information it cares about.

And once you get to profile classes, then you can do the same thing.

profile.order_date.day, profile.order_date.month, etc

Re:Boy that looks familiar :)

Ovid on 2006-04-21T17:11:27

First, what is CGI::Tree? I couldn't find that.

Second, I've deliberately adopted an approach which fits parameter names I'm seeing in most forms. I do admit that what you're doing is cleaner, but it's not what folks in forms seem to be doing. However, your approach does seem to be very workable. I'll think about it.

Out of curiosity, why haven't you released any of that to the CPAN, or did I just not see it in your slough of modules?

Re:Boy that looks familiar :)

Alias on 2006-04-23T10:48:57

Sorry, I got "CGI::Tree" from memory, I'll find you the proper name later.

The reason I never released my stuff to CPAN was because it was too integrated into my AppSpace project to be able to break it out properly.

It used a MetaModel (think something a bit like a Moose model) to do both the Class::DBI-like stuff, and the Class::CGI-like stuff. And because it was using MetaModel data to control the widgets, it would have been very difficult to get it out.

Plus I screwed up in that the entire project had a dynamic evolving API, due to the code-generation side of things.

You'll get a good look at the whole project in my I talk at YAPC::NA.

http://use.perl.org/user/Alias/journal/29383

Re:Boy that looks familiar :)

Alias on 2006-04-23T10:50:01

ugh, gotta stop using POD in my HTML :)

Re:Boy that looks familiar :)

Ovid on 2006-04-21T20:18:49

I might addt that here's one way to support your dot syntax:

package My::Handler::DateTime;
use DateTime;

sub new {
    my ($class, $cgi, $param) = @_;
    my @params = qw(day month year);
    if ('date' ne $param) {
        @params = map { "$param.$_" } @params;
    }
    my ($day, $month, $year) = map { $cgi->raw_param($_) } @params;
    return DateTime->new(
        day   => $day,
        month => $month,
        year  => $year,
    );
}

It's really just that simple! That's why I'm really liking Class::CGI. You get a lot of flexibility with very little code.

And what's a profile class?

Not everyone is reinventing your particular wheels

Aristotle on 2006-04-23T03:29:03

order_date => 'Class::CGI::DateTime' should not break out of “order_date”.

Did you actually look at Class::CGI? It has no conventions whatsoever regarding parameter naming. You can do your beloved dot-based namespaces just as well as someone else can choose not to. Given order_date, it is entirely up to Class::CGI::DateTime to decide whether it wants to consume qw( order_year order_month order_day ) or qw( order_date.year order_date.month order_date.day ).

Re:Not everyone is reinventing your particular whe

Alias on 2006-04-23T11:14:19

You sound bitter.

Certainly it is up to order_date what it wants to consume.

Just as it's up to Class::CGI whether it wants to use Class::DBI as well, or up to Class::CGI::DateTime if it wants to spread it's code around into Class::CGI::TextBox and Class::CGI::DropBox.

Except that although you can do this, you shouldn't.

If you have n Class::CGI objects on a page, how do you share out the names? It naturally becomes a namespace, just as Perl uses namespace. That it is a dot is a matter of personal preference, it looks cleaner and feels comfortable.

You could use _ (I did at first) you could use a unicode smiley-face.

But it is a namespace, the the only way to work with namespaces scalably is to stay within the space you are allocated. That's why they work.

Re:Not everyone is reinventing your particular whe

Aristotle on 2006-04-23T12:56:25

I’m not arguing about the merit of namespaces or of employing them. I’m saying that these issues are orthogonal (and hence irrelevant) to Class::CGI’s interface, so there’s no need to keep harping on them. This is the third time you brought it up – it’s okay, we got it. That’s all; no bitterness.