Tag: database

  • 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­agesused dif­fer­ent­ly. Objects are just data struc­tures blessed into a class, meth­ods are just sub­rou­tineswhose first argu­ment is an object or class name, and attributes/​properties are often just the key-​value pair of a hashstored 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­temshave 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­fiersfor 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 Mooseand its meta-​object pro­to­colwith min­i­mal effort.

    Corinna, Object::Pad, and porting dbcritic

    But there’s a new kid on the block. Curtis ​“Ovid” Poehas 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 CPANmod­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­taxin his Object::Padmodule.

    Both Ovidand 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­icto 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 BUILDor ADJUSTphase 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 ADJUSTblock:

    has $schema :reader :param = undef;

    Speaking of ADJUSTblocks, it took a lit­tle bit of insight from the #perl IRC chan­nelto 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 lazyattrib­ut­es and buildermeth­ods. Clarifying the main set of option­al con­struc­tor argu­ments into a sin­gle ADJUSTblockhelped 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 ADJUSTblocks run on object con­struc­tion, where­as Moo lazyattrib­ut­es are only built when need­ed. This tends to mat­ter for data­base access.

    The ADJUSTblock for the $schemaslot:

    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 scalarsand ref­er­encescon­tained in scalars. This means meth­ods in your class can elim­i­nate a deref­er­enc­ingstep, again lead­ing to clean­er code. I used this specif­i­cal­ly in the @violationsarrayand %elementshashslots and was very pleased with the results.

    The @violationsand %elementsslots and their ADJUSTblocks:

    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 perltidyand perlcriticin my build and test phas­es, respec­tive­ly: perltidydoes­n’t under­stand slot attrib­ut­es like :readerand :paramand will emit an error file (but code still com­piles), and sev­er­al of the perlcriticpoli­cies I use report prob­lems because its PPIpars­er does­n’t rec­og­nize the new syn­tax. I could add excep­tions in the perlcriticrcfile and lit­ter my code with more ## no criticanno­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::Varstest 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 requestto 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­teeimprove­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.

  • Part 7 video of pair programming a Perl web app

    This week we con­sid­ered a view­er’s pull request, added admin­is­tra­tor login, and start­ed on adding the SQLite data­basethat will store the admin­is­tra­tor’s accep­tance of assign­ments. We also shored up file upload per­mis­sions for authen­ti­cat­ed usersonly and added a logout link, learn­ing about some more Mojolicious helpers.

    You can find the whole series here.