[Dbix-class] What is the actual problem with component load order and how to fix it?

Daniel Böhmer post at daniel-boehmer.de
Sat Jan 16 22:32:53 GMT 2021


Dear DBIC folks,

I’m fighting with DBIC components from CPAN and my own modules
and their load order. I’ve spent some time on research
and now would very much appreciate your insights.

We've just had a bug where our app's code tried to set a
boolean column to Perls default false value, the empty string ''.
I have a custom component that inherits from FilterColumn
and makes all boolean columns coerce values to 0 or 1.
https://github.com/dboehmer/coocook/blob/issue142/lib/Coocook/Schema/Component/Result/Boolify.pm

I don't care to inflate boolean values to some blessed
objects when reading from the database because reading
0 or 1 yields the correct truthiness and is fine.
That's why I don't use
https://metacpan.org/pod/DBIx::Class::InflateColumn::Boolean

I’ve found https://blog.afoolishmanifesto.com/posts/mros-and-you/
but unfortunately I still don't understand the issue 100%
and it seems my problem doesn't have the exact same cause.

At the bottom of this email you'll find a test output
with the inheritance chain as reported by the mro module
displayed similarly to the article. The test run is
the version before my false fix. It passes for some
tables and fails for others.

I’ve pushed a row of debugging/fiddling commits to an
issue branch to track this down:
https://github.com/dboehmer/coocook/tree/issue142

# 34d6958 Rewrite t/schema_Component_Boolify.t to test actual bool 
columns

The original version of this test tested only a
minimal, artificial, in-memory resultset. I fixed
it to test the boolean columns of the actual schema.
It failed immediately what is a good thing for debugging.

# f539f75 Make every DBIC component have a base class

I've read about this in the article and just tried
to make sure every class has a base class. No change.

# 37e66bc Use mro 'c3' in all classes that don't aleady use Moose

The article is all about the inheritance order but
manually setting C3 in all modules that were not
already Moose classes also didn't change anything.

# f1964fc Centralize load_components('Ordered') in Schema::Result

I moved to loading of DBIx::Class::Ordered to my
custom Result base class and played with the order
of entries. This actually fixed the test written
above but, of course, not all my tables are
ordered and have a respective column. The app
and are fair amount of other tests broke.


1) What exactly is the problem here?

2) How to fix it so that only selected tables can
load the Ordered component?


Kind regards
Daniel Böhmer


Here the test output:

$ git co 37e66bc
HEAD is now at 37e66bc Use mro 'c3' in all classes that don't aleady use 
Moose
$ prove -Ilib t/schema_Component_Boolify.t -v
t/schema_Component_Boolify.t ..
# mro::get_linear_isa('Coocook::Schema::Result::Item'):
#  - Coocook::Schema::Result::Item
#  - Coocook::Schema::Component::Result::Convertible
#  - Coocook::Schema::Result
#  - Coocook::Schema::Component::ProxyMethods
#  - Coocook::Schema::Component::Result::Boolify
#  - DBIx::Class::FilterColumn
#  - DBIx::Class::Helper::Row::SelfResultSet
#  - DBIx::Class::TimeStamp
#  - DBIx::Class::DynamicDefault
#  - DBIx::Class::InflateColumn::DateTime
#  - DBIx::Class::Core
#  - DBIx::Class::Relationship
#  - DBIx::Class::Relationship::Helpers
#  - DBIx::Class::Relationship::HasMany
#  - DBIx::Class::Relationship::HasOne
#  - DBIx::Class::Relationship::BelongsTo
#  - DBIx::Class::Relationship::ManyToMany
#  - DBIx::Class::Relationship::Accessor
#  - DBIx::Class::Relationship::CascadeActions
#  - DBIx::Class::Relationship::ProxyMethods
#  - DBIx::Class::Relationship::Base
#  - DBIx::Class::InflateColumn
#  - DBIx::Class::PK::Auto
#  - DBIx::Class::PK
#  - DBIx::Class::Row
#  - DBIx::Class::ResultSourceProxy::Table
#  - DBIx::Class::ResultSourceProxy
#  - DBIx::Class
#  - DBIx::Class::Componentised
#  - Class::C3::Componentised
#  - DBIx::Class::AccessorGroup
#  - Class::Accessor::Grouped
#  - Moose::Object
# Subtest: Item.purchased
     ok 1 - '' => 0
     ok 2 - 42 => 1
     1..2
