Tag: Moose

  • Taming the Moose: Method modifiers instead of overrides in object-​oriented Perl

    Taming the Moose: Method modifiers instead of overrides in object-​oriented Perl

    Last month I wrote about using Moose’s override func­tion to, well, over­ride a superclass’s method. Chris Prather on the #moose IRC chan­nel sug­gest­ed soon after that the around method mod­i­fi­er (or its lit­tle sis­ters before and after) might be a bet­ter choice if you’re also call­ing the orig­i­nal method inside. He not­ed that at a min­i­mum override only works if you’re sub­class­ing, around will apply to com­posed meth­ods too.”

    His point was that when you decide to com­pose roles (also know as traits) instead of or in addi­tion to more tra­di­tion­al inher­i­tance, override sim­ply doesn’t work: only a method mod­i­fi­er will do. (And as Graham Knop and Karen Etheridge lat­er remarked on IRC, override isn’t even an option if you’re using Moo as an alter­na­tive to Moose.)

    Modifying a role’s method with around might look like this:

    #!/usr/bin/env perl

    use v5.12; # for strict and say
    use warnings;

    package Local::Role::Hungry;
    use Moose::Role;
    requires 'name';

    sub wants_food {
    my $self = shift;
    say $self->name, ' is hungry!';
    return;
    }

    package Local::GuineaPig;
    use Moose;
    has name => (is => 'ro');
    with 'Local::Role::Hungry';

    around wants_food => sub {
    my ($orig, $self, @args) = @_;
    say $self->name, ' runs to the front of the cage!';
    $self->$orig(@args);
    say 'Wheek!';
    return;
    };

    package Local::Dog;
    use Moose;
    has name => (is => 'ro');
    with 'Local::Role::Hungry';

    around wants_food => sub {
    my ($orig, $self, @args) = @_;
    say $self->name, ' runs to the kitchen!';
    $self->$orig(@args);
    say 'Woof!';
    return;
    };

    before wants_food => sub {
    my $self = shift;
    say $self->name, ' is jumping!';
    };

    package main;
    my $dog = Local::Dog->new(name => 'Seamus');
    my @pigs = map { Local::GuineaPig->new(name => $_) }
    qw<Cocoa Ginger Pepper>;

    for my $animal ($dog, @pigs) {
    $animal->wants_food();
    }

    Running the above yields:

    Seamus runs to the kitchen!
    Seamus is hungry!
    Woof!
    Cocoa runs to the front of the cage!
    Cocoa is hungry!
    Wheek!
    Ginger runs to the front of the cage!
    Ginger is hungry!
    Wheek!
    Pepper runs to the front of the cage!
    Pepper is hungry!
    Wheek!

    It’s a lit­tle more involved than over­rid­ing a sub, since method mod­i­fiers are passed both the con­sumed role’s orig­i­nal method ($orig above) and the instance ($self above) as para­me­ters. It has the same effect, though, and you can call the orig­i­nal method by say­ing $self->$orig(parameters).

    If all you want to do is have some­thing hap­pen either before or after the orig­i­nal method, just use before or after:

    before wants_food => sub {
    my $self = shift;
    say $self->name, ' is jumping!';
    };

    Note that there’s no return val­ue in a before or after mod­i­fi­er, as those are han­dled by the orig­i­nal method.

    Modifiers aren’t lim­it­ed to con­sum­ing class­es; they can be in roles and mod­i­fy their con­sumers’ meth­ods. They also have a cou­ple of oth­er tricks:

    • You can pass an array ref­er­ence to mod­i­fy mul­ti­ple meth­ods at once.
    • You can use the con­tents of a vari­able to spec­i­fy the mod­i­fied method name, and use that same vari­able in the mod­i­fi­er itself.
    • You can use a reg­u­lar expres­sion to select meth­ods. (Beware if you’re using Moo that its Class::Method::Modifiers mod­ule doesn’t sup­port this.)

    Putting these togeth­er gives you con­structs like these:

    after qw<foo bar baz> => sub {
    say 'Something got called';
    };

    for my $method_name (qw<foo bar baz>) {
    before $method_name => sub {
    say "Calling $method_name...";
    };
    }

    before qr/^request_/ => sub {
    my ($self, @args) = @_;
    $self->is_valid(@args) or die 'Invalid arguments';
    };

    Moose comes with great intro­duc­to­ry man­u­als for method mod­i­fiers and roles, so be sure to check those out. There’s a lot more to them and a blog can only cov­er so much.

  • What’s Next for Object-​Oriented Perl?

    What’s Next for Object-​Oriented Perl?

    Introduction: The current state of play

    Perl has very min­i­mal” sup­port for object-​oriented (OO) pro­gram­ming out of the box by its own admis­sion. It’s class-​based but class­es are just pack­ages used dif­fer­ent­ly. Objects are just data struc­tures blessed into a class, meth­ods are just sub­rou­tines whose first argu­ment is an object or class name, and attributes/​properties are often just the key-​value pair of a hash stored in the object. (This last is a fea­ture shared with JavaScript, whose prototype-​based objects are just col­lec­tions of key-​value pairs with the keys addressed as prop­er­ties.) You’ve got poly­mor­phism, inher­i­tance, and it’s up to you to enforce encap­su­la­tion.

    This can take a lot of work to use effec­tive­ly. To help address that, sev­er­al sys­tems have been devel­oped over the years to reduce boil­er­plate and pro­vide mod­ern (or post­mod­ern”) OO fea­tures that devel­op­ers from oth­er lan­guages expect. My favorite for a while has been Moo: it’s got the fea­tures I need 90% of the time like built-​in con­struc­tors, roles (an alter­na­tive to com­po­si­tion through inher­i­tance), attrib­ut­es, type val­i­da­tion, and method mod­i­fiers for enhanced poly­mor­phism. And if I need to dig around in the guts of class­es, attrib­ut­es, and the like I can always upgrade to Moo’s big broth­er Moose and its meta-​object pro­to­col with min­i­mal effort.

    Corinna, Object::Pad, and porting dbcritic

    But there’s a new kid on the block. Curtis Ovid” Poe has been spear­head­ing Corinna, an effort to bring effec­tive OO to the Perl core and leapfrog [empha­sis his] the capa­bil­i­ties of many OO lan­guages today.” No CPAN mod­ules, no chain of depen­den­cies; just sol­id OO fea­tures and syn­tax built-​in. And while Corinna is a ways off from ship­ping, Paul LeoNerd” Evans (maybe I should get a cool nick­name too?) has been imple­ment­ing some of these ideas as new Perl key­word syn­tax in his Object::Pad module.

    Both Ovid and LeoNerd have been ask­ing devel­op­ers to try out Object::Pad, not just as a new toy, but to get feed­back on what works and what needs to be added. So I thought I’d try port­ing an old­er small Moo-​based project named dbcrit­ic to this new real­i­ty. In the process, I learned some of the advan­tages and dis­ad­van­tages of work­ing with Object::Pad. Hopefully, this can inform both it and Corinna’s evo­lu­tion as well as oth­er curi­ous devel­op­ers’ eval­u­a­tions. You can fol­low my cod­ing efforts in this GitHub branch.

    First, the mar­quee result: the code for App::DBCritic (the class I start­ed with) is clean­er and short­er, with 33 lines shaved off so far. Mainly this is due to Object::Pad’s more con­cise attribute syn­tax (called slots” in its doc­u­men­ta­tion) and lack of explic­it sup­port for Moo’s attribute coer­cion. I only used the lat­ter for one attribute in the Moo ver­sion and I’m not sure it worked par­tic­u­lar­ly well, so it was­n’t hard to jet­ti­son. But if your code sup­ports coer­cions exten­sive­ly, you’ll have to look into Object::Pad’s BUILD or ADJUST phase blocks for now.

    Before, a Moo attribute with var­i­ous options:

    has schema => (
        is        => 'ro',
        coerce    => 1,
        lazy      => 1,
        default   => \&_build_schema,
        coerce    => \&_coerce_schema,
        predicate => 1,
    );

    After, an Object::Pad slot. No coer­cion and builder code is han­dled in a lat­er ADJUST block:

    has $schema :reader :param = undef;

    Speaking of ADJUST blocks, it took a lit­tle bit of insight from the #perl IRC chan­nel to real­ize that they were the appro­pri­ate place for set­ting slot defaults that are com­put­ed from oth­er slots. Previously I was using a maze of depen­den­cies mix­ing Moo lazy attrib­ut­es and builder meth­ods. Clarifying the main set of option­al con­struc­tor argu­ments into a sin­gle ADJUST block helped untan­gle things, so this might be an indi­ca­tion that lazy attrib­ut­es are an antipat­tern when try­ing to write clean code. It’s also worth not­ing that Object::Pad ADJUST blocks run on object con­struc­tion, where­as Moo lazy attrib­ut­es are only built when need­ed. This tends to mat­ter for data­base access.

    The ADJUST block for the $schema slot:

    ADJUST {
        my @connect_info = ( $dsn, $username, $password );
        if ($class_name and eval "require $class_name") {
            $schema = $class_name->connect(@connect_info);
        }
        elsif ( not ( blessed($schema) and $schema->isa('DBIx::Class::Schema') ) ) {
            local $SIG{__WARN__} = sub {
                if ( $_[0] !~ / has no primary key at /ms ) {
                    print {*STDERR} $_[0];
                }
            };
            $schema = App::DBCritic::Loader->connect(@connect_info);
        }
        croak 'No schema defined' if not $schema;
    }

    Object::Pad’s slots have one great advan­tage over Moo and Moose attrib­ut­es: they direct­ly sup­port Perl array and hash data struc­tures, while the lat­ter only sup­ports scalars and ref­er­ences con­tained in scalars. This means meth­ods in your class can elim­i­nate a deref­er­enc­ing step, again lead­ing to clean­er code. I used this specif­i­cal­ly in the @violations array and %elements hash slots and was very pleased with the results.

    The @violations and %elements slots and their ADJUST blocks:

    has %elements;
    
    ADJUST {
        %elements = (
            Schema       => [$schema],
            ResultSource => [ map { $schema->source($_) } $schema->sources ],
            ResultSet    => [ map { $schema->resultset($_) } $schema->sources ],
        );
    }
    
    has @violations;
    
    ADJUST {
        @violations = map { $self->_policy_loop( $_, $elements{$_} ) }
            keys %elements;
    }
    
    method violations { wantarray ? @violations : \@violations }
    

    Issues

    I did have some devel­op­ment life­cy­cle issues with Object::Pad, but they’re main­ly a result of its future-​facing syn­tax. I had to give up using perltidy and perlcritic in my build and test phas­es, respec­tive­ly: perltidy does­n’t under­stand slot attrib­ut­es like :reader and :param and will emit an error file (but code still com­piles), and sev­er­al of the perlcritic poli­cies I use report prob­lems because its PPI pars­er does­n’t rec­og­nize the new syn­tax. I could add excep­tions in the perlcriticrc file and lit­ter my code with more ## no critic anno­ta­tions than it already had, but at this point, it was eas­i­er to just dis­able it entirely.

    Another thing I had to dis­able for now was my Dist::Zilla::Plugin::Test::UnusedVars-gen­er­at­ed Test::Vars test for detect­ing unused vari­ables, as it reports mul­ti­ple fail­ures for the hid­den @(Object::Pad/slots) vari­able. It does have options for ignor­ing cer­tain vari­ables, though, so I can explore using those and pos­si­bly file a pull request to ignore that vari­able by default.

    Conclusion: The future looks bright

    Overall I’m sat­is­fied with Object::Pad and by exten­sion some of the syn­tax that Corinna will intro­duce. I’m going to try port­ing the rest of dbcrit­ic and see if I can work around the issues I list­ed above with­out giv­ing up the kwali­tee improve­ment tools I’m used to. I’ll post my find­ings if I feel it mer­its anoth­er blog.

    What do you think? Is this the future of object-​oriented Perl? Let me know in the com­ments below.

  • Taming the Moose: Picking the best way to subclass Perl methods

    Taming the Moose: Picking the best way to subclass Perl methods

    The override key­word in Perl’s Moose object sys­tem is a nice bit of code-​as-​documentation since it explic­it­ly states that a giv­en method over­rides from its super­class. It also has a super key­word that can be used inside an override, call­ing the next most appro­pri­ate super­class method with the same argu­ments as the orig­i­nal method.”

    The Moose doc­u­men­ta­tion then goes on to say, The same thing can be accom­plished with a nor­mal method call and the SUPER:: pseudo-​package; it is real­ly your choice.” So when should you use one and not the oth­er? I decid­ed to find out.

    First I defined a sim­ple Moose super­class with a sin­gle method:

    package Local::MyClass;
    
    use Moose;
    
    sub my_method {
        return blessed $_[0];
    }
    
    __PACKAGE__->meta->make_immutable();
    
    1;

    And then a pair of sub­class­es, one using Moose’s override key­word and one with a plain sub:

    package Local::MyClass::MyChildOverride;
    
    use Moose;
    extends 'Local::MyClass';
    
    override my_method => sub {
        my $self = shift;
        return 'child ' . super;
    };
    
    __PACKAGE__->meta->make_immutable();
    
    1;
    package Local::MyClass::MyChildPlain;
    
    use Moose;
    extends 'Local::MyClass';
    
    sub my_method {
        my $self = shift;
        return 'child ' . $self->SUPER::my_method();
    }
    
    __PACKAGE__->meta->make_immutable();
    
    1;

    So far so good, and both can be called successfully:

    $ perl -Ilib -MLocal::MyClass::MyChildPlain \
      -MLocal::MyClass::MyChildOverride \
      -E '$PREFIX = "Local::MyClass::MyChild";
      for ( qw(Plain Override) ) {
        $object = "$PREFIX$_"->new();
        say $object->my_method()
      }'
    child Local::MyClass::MyChildPlain
    child Local::MyClass::MyChildOverride

    Let’s toss in a new wrin­kle, though. What if we for­got to define the method in the superclass?

    package Local::MyClassNoMethod;
    
    use Moose;
    
    __PACKAGE__->meta->make_immutable();
    
    1;

    Both ways of call­ing the super­class’s method will bug out, of course, but unlike a plain over­ride Moose will actu­al­ly pre­vent you from useing the offend­ing sub­class dur­ing the BEGIN phase:

    $ perl -Ilib -MLocal::MyClassNoMethod::MyChildOverride \
      -E ''
    You cannot override 'my_method' because it has no super method at /Users/mgardner/.plenv/versions/5.34.0/lib/perl5/site_perl/5.34.0/darwin-2level/Moose/Exporter.pm line 419
    	Moose::override('my_method', 'CODE(0x7fe5cb811a88)') called at lib/Local/MyClassNoMethod/MyChildOverride.pm line 9
    	require Local/MyClassNoMethod/MyChildOverride.pm at -e line 0
    	main::BEGIN at lib/Local/MyClassNoMethod/MyChildOverride.pm line 0
    	eval {...} at lib/Local/MyClassNoMethod/MyChildOverride.pm line 0
    Compilation failed in require.
    BEGIN failed--compilation aborted.

    With plain method over­rid­ing, you only get an error if you try to call the super­class’s method. If your over­rid­den method does­n’t do that, it’s per­fect­ly safe to define and call. It’s only if you use that SUPER:: pseudo-​package that things blow up at runtime:

    $ perl -Ilib -MLocal::MyClassNoMethod::MyChildPlain \
      -E '$obj = Local::MyClassNoMethod::MyChildPlain->new();
      $obj->my_method()'
    Can't locate object method "my_method" via package "Local::MyClassNoMethod::MyChildPlain" at lib/Local/MyClassNoMethod/MyChildPlain.pm line 8.

    Note that none of this is caught at com­pile time. perl -c will hap­pi­ly com­pile all these class­es and sub­class­es with­out a peep:

    $ find . -name '*.pm' -exec perl -c {} \;
    ./lib/Local/MyClass/MyChildPlain.pm syntax OK
    ./lib/Local/MyClass/MyChildOverride.pm syntax OK
    ./lib/Local/MyClassNoMethod/MyChildPlain.pm syntax OK
    ./lib/Local/MyClassNoMethod/MyChildOverride.pm syntax OK
    ./lib/Local/MyClass.pm syntax OK
    ./lib/Local/MyClassNoMethod.pm syntax OK

    So what can we con­clude? Moose’s override is a good way of describ­ing your intent with a sub­class, and it will catch you out if you try to use it with­out a cor­re­spond­ing method in a super­class. It is a non-​standard key­word though, so syntax-​highlighting edi­tors and code analy­sis tools won’t rec­og­nize it unless taught. Further, if your sub­class method does­n’t call the same method in a super­class you could even­tu­al­ly get away with remov­ing the lat­ter if you use a plain sub.

    I’ve cre­at­ed a small GitHub project with the sam­ple code from this arti­cle, includ­ing test scripts.

    What do you think? Is override suit­able for your Moose projects, or are you sat­is­fied with plain sub? Let me know in the comments.