Perl: loading a dynamic module, object inheritance, and "shared helper files"

In a nutshell, I'm trying to simulate a network topology using objects for each instance on the network. In addition, I got the top-level manager class responsible for managing these objects and performing integrity checks. The file structure is as follows (I left most of the object files since they are all structured pretty much the same):

Manager.pm Constants.pm Classes/ +- Machine.pm +- Node.pm +- Object.pm +- Switch.pm 

Since quite a few years in OOP, I have been a fan of code reuse, etc., so I set inheritance between these objects, the inheritance tree (in this example) looks like this:

 Switch -+-> Node -+-> Object Machine -+ 

All of these objects are structured as follows:

 package Switch; use parent qw(Node); sub buildFromXML { ... } sub new { ... } # additonal methods 

Now for the interesting part:

Question 1

How can I ensure that all of these objects load correctly without typing names statically? The main problem is this: if I just require "$_" foreach glob("./Classes/*"); , I get a lot of "Subroutine with new overridden errors" errors. I also played with use parent qw(-norequire Object) , Module::Find and some other modifications of @INC in various combinations to make it short: it did not work. Currently, I am statically importing all the classes used, they automatically import the parent classes.
So basically, what I'm asking is: what is the (perl-) right way to do this?
And advanced: it would be very useful to create a more complex folder structure (since there will be quite a lot of objects) and still have inheritance + "autoload"

Question 2 - SOLVED

How can I “share my import”? I use several libraries (my own, containing some helper functions, LibXML , Scalar::Util , etc.), and I want to share them between my objects. (The reason for this is that I may need to add another shared library to all objects, and there is a possibility that there will be much more than 100 objects - without fun editing all of them manually, and this is theoretically with a regex / script, but this is not seems like the cleanest solution)
What I tried:

  • import everything into Manager.pm Works inside the Manager package - gives me errors like "undefined routine & Switch :: trace"
  • Create an include.pl file and do / require / use inside each object - gives me the same errors.
  • A few more things, which I, unfortunately, do not remember.

include.pl will basically look like this:

 use lib_perl; use Scalar::Util qw(blessed); use XML::LibXML; use Data::Dumper; use Error::TryCatch; ... 

Again I ask: what is the right way to do this? Am I using the right approach and just not doing the execution or should I completely restructure?
It doesn't matter why my current code doesn’t work so well, providing the correct, clean approach for these problems will be enough :)

EDIT: a completely forgotten version of perl -_- Sidenote: I cannot update perl since I need libraries stuck with 5.8: /

 C:\> perl -version This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 50 registered patches, see perl -V for more detail) Copyright 1987-2006, Larry Wall Binary build 820 [274739] provided by ActiveState http://www.ActiveState.com Built Jan 23 2007 15:57:46 
+7
source share
3 answers

This is only a partial answer to question 2, sharing imports.

Loading a module (through use ) does two things:

  • Compiling the module and setting the contents in a namespace hierarchy (which is shared). See perldoc -f require .
  • Call import sub for each loaded module. This loads some subsets or constants, etc. To the caller's namespace. This is the process that the Exporter class is largely hiding from the view. This part is important for using subtitles, etc. Without their full name, for example. max instead of List::Util::max . See perldoc -f use .

Allows you to view the following three modules: A , B and User .

 { package A; use List::Util qw(max); # can use List::Util::max # can use max } { package User; # can use List::Util::max -> it is already loaded # cannot use max, this name is not defined in this namespace } 

Package B defines a sub load that loads a predefined list of modules and subsets into the caller namespace:

 { package B; sub load { my $package = (caller())[0]; # caller is a built-in, fetches package name eval qq{package $package;} . <<'FINIS' ; use List::Util qw(max); # add further modules here to load # you can place arbitrarily complex code in this eval string # to execute it in all modules that call this sub. # (eg testing and registering) # However, this is orthogonal to OOP. FINIS if ( $@ ) { # Do error handling } } } 

Inside the eval 'd line, we temporarily enable the caller package and load the specified module. This means that the User package code now looks like this:

 { package User; B::load(); # can use List::Util::max # can use max } 

However, you must ensure that the load subcomputer is already loaded on its own. use B if in doubt. It is best to execute B::load() in the BEGIN phase before the rest of the module is compiled:

 { package User; BEGIN {use B; B::load()} # ... } 

equivalently

 { package User; use B; use List::Util qw(max); # ... } 

TIMTOWTDI. Although I believe that the eval code is pretty dirty and dangerous, this is how I chased in this scenario (and not do files, similar, but has different side effects). Manually messing with typeglobs in the package namespace is hell in comparison, and copying the list of module names is a return to the days when there was not even a C preprocessor.


Edit: Import::Into

... is a CPAN module that provides this functionality through an interesting method interface. Using this module, we will redefine our package B as follows:

 { package B; use List::Util; # you have to 'use' or 'require' this first, before using 'load'. use Import::Into; # has to be installed from CPAN first sub load { my $package = caller; List::Util->import::into($package, qw(max)); # should work too: strict->import::into($package); # ... } } 

This module hides all dirty work ( eval ing) from the view and performs the resolving gymnastics resolution method to allow the import of pragmas into other namespaces.

+3
source

Adding to Import :: Into Solution

I found a script that seems to require eval () from an Import :: Into solution. In this case, the user mod is effectively involved in using package B. This could be a common scenario for people using Import :: Into.

Features:

  • I created a uses_exporter module with separate subtypes for importing various groups of modules, for example. load_generic () and load_list_utils ().

  • The use of load_list_utils () refers to publicly accessible mods such as List :: MoreUtils, and to my module, list_utils_again. That the local module also calls load_list_utils (). The call fails if load_list_utils () uses list_utils_again.

  • My solution was to use list_utils_again in eval which is not excecute when $ target eq 'list_utils_again'

0
source

The correct idiomatic way for Perl to do this is to not always load a bunch of modules, used or not; he must have every file use those modules that he directly (not indirectly) needs.

If it turns out that each file uses the same set of modules, you can make things simpler if one dedicated module uses all the ones in this common set.

0
source

All Articles