ok 1 - Item.purchased
# mro::get_linear_isa('Coocook::Schema::Result::Project'):
#  - Coocook::Schema::Result::Project
#  - Coocook::Schema::Result
#  - Coocook::Schema::Component::ProxyMethods
#  - DBIx::Class
#  - DBIx::Class::Componentised
#  - Class::C3::Componentised
#  - DBIx::Class::AccessorGroup
#  - Class::Accessor::Grouped
#  - Coocook::Schema::Component::Result::Boolify
#  - DBIx::Class::FilterColumn
#  - DBIx::Class::Row
#  - DBIx::Class::Helper::Row::SelfResultSet
#  - DBIx::Class::TimeStamp
#  - DBIx::Class::DynamicDefault
#  - DBIx::Class::InflateColumn::DateTime
#  - DBIx::Class::InflateColumn
#  - DBIx::Class::Core
#  - DBIx::Class::Relationship
#  - DBIx::Class::Relationship::Helpers
#  - DBIx::Class::Relationship::HasMany
#  - DBIx::Class::Relationship::HasOne
#  - DBIx::Class::Relationship::BelongsTo
#  - DBIx::Class::Relationship::ManyToMany
#  - DBIx::Class::Relationship::Accessor
#  - DBIx::Class::Relationship::CascadeActions
#  - DBIx::Class::Relationship::ProxyMethods
#  - DBIx::Class::Relationship::Base
#  - DBIx::Class::PK::Auto
#  - DBIx::Class::PK
#  - DBIx::Class::ResultSourceProxy::Table
#  - DBIx::Class::ResultSourceProxy
#  - Moose::Object
# Subtest: Project.is_public
     ok 1 - '' => 0
     ok 2 - 42 => 1
     1..2
ok 2 - Project.is_public
# mro::get_linear_isa('Coocook::Schema::Result::RecipeIngredient'):
#  - Coocook::Schema::Result::RecipeIngredient
#  - Coocook::Schema::Component::Result::Convertible
#  - DBIx::Class::Ordered
#  - Coocook::Schema::Result
#  - Coocook::Schema::Component::ProxyMethods
#  - Coocook::Schema::Component::Result::Boolify
#  - DBIx::Class::FilterColumn
#  - DBIx::Class::Helper::Row::SelfResultSet
#  - DBIx::Class::TimeStamp
#  - DBIx::Class::DynamicDefault
#  - DBIx::Class::InflateColumn::DateTime
#  - DBIx::Class::Core
#  - DBIx::Class::Relationship
#  - DBIx::Class::Relationship::Helpers
#  - DBIx::Class::Relationship::HasMany
#  - DBIx::Class::Relationship::HasOne
#  - DBIx::Class::Relationship::BelongsTo
#  - DBIx::Class::Relationship::ManyToMany
#  - DBIx::Class::Relationship::Accessor
#  - DBIx::Class::Relationship::CascadeActions
#  - DBIx::Class::Relationship::ProxyMethods
#  - DBIx::Class::Relationship::Base
#  - DBIx::Class::InflateColumn
#  - DBIx::Class::PK::Auto
#  - DBIx::Class::PK
#  - DBIx::Class::Row
#  - DBIx::Class::ResultSourceProxy::Table
#  - DBIx::Class::ResultSourceProxy
#  - DBIx::Class
#  - DBIx::Class::Componentised
#  - Class::C3::Componentised
#  - DBIx::Class::AccessorGroup
#  - Class::Accessor::Grouped
#  - Moose::Object
# Subtest: RecipeIngredient.prepare
     not ok 1 - '' => 0

     #   Failed test ''' => 0'
     #   at t/schema_Component_Boolify.t line 31.
     #          got: ''
     #     expected: '0'
     not ok 2 - 42 => 1

     #   Failed test '42 => 1'
     #   at t/schema_Component_Boolify.t line 35.
     #          got: '42'
     #     expected: '1'
     1..2
     # Looks like you failed 2 tests of 2.
not ok 3 - RecipeIngredient.prepare

