[Dbix-class] InflateColumn::DateTime, inheritance, and insert/delete problems

Peter Rabbitson rabbit+dbic at rabbit.us
Wed Mar 11 07:53:19 GMT 2009


Steve Caldwell wrote:
> Consider the following script:
> 
> #!/usr/bin/perl
> use strict;
> use warnings;
> 
> # mysql>
> #   create database foo;
> #   create table foo.tblfoo (
> #     fooid integer not null auto_increment,
> #     mydate datetime,
> #     primary key (fooid)
> #   );
> #   grant all privileges on foo.* to foouser at localhost
> #     identified by 'foopassword';
> 
> use DateTime;
> 
> {
>     package My::Plugin::RowStuff;
>     sub insert {
>         my $self = shift;
>         warn "I'm going to do something before inserting";
>         $self->next::method(@_);
>     }
>     sub delete {
>         my $self = shift;
>         warn "I'm going to do something before deleting";
>         $self->next::method(@_);
>     }
>     1;
> 
>     package My::SchemaBase;
>     use base 'DBIx::Class';
>     __PACKAGE__->load_components(qw/
>         InflateColumn::DateTime
>         +My::Plugin::RowStuff
>         Core
>     /);
>     1;
> 
>     package My::Schema::Foo;
>     use base 'My::SchemaBase';
>     __PACKAGE__->table('tblfoo');
>     __PACKAGE__->add_columns(
>         fooid  => {},
>         mydate => { data_type => 'datetime' },
>     );
>     __PACKAGE__->set_primary_key('fooid');
>     1;
> 
>     package My::Schema;
>     use base 'DBIx::Class::Schema';
>     My::Schema->load_classes(qw/Foo/);
>     1;
> }
> 
> my $schema = My::Schema->connect(
>     'DBI:mysql:database=foo', 'foouser', 'foopassword'
> );
> 
> my $obj = $schema->resultset('Foo')->create({
>     mydate => DateTime->now(),
> });
> 
> $obj->delete();
> 
> exit 0;
> 
> 1;
> 
> 
> When I run this, my custom insert and delete methods in
> My::Plugin::RowStuff do not get run.  I can't figure out why not, but I
> have identified the following:
> 
> 1) removing InflateColumn::DateTime will make it work
> 
> 2) removing the inheritance of My::SchemaBase will work - i.e. have
> My::Schema::Foo inherit directly from DBIx::Class and call
> load_components(...) in that package.
> 
> 3) adding skeleton insert and delete methods to My::SchemaBase will
> work, so that it now looks like:
> 
>     package My::SchemaBase;
>     use base 'DBIx::Class';
>     __PACKAGE__->load_components(qw/
>         InflateColumn::DateTime
>         +My::Plugin::RowStuff
>         Core
>     /);
>     sub insert { shift->next::method(@_) }
>     sub delete { shift->next::method(@_) }
>     1;
> 
> As #1 and #2 aren't viable for my codebase, I'm going with the odd #3
> solution.  Does anyone know why InflateColumn::DateTime is exhibiting
> this behavior?
> 

Sadly this is a weird MRO problem, and not really a DBIC issue. Consider:

#!/usr/bin/perl
use strict;
use warnings;

# Everything ultimately inherits from this
package RootPkg;
sub root {__PACKAGE__};

# Chain 1 - Extra -> Extra::Sub -> RootPkg
package Extra::Sub;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/+RootPkg/;

package Extra;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/Sub/;


#Chain2 - BasePkg --> BasePkg::Sub1 --> RootPkg
#                 \-> BasePkg::Sub2 -/
package BasePkg::Sub1;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/+RootPkg/;

package BasePkg::Sub2;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/+RootPkg/;

package BasePkg;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/+Extra Sub1 Sub2/;


# Two end users
package User1;
use base qw/BasePkg/;

package User2;
use base qw/Class::C3::Componentised/;
sub component_base_class { __PACKAGE__ };
__PACKAGE__->load_components qw/+Extra +BasePkg::Sub1 +BasePkg::Sub2/;



package main;
use Data::Dumper;
use MRO::Compat;

# just to be sure
Class::C3::reinitialize();

$Data::Dumper::Sortkeys = 1;
print Dumper {
  "MRO after User1 use base BasePkg (which load_components qw/+Extra Sub1 Sub2/)" => mro::get_linear_isa ('User1'),
  'MRO after User2->load_components qw/+Extra +BasePkg::Sub1 +BasePkg::Sub2/' => mro::get_linear_isa ('User2'),
};


Which outputs:
$VAR1 = {
          'MRO after User1 use base BasePkg (which load_components qw/+Extra Sub1 Sub2/)' => [
                                                                                               'User1',
                                                                                               'BasePkg',
                                                                                               'Extra',
                                                                                               'Extra::Sub',
                                                                                               'RootPkg',
                                                                                               'Class::C3::Componentised',
                                                                                               'BasePkg::Sub1',
                                                                                               'BasePkg::Sub2'
                                                                                             ],
          'MRO after User2->load_components qw/+Extra +BasePkg::Sub1 +BasePkg::Sub2/' => [
                                                                                           'User2',
                                                                                           'Extra',
                                                                                           'Extra::Sub',
                                                                                           'BasePkg::Sub1',
                                                                                           'BasePkg::Sub2',
                                                                                           'RootPkg',
                                                                                           'Class::C3::Componentised'
                                                                                         ]
        };



Your case is User1, where InflateColumn::DateTime == Extra, RootPkg == DBIx::Class::Row
(which implements the terminating insert() and delete() ) and and My::Plugin::RowStuff
== BasePkg::Sub1. The rest you can deduce yourself :)




More information about the DBIx-Class mailing list