http://qs321.pair.com?node_id=366285


in reply to Draft - Writng plugable programs with perl.

A few small nits. Be careful with this:

foreach (@plugin_list) { require $_ or warn "Plugin $_ failed to load"; }

As your next example demonstrates without explaning, perl evaluates require EXPR (where EXPR is not a bareword) to requiring a filename, not a module name.

Also this code can be wrong in certain cases:

# Initialization: foreach (@plugins) { $_->init if UNIVERSAL::can($_, 'init'); }

Consider if I'd implemented my own can() method:

#!/usr/bin/perl -w use strict; package Foo; sub new { bless {}, shift; } my $bar = sub { return 'Bar!'; }; sub can { my ($self, $method) = @_; return $bar if $method eq 'bar'; return $self->SUPER::can( $method ); } package main; my $foo = Foo->new(); if (UNIVERSAL::can( $foo, 'bar' )) { print "UNIVERSAL thinks Foo can bar\n"; } if (my $bar = $foo->can( 'bar' )) { print "Foo can actually ", $foo->$bar(), "\n"; }

I realize that this is a silly example, but there are very valid reasons to override can(). I'd rather write your snippet as:

# Initialization: foreach (@plugins) { eval { return unless $_->can( 'init' ); return $_->init(); } }

If you document that init() should always return true, you can put a guard after that eval to see which plugins initialized successfully.

Alternately, if you want to see that an object you've somehow created or received performs specific behaviors, you could use Class::Roles.

Replies are listed 'Best First'.
Re^2: Draft - Writng pluggable programs with perl.
by yosefm (Friar) on Jun 13, 2004 at 18:01 UTC
    Yes, good points. except for the 'can' thing. I usually assume (especially for a tutorial) that nobody is trying to fool me. If someone did repplace can(), he must know what he's doing.

    I'll fix the explanation about require.


    perl -e'$b=unpack"b*",pack"H*","59dfce2d6b1664d3b26cd9969503";\ for(;$a<length$b;$a+=9){print+pack"b8",substr$b,$a,8;}'
    My public key
      If someone did repplace can(), he must know what he's doing.

      Yes, but your code won't let him do it. If I'd written a plugin with a custom can(), to make it work with your program, I'd have to override UNIVERSAL::can()! Putting specific code to make a subclass work in a superclass (let alone the ultimate superclass) is bad design.

      I understand that your code tries to ensure that everything in @plugins actually represents a valid invocant. That's well-worth doing, but I think you're forcing behavior of the plugins beyond the minimum you need to do what you're doing.

      You've created a plugin system because you don't know what people will need to do to solve problems in the future. Don't enforce too many restrictions on how they'll solve those problems!