#   Failed test 'RecipeIngredient.prepare'
#   at t/schema_Component_Boolify.t line 36.
# mro::get_linear_isa('Coocook::Schema::Result::DishIngredient'):
#  - Coocook::Schema::Result::DishIngredient
#  - Coocook::Schema::Component::Result::Convertible
#  - DBIx::Class::Ordered
#  - Coocook::Schema::Result
#  - Coocook::Schema::Component::ProxyMethods
#  - Coocook::Schema::Component::Result::Boolify
#  - DBIx::Class::FilterColumn
#  - DBIx::Class::Helper::Row::SelfResultSet
#  - DBIx::Class::TimeStamp
#  - DBIx::Class::DynamicDefault
#  - DBIx::Class::InflateColumn::DateTime
#  - DBIx::Class::Core
#  - DBIx::Class::Relationship
#  - DBIx::Class::Relationship::Helpers
#  - DBIx::Class::Relationship::HasMany
#  - DBIx::Class::Relationship::HasOne
#  - DBIx::Class::Relationship::BelongsTo
#  - DBIx::Class::Relationship::ManyToMany
#  - DBIx::Class::Relationship::Accessor
#  - DBIx::Class::Relationship::CascadeActions
#  - DBIx::Class::Relationship::ProxyMethods
#  - DBIx::Class::Relationship::Base
#  - DBIx::Class::InflateColumn
#  - DBIx::Class::PK::Auto
#  - DBIx::Class::PK
#  - DBIx::Class::Row
#  - DBIx::Class::ResultSourceProxy::Table
#  - DBIx::Class::ResultSourceProxy
#  - DBIx::Class
#  - DBIx::Class::Componentised
#  - Class::C3::Componentised
#  - DBIx::Class::AccessorGroup
#  - Class::Accessor::Grouped
#  - Moose::Object
# Subtest: DishIngredient.prepare
     not ok 1 - '' => 0

     #   Failed test ''' => 0'
     #   at t/schema_Component_Boolify.t line 31.
     #          got: ''
     #     expected: '0'
     not ok 2 - 42 => 1

     #   Failed test '42 => 1'
     #   at t/schema_Component_Boolify.t line 35.
     #          got: '42'
     #     expected: '1'
     1..2
     # Looks like you failed 2 tests of 2.
not ok 4 - DishIngredient.prepare

#   Failed test 'DishIngredient.prepare'
#   at t/schema_Component_Boolify.t line 36.
# mro::get_linear_isa('Coocook::Schema::Result::Unit'):
#  - Coocook::Schema::Result::Unit
#  - Coocook::Schema::Result
#  - Coocook::Schema::Component::ProxyMethods
#  - DBIx::Class
#  - DBIx::Class::Componentised
#  - Class::C3::Componentised
#  - DBIx::Class::AccessorGroup
#  - Class::Accessor::Grouped
#  - Coocook::Schema::Component::Result::Boolify
#  - DBIx::Class::FilterColumn
#  - DBIx::Class::Row
#  - DBIx::Class::Helper::Row::SelfResultSet
#  - DBIx::Class::TimeStamp
#  - DBIx::Class::DynamicDefault
#  - DBIx::Class::InflateColumn::DateTime
#  - DBIx::Class::InflateColumn
#  - DBIx::Class::Core
#  - DBIx::Class::Relationship
#  - DBIx::Class::Relationship::Helpers
#  - DBIx::Class::Relationship::HasMany
#  - DBIx::Class::Relationship::HasOne
#  - DBIx::Class::Relationship::BelongsTo
#  - DBIx::Class::Relationship::ManyToMany
#  - DBIx::Class::Relationship::Accessor
#  - DBIx::Class::Relationship::CascadeActions
#  - DBIx::Class::Relationship::ProxyMethods
#  - DBIx::Class::Relationship::Base
#  - DBIx::Class::PK::Auto
#  - DBIx::Class::PK
#  - DBIx::Class::ResultSourceProxy::Table
#  - DBIx::Class::ResultSourceProxy
#  - Moose::Object
# Subtest: Unit.space
     ok 1 - '' => 0
     ok 2 - 42 => 1
     1..2
ok 5 - Unit.space
1..5
# Looks like you failed 2 tests of 5.
Dubious, test returned 2 (wstat 512, 0x200)
Failed 2/5 subtests

Test Summary Report
-------------------
t/schema_Component_Boolify.t (Wstat: 512 Tests: 5 Failed: 2)
   Failed tests:  3-4
   Non-zero exit status: 2
Files=1, Tests=5,  3 wallclock secs ( 0.05 usr  0.01 sys +  1.66 cusr  
0.15 csys =  1.87 CPU)
Result: FAIL



More information about the DBIx-Class mailing list