Re: Problem with before_create triggers & objects

[prev] [thread] [next] [Date index for 2004/12/21]

From: Charles Bailey
Subject: Re: Problem with before_create triggers & objects
Date: 18:42 on 21 Dec 2004
--On Tuesday, December 21, 2004 1:24:20 PM EST -0500 Drew Taylor 
<taylor.andrew.j@xxxxx.xxx> wrote:

> I've finally (Woo Hoo!) tracked down a problem (version 0.95, but also
> present in earlier versions) I've been having with a before_create
> trigger which autofills a "created" column. I am doing the following:
>
> 1) defining a "created" column
> 2) setting up a has_a relationship for the "created" column using my
> date class (which defines a deflate() method for getting mysql
> datetime format, but stringifies via overloading to "MM/DD/YYYY
> HH:MM"),
> 3) adding a before_create trigger to autopopulate the value
>
> package Foo;
> Foo->columns(All=>qw(id foo bar created updated));
> Foo->has_a('created' => 'My::DateTime::Class');
> Foo->add_trigger('before_create', \&_fill_created);
>
> sub _fill_created {
>     my $self = shift;
>      my $date = My::DateTime::Class->now;#->deflate;
>#    $self->_attribute_set('created', $date);
>     $self->set('created', $date);
> }
>
> The problem is that when I call create() and the before_create
> triggers are run I end up with 3 triggers: _deflate_object() as set
> via has_a(), something via an overload call in CDBI, and my
> _fill_created(), usually in that order.
>
> In _deflate_object(), it calls $self->_attribute_exists($col) which
> returns false because my trigger code has not run yet. This is the
> gate keeper to _deflated_column() which does the actual object
> deflation. Then later when my trigger does run it creates the object
> and sets created, but it is not properly deflated because
> _deflate_object() has already run!
>
> So that's the problem in a nutshell. I have a workaround as seen in
> the commented code above: use the low level accessors & set the final
> value directly, which Just Works. But the problem I described above
> was a serious bear to track down. It only manifested itself by
> _usually_ (and not always!) having a blank created column value. I
> guess it sometimes worked because perl gave CDBI the triggers in a
> different order.
>
> I don't know that there is any way to not force the use of the low
> level accessors unless the order of triggers can be set. That would be
> nice because any user level code could by default be set to run before
> any of CDBI's built in triggers.
>
> The simple fix seems to be to document that if you want to set column
> values via a before_create trigger you MUST ALWAYS use the
> _attribute_* methods.

That'd work.  You can get away with the object-level accessors now, too, as 
long as you store the deflated value, but I don't think that's guaranteed.

The real (imho) problem here is that triggers are executed in arbitrary 
order.  Barring a change in the handling of attribute values (which I know 
I've agreed to work on; I'm desperately searching for tuits), it may be 
best to bundle all of your class-specific processing into a single function 
and register that at all of the necessary triggerpoints,  rather than using 
separate functions for defaulting, deflating, constraint checking for 
updates, etc. and having to armor each against the possibility that 
something else in the chain hasn't yet run.


--
Regards,
Charles Bailey  < bailey _at_ newman _dot_ upenn _dot_ edu >
Newman Center at the University of Pennsylvania

Problem with before_create triggers & objects
Drew Taylor 18:24 on 21 Dec 2004

Re: Problem with before_create triggers & objects
Charles Bailey 18:42 on 21 Dec 2004

Generated at 12:15 on 16 Jan 2005 by mariachi v0.52