[Dbix-class] DBIx::Class::Field -- Feedback Wanted

Dan Kubb dan.kubb at autopilotmarketing.com
Thu Aug 4 21:13:51 CEST 2005


I finished up the work on an alpha version of DBIx::Class::Field I  
mentioned
earlier and I'd like to get the list's opinion on before I go any  
further
with it.  This time I've got real code to show you ;)

Basically this module's purpose is to encapsulate the information about
a single field in a table (or a view).  At a very basic level it stores
the name of the field accessor, as well as the name of the column
it maps to.

So, if this is incorporated into DBIx::Class, there's no more need for
accessor_name hacks; all you'd do is say "this is the field in my table
class, and this is the column it maps to".  If you don't specify the
mapping the column name will just default to the name of the field,
which is what you'd expect to happen.

That part (on its own) is a pretty simple problem to solve, just have a
hash mapping names to columns somewhere.  But what if you need to know
more about a field?  What if you want to attach meta-data about a  
field's
constraints so that the data is validated before a critical insert/ 
update
is performed?  Thats why I wrote DBIx::Class::Field.

With DBIx::Class::Field you specify all the meta-data about a field
in one single place.  Each field can have one or more types, which
allow it to describe different types of meta-data.

For example, there's a string type, which allows you to specify the
minimum and maximum length of the field, a regular expression to match
the string against and other checks you'd want to perform on a string.
There's also a number type, an object type, and others that I specified
in greater detail in an earlier message to the list.  Oh, its dead
simple to add new types too.

Each field is defined in it's own class, and can inherit from one
or more types, like this:

   package My::Table::customer_id;

   use base qw(
       DBIx::Class::Field::Singleton
       DBIx::Class::Field::Type::column
       DBIx::Class::Field::Type::number
       DBIx::Class::Field::Type::identifier
       DBIx::Class::Field::Type::auto_increment
   );

   __PACKAGE__->set_instance({
       name           => 'customer_id',
       label          => 'Customer ID',
       column_name    => 'weird_name_in_the_db_id',
       table          => 'My::Table',
       inflate        => \&inflate,
       deflate        => \&deflate,
       min_range      => 1,
       max_range      => 2**32 - 1,
       min_fractional => 0,
       max_fractional => 0,
   });

Now I realize this is really verbose.  Thats why a lot
of this can be auto-discovered from the existing table
using an approach similar to Class::DBI::Loader.  I certainly
don't expect people type all this in for every field ;)

The main benefit to having the field be an object is that
the object can have built-in validation rules in a single
place.  Imagine the following code:

   # $field is a My::Table::customer_id object

   my $customer_id = 1;

   # iterate through all the tests and return any errors

   if (my @errors = $field->validate($customer_id)) {
        # the $customer_id is bad.  @errors has more
        # details specifying exactly what rules failed.
   }
   else {
        # the $customer_id is good
   }

This sort of check can be done any place a field is set, or
before an insert/update is performed.  If you wanted to the
@errors could even propagated up to the user GUI.  The constraints
are openly available to any object that wants them -- for
example it would be pretty easy to create a Data::FormValidator
profile directly from the field meta-data if you wished.

I've put DBIx::Class::Field here for download:

   http://www.onautopilot.com/oss/dbixc-field/latest

There's only two things you need to do in order to install this:

   - Install version.pm from CPAN   (if you haven't already)
   - Patch Class::Std               (instructions below)

BTW I strongly recommend looking at Class::Std if you haven't
already.  It can perform most of the same MI dispatching stuff as
NEXT.pm, without some of the drawbacks of NEXT.pm -- check
out how I use  CUMULATIVE in the various classes.  That's just
the beginning too, there's lots of other goodies in CSTD.

An interesting side note is that in Damian's new book he talks about
Class::Std's and NEXT.pm, but he refers to NEXT's approach as
"inherently fragile" in comparison.  I still really like using NEXT,
but from the testing I've performed over the last week I've seen that
much of what NEXT does can be more cleanly and easily done with
Class::Std.

Here's the patch to Class::Std:

   http://www.onautopilot.com/oss/dbixc-field/Class-Std.all.patch

(Damian's going to incorporate this patch, or something very close
shortly after he's done at OSCon)

To patch and install Class::Std just do this:

   1. perl -MCPAN -e 'CPAN::Shell->look("Class::Std");
   2. patch -p0 < /path/to/Class-Std.all.patch
   3. perl Build.PL; ./Build; ./Build test;
   4. ./Build install  (as root or with sudo)

Anyway, give it a look and let me know what you think.  I'm
certainly not married to the approach, this is just a proof of
concept, so if you have a better idea please share it.

Thanks,

Dan Kubb



More information about the Dbix-class mailing list