Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I find all the packages that inherit from a package in Perl?

I have a number of different sites that I download data from and massage into other formats (using Perl) for use at work, that are all run from one Perl script kinda like so:

#! /usr/bin/perl
use strict;

use My::Package1;
use My::Package2;

my $p1 = My::Package1->new;
$p1->download;

my $p2 = My::Package2->new;
$p2->download;

and so on and so forth. At the moment each My::Package is its own package; it doesn't inherit from a base package or anything. I am planning to re-write them using Moose and I was hoping that rather than having to edit the Perl script that runs the download each time a new package is added, there might be a way of finding packages that inherit from a base package, and then in a loop instantiate each and do the downloading, kinda like so:

#! /usr/bin/perl
use strict;

for my $pname (packages_that_inherit_from("My::Package")) {
    my $package = $pname->new;
    $package->download;
}

Is it, or something ilke it, possible?

TIA

like image 732
Mark Avatar asked Dec 17 '22 03:12

Mark


2 Answers

Using Moose's Class::MOP underpinning you can find the subclasses assigned to each class (at that point in time).

From Class::MOP::Class docs:

$metaclass->subclasses This returns a list of all subclasses for this class, even indirect subclasses.

$metaclass->direct_subclasses This returns a list of immediate subclasses for this class, which does not include indirect subclasses.

So for example if we build these classes:

{
    package Root;
    use Moose;
    use namespace::clean -except => 'meta';

    sub baz      { say 'Some root thingy' }
    sub download { say "downloading from " . __PACKAGE__ }
}

{
    package NodeA;
    use Moose;
    extends 'Root';
    use namespace::clean -except => 'meta';
    sub download { say "downloading from " . __PACKAGE__ }
}

{
    package NodeA1;
    use Moose;
    extends 'NodeA';
    use namespace::clean -except => 'meta';
    sub download { say "downloading from " . __PACKAGE__ }
}

Then using your example as a basis we can do this:

for my $pname ( Root->new->meta->direct_subclasses ) {
    my $package = $pname->new;
    $package->download;
}

# =>  "downloading from NodeA"

So above runs NodeA->download. Changing above to meta->subclasses would also run the NodeA1->download.

/I3az/

like image 126
draegtun Avatar answered May 16 '23 08:05

draegtun


Although you say you are moving to Moose, a non-Moose way is to put all the derived packages in the known subdirectory based on the base package name. You then load all of the modules

For instance, if your base package is Local::Downloader, all the derived packages start with Local::Downloader::Plugin or something similar. You then look for all modules in your @INC that much .../Local/Downloader/Plugin/.... Although it's not too hard to do yourself, something like Module::PluginFinder can do it for you too.

like image 43
brian d foy Avatar answered May 16 '23 07:05

brian d foy