Catalyst: More Lessons in How Not To Write An Application

Ovid on 2008-03-07T10:16:32

Trying to figure out the source of a method in one of our Catalyst controllers. Our controller inherited from one of our controller base classes. That, in turn, inherited from the deprecated Catalyst::Base which, in turn, does nothing but inherit from Catalyst::Controller, which inherits from three different classes. Two of those inherit from two classes each, which in turn ...

Let's see if I can figure this out our inheritance heirarchy so I can tell where that method is coming from:

Our::Controller
    Our::ControllerBase
        Catalyst::Base (deprecated passthrough)
            Catalyst::Controller
                Catalyst::Component
                    Class::Accessor::Fast
                        Class::Accessor
                    Class::Data::Inheritable
                Catalyst::AttrContainer
                    Class::Accessor::Fast
                        Class::Accessor
                    Class::Data::Inheritable
                  Class::Accessor::Fast
                      Class::Accessor
    Catalyst::Controller::REST
        Catalyst::Controller
            Catalyst::Component
                Class::Accessor::Fast
                    Class::Accessor
                Class::Data::Inheritable
            Catalyst::AttrContainer
                Class::Accessor::Fast
                    Class::Accessor
                Class::Data::Inheritable
              Class::Accessor::Fast
                  Class::Accessor

Got that?

On the off chance that you're a Multiple Inheritance fanboy, I don't think I'm going to say anything right now. I'm on the verge of profanity. MI is a tool of last resort (no, I'm not saying it's always the wrong answer). Today there are so many excellent alternatives that you really have no excuse for using MI other than "I don't like change".


Err...

mauzo on 2008-03-07T12:19:37

perl -MOur::Controller -MSub::Identify=stash_name -le'print stash_name(Our::Controller->can("method"))'
? Of course, that doesn't help with finding its super-methods, and if it does funny classless things then you'd need to instantiate an object. (Pause for a moment to hate the fact that perl parses

stash_name Our::Controller->can(...)
as a call to

Our::Controller->stash_name
... wretched indirect object syntax.)

Re:Err...

Ovid on 2008-03-07T13:21:50

Actually, I use my Sub::Information as it groups the info I need about subs in one spot. That being said, I typically don't reach for it as a first resort. I see a method or sub and I have the code open in my editor and I look for it. I only use tricky code when I'm really stuck because manually looking for code gives me a greater understanding of the overall code base. That's when I saw our lovely diamond inheritance which itself has diamonds further down :(

Don't read it, look at it

jplindstrom on 2008-03-07T15:18:51

PerlySense says (widen your browser window. no even more!):

* Inheritance *
  +------------------------------------------------------------------------------- ------------------------------------+
  |    +------------------------------------------------------------------------------- ---+                           |
  |    |  [ Class::Accessor                     ]                                          |                           |
  |    +> [ Class::Accessor::Fast               ] <+                                       |                           |
  +------ [ Catalyst::AttrContainer             ]  |                                       |                           |
            |                                      |                                       |                           v
          [ Catalyst::Controller                                                   ] --> [ Catalyst::Component ] --> [ Class::Data::Inheritable ]
          [ Catalyst::Base                      ]       ^
          [ MyApp::ControllerBase::Import       ]       |
          [ MyApp::ControllerBase::Import::REST ]       |
          [<MyApp::Our::Controller             >] --> [ Catalyst::Controller::REST ]
Which to be fair is very complex, but at least it's somewhat readable.

NEXT

perrin on 2008-03-07T16:55:29

I thought Catalyst used NEXT for inheritance. Is that no longer the case?

Re:NEXT

Ovid on 2008-03-07T18:11:42

Yes, they do, but not all of our code does. However, MI is still a bad and fragile thing. It frequently introduces inappropriate compositions because it's a seductively easy (read: bad lazy) way of sharing behavior.

Re:NEXT

perrin on 2008-03-07T19:54:59

I agree. I was wondering if NEXT would make it better or worse.

Re:NEXT

Aristotle on 2008-03-11T09:33:42

The plan, it seems, is to eventually rewrite Catalyst with Moose. (Originally the plan was to use C3 instead of NEXT, but that work was never completely finished.)

Re:NEXT

groditi on 2008-03-19T03:06:04

Yes, that is correct. I am currently working on that project and we are making decent progress, which you may see as a branch of 5.8 on the repo. One of the goals of this project is to eliminate unnecessary instances of MI (C::A::F, C::D::I) in the Catalyst core, and replace most instances of MI with roles. C3 will work fine with Moose (according to people who know better than I do) and so we will be using C3 and a compatibility layer for NEXT to minimize instances of broken legacy code.