Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

autarch (914)

  (email not shown publicly)

Journal of autarch (914)

Thursday March 01, 2007
08:39 PM

Catalystic conversion - part 1

[ #32539 ]

So I've already found a couple glitches/design issues with Catalyst.

Issue #1: Inheritance and actions

Say you have some code like this:

package Parent;
sub end : ActionClass('Stultify') { }
package Child;
use base 'Parent';
sub end : Private
    my ( $self, $c ) = @_;
    return $self->NEXT::end($c)
        if $c->stash()->{foo};

The problem here is how Catalyst implements actions (and attributes in general, AFAICT). When you call $self->NEXT::end() from Child, you'd expect the Stultify action to get run. But it won't, because of the way Catalyst handles action attributes. The canonical (in my mind) way to implement an attribute (at least that modifies the action of a sub) in Perl is to make a new sub which wrap the original sub and implements the action. Then you take this new sub and replace the old sub in the symbol table. Of course, given a few lexicals and a few attributes, you could create a nice little nested closure leak, but hey, what's some memory between friends?

Catalyst, however, does not do this. I don't really blame the authors, because the whole wrapping thing sucks. Instead, Catalyst just records that sub X has an attribute and makes use of that knowledge somewhere in the system. In the case of actions, it seems like these are called via Catalyst's internal dispatching mechanism.

That's the root of the inheritance problem. That call to NEXT::end() does not go back through the Catalyst dispatcher, it goes through's dispatcher. This means that the Stultify action never gets called. Grr, annoying.

The solution, of course, is just to stick the Stultify action on the Child::end(), but that kinda defeats the purpose of inheritance!

A better solution might be to replace Catalyst's use of NEXT with something that respects Catalyst's dispatching, maybe "CNEXT" or something like that. At the very least, this is worth documenting somewhere with a big "this will not work like you expect" warning.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.
  • ...I usually just use forward or detach. Catalyst's actions are more than subs in a package, they are full objects knowing their name, private paths, attributes and what else they might need to know. The actions are just built on subs in your controller package.
    Ordinary morality is for ordinary people. -- Aleister Crowley
  • Using forward or detach doesn't quite make sense. Here's my example in a nutshell:

    package VegGuide::Controller::Base;
    use base 'Catalyst::Controller::REST';
    sub end : ActionClass('Serialize')
        my $self = shift;
        my $c = shift;
        # This works
        return $self->NEXT::end($c)
            if $c->stash()->{rest};
        # These do not
        # $c->detach('Catalyst::Controller::REST');