Class::CGI::DateTime

Ovid on 2006-04-23T00:35:15

I should have Class::CGI::DateTime uploaded tomorrow. It was really simple to write. Here's the bulk of it:

package Class::CGI::DateTime;

use strict;
use warnings;
use DateTime;

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

    my $args = $cgi->args($param);
    my @params = $args ? @$args : qw(day month year);
    if ( 'date' ne $param ) {
        @params = map {"$param.$_"} @params;
    }

    # original param name and param value (yuck)
    my %args = map { /([[:word:]]+)$/; $1, $cgi->raw_param($_) } @params;

    # untaint them puppies
    while ( my ( $arg, $value ) = each %args ) {
        if ( 'time_zone' eq $arg ) {
            $value =~ /^(floating|local|\+\d+|[[:word:]]+\/[[:word:]]+)$/;
            $args{$arg} = $1;
        }
        else {
            $value =~ /^(\d+)$/;
            $args{$arg} = $1;
        }
    }
    return DateTime->new(%args);
}

1;

How many times do you see folks writing date handling code where they don't bother to untaint it? I see it quite a bit. The validation, by the way, is left to DateTime. It appears to return error messages more suitable for programmers than end users, though.


Data::FormValidator::Constraints::DateTime

jonasbn on 2006-04-24T07:25:12

Have you checked out:

Data::FormValidator::Constraints::DateTime?

Re:Data::FormValidator::Constraints::DateTime

Ovid on 2006-04-24T18:10:30

I am familiar with that. The intent of Class::CGI is to provide a mediator pattern which make the code easier to use and, as a nice side benefit, tends to impose a certain uniformity on form parameters while still providing flexibility. The basic use of the above could be:

use Class::CGI
  handlers => {
    customer   => 'My::Customer::Handler',
    date       => 'Class::CGI::DateTime',
    order_date => 'Class::CGI::DateTime',
  };

my $cgi = Class::CGI->new;
my $customer   = $cgi->param('customer');
my $date       = $cgi->param('date');
my $order_date = $cgi->param('order_date');
my $email      = $cgi->param('email');      # not an object

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

Note how clean that is. By handling the untainting, validating and object construction to handler (mediator) classes, the end code that users write can focus more on the specific problem they want to solve rather than a bunch of grunt-work which, while required, is secondary in nature. More specifically, the grunt-work is readily available to look at, but it doesn't clutter the main code.

In the above, just as Data::FormValidator::Constraints::DateTime allows, there are no "date" or "order_date" form parameters. Instead, we have "day", "month", "year", "order_date.day", "order_date.month", and "order_date.year" parameters. It's easy to specify that you also have, for example, an "hour" parameter:

# must be called before param()
$cgi->args( date => [ qw/ day month year hour / ] );

One thing about Data::FormValidator::Constraints::DateTime which Class::CGI does not do is have a bunch of extra built-in validation methods which handle conditions such as "before today", "after datetime", and so on. I may alter the interface (since I haven't released it yet) to allow that as an optional callback interface, but the decision to separate object validation and business validation was deliberate. For Class::CGI, I recommend that the handler classes only concern themselves with what objects can always be legally created and the primary code deals with its specific problem domain. The first is universal and the second is task specific.

It's not that I think there's anything wrong with Data::FormValidator, I just have a different design philosophy which its interface doesn't support. I like simple code which doesn't do too much, offers the flexibility to get the "extra" stuff done and is easy to learn. With Class::CGI, if you write one or two handlers, you'll pretty much never need to consult the docs again.