perloopmoosemopcgiapp

Dealing with multiple-inherited constructors in Moose


Greetings,

I'm learning Moose and I'm trying to write a CGI::Application subclass with Moose, which is made difficult by the fact that CGI-App is not based on Moose.

In my other CGI-App subclasses, I like to have a parent class with a setup method that looks at the child class's symbol table and automatically sets up the runmodes. I figure I can use Moose's metaclass facilities to achieve the same thing in a cleaner way. So here is what I have in my parent class:

use MooseX::Declare;

class MyApp::CGI 
extends Moose::Object
extends CGI::Application { 

    method setup { 
        $self->start_mode( 'main' );

        my @methods = map { $_->name } $self->meta->get_all_methods;

        $self->run_modes( map  { /^rm_(.+)$/  => $_ }
                          grep { /^rm_/ }
                          @methods
                        );
    }

}

...and in my child class:

use MooseX::Declare;

class MyApp::CGI::Login 
extends MyApp::CGI { 
    method rm_main { 
        return "it works";
    }
}

I realized that the reason my runmodes were not getting setup properly is because setup is called by the CGI-App constructor, and Moose::Object is sticking its own constructor in my class. I tried to solve this with a method modifier:

around new { 
    $self = $orig->( @_ );
    $self->CGI::Application::new( @_ );
}

This gives me

Can't call method "BUILDARGS" on unblessed reference at ...Moose/Object.pm line 21.

I have a feeling, however, that I'm going about this in completely the wrong way, and Moose has a much better facility for achieving what I want, which I have not as yet discovered.


Solution

  • Have you already looked at Moose::Cookbook::Basics::DateTime_ExtendingNonMooseParent and MooseX::NonMoose?

    Update: I am not very familiar with Moose and assorted techniques. I was not able to get the modules to compile using MooseX::Declare and MooseX::NonMoose together. However, here is something that seems to work:

    Application Base Class

    package My::App;
    
    use Moose;
    use MooseX::NonMoose;
    extends 'CGI::Application';
    
    sub setup {
        my $self = shift;
        $self->start_mode( 'main' );
    
        $self->run_modes(
            map { $_ = $_->name;
                  /^rm_ (?<rm>.+) $/x ? ( $+{rm} => $_ ) : ()
            } $self->meta->get_all_methods
        );
    }
    
    "My::App"
    

    Derived Class

    package My::Login;
    use Moose;
    extends 'My::App';
    
    sub rm_main { 'it works!' }
    
    "My::Login"
    

    Script

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # For testing on the command line
    use FindBin qw( $Bin );
    use lib $Bin;
    
    use My::Login;
    
    my $app = My::Login->new;
    
    $app->run;
    

    Output

    C:\Temp\f> t
    Content-Type: text/html; charset=ISO-8859-1
    
    it works!