http://www.perlmonks.org?node_id=997244

Wcool has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks. I am writing a library of utilities to be used by Perl scripts. The library contains some constants, some generic functions and some object oriented classes. For practicality I like to put all these packages in one file, to make distribution easy (no subdirectories etc) So the layout of the file looks something like this:
package STDLIB; use Exporter() some constants i like to export some functions i like to export package OBJECT1 some object constants i like to export like my OBJ1_CONSTANT1; my OBJ1_CONSTANT2; some methods package OBJECT2 some object constants i like to export like my OBJ2_CONSTANT1; my OBJ2_CONSTANT2; some methods 1;
I managed to export the generic functions and constants but whatever I try, I can't seem to export the OO constants. Would someone be able to show me how to do this? Would I have to add multiple use Exporter; statements to each package? Do i have to end each package with 1; ? Or only one Exporter statement? And in the parent program, can restrict the import to only 1 package so something like:
use STDLIB qw(OBJECT1);
Any pointers appreciated!

Replies are listed 'Best First'.
Re: How to export multiple packages in one file to another Perl program?
by daxim (Curate) on Oct 04, 2012 at 15:25 UTC
    Constants belong to a package and do not pass down inheritance, so this is not suitable for OO. You want read-only attributes.
    package Object2; use Moose; has 'some_attr', is => 'ro', default => 42; ################## package main; my $o = Object2->new; $o->some_attr; # returns 42
Re: How to export multiple packages in one file to another Perl program?
by Anique (Acolyte) on Oct 04, 2012 at 15:00 UTC

    As far as I understand it, the idea of using OO is that you do not export anything from within your classes

    That said, for each package where you want to export something, you will need to use Exporter(); within the package

      Well I do use exporter for the non-object oriented code already and it works. I even have 'sections' I can export like :date and :maths But I run into trouble importing any objectt oriented parts I have to use use STDLIB; to get the library file read, but how do I get the code for package OBJECT1 read?
Re: How to export multiple packages in one file to another Perl program?
by Tanktalus (Canon) on Oct 05, 2012 at 03:34 UTC

    "How" really depends on what you're trying to accomplish. Generally, I try to avoid this, but it's definitely not always possible.

    1. As far as I'm aware, you can't export my variables. Use our instead. The issue here is that my variables are lexical variables, and to export a variable it must be global. (Variables created with our are also lexical, but they're aliases to global variables in the package in which the our occurred. If that's confusing, as long as you only have one package per file then it's roughly the same thing as "it declares a global variable".)

    2. Don't put more than one package in the same file. Really, don't. Yes, some people do it. I even do it. But if you're getting confused by it, don't. I only do it when it's a small hidden package that I don't expose to anyone. That sounds to be about the opposite of what I interpret you're trying to explain that you're doing. If that sounds like someone is confused, it's because I am.

    3. You don't export packages. use STDLIB qw(OBJECT1) doesn't make any sense when OBJECT1 is a package.

    4. Package names are generally TitleCase, not ALLUPPERCASE, even if IT_HAS_UNDERSCORES. Constants are often all uppercase, but not package names.

    5. Only the "primary" package in a file gets to export stuff. While this is not technically true, it's close enough. When I "use STDLIB", only STDLIB's import gets called. That means either STDLIB's import needs to be super fancy, or I can only import stuff from the STDLIB package.

    6. The "1;" is a file-level thing. So it's only needed once at the end of the entire file.

    7. XY Problem. What are you really trying to do? What interface are you trying to provide? If it's simply that someone can say my $obj = OBJECT1->new(), then you don't need to export anything. If it's that you want them to say my $obj = OBJECT1->new($OBJ1_CONSTANT1), consider using strings instead, or multiple constructors, e.g., my $obj = OBJECT1->new_with_const1(). Ok, that last one isn't a good name, but neither is OBJ1_CONSTANT1, so I can't suggest a better name.

    8. Oh, and "my OBJ1_CONSTANT1;" doesn't make any sense. You're missing the sigil, so I'm not sure what type of constant you're going for here.

    9. Once you've separated everything into separate files, take a look at Import::Into. I've used this to import from module X when doing "use Y;". Works pretty good so far, though I can imagine some places where even that won't be sufficient (but we're talking about arcane stuff, and I'd encourage avoiding said arcane stuff).

    Hope that helps.

Re: How to export multiple packages in one file to another Perl program?
by choroba (Cardinal) on Oct 04, 2012 at 15:07 UTC
    What do you mean by an OO constant?
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      I have some methods that take optional parameters like this: $OBJ1_SORT_BY_SIZE $OBJ1_SORT_BY_DATE $OBJ1_SORT_BY_USER I want these labels exported. Of course I could create different methods for each label but prefer to have one sort method taking additional parameters So OO constant is a constant used by the object but that needs be known outside the object
Re: How to export multiple packages in one file to another Perl program?
by sundialsvc4 (Abbot) on Oct 04, 2012 at 16:09 UTC

    First of all, I suggest that each file should contain one package.

    If you do need to publish constants, then I suggest that you surf to http://search.cpan.org and search for “constants” to find some good ideas to copy.   In general, look for a substantial package there and notice how they do it.

      First of all, I suggest that each file should contain one package.

      If you do need to publish constants, then I suggest that you surf to http://search.cpan.org and search for “constants” to find some good ideas to copy.   In general, look for a substantial package there and notice how they do it.

      Aha, its a classed up version of RTFM/JFGI

Re: How to export multiple packages in one file to another Perl program?
by BrowserUk (Patriarch) on Oct 05, 2012 at 10:10 UTC

    Ignoring whether "you should be doing this"; here's one way it could be done:

    The PM file:

    package MyObjs; use strict; use warnings; use enum qw[ :MOError=0 One Two Three Four Five ]; sub import { my $self = shift; my $caller = caller(); for( @_ ) { if( /^:MO/ ) { no strict; no warnings; *{ "$caller\:\:MOError$_" } = *{ "MOError$_" } for qw[ One + Two Three Four Five ]; } elsif( /^:O1/ ) { no strict; no warnings; *{ "$caller\:\:O1Error$_" } = *{ "Object1\:\:O1Error$_" } +for qw[ One Two Three Four Five ]; } elsif( /^:O2/ ) { no strict; no warnings; *{ "$caller\:\:O2Error$_" } = *{ "Object2\:\:O2Error$_" } +for qw[ One Two Three Four Five ]; } else { die "Unknown export $_"; } } } package Object1; use enum qw[ :O1Error=100 One Two Three Four Five ]; package Object2; use enum qw[ :O2Error=200 One Two Three Four Five ]; 1;

    And a script that imports from that:

    #! perl -slw use strict; use MyObjs qw[ :MO :O1 :O2 ]; print for MOErrorOne, O1ErrorThree, O2ErrorFive; __END__ C:\test>t-MyLib.pl 0 102 204

    NOTE: The use of the enum module is not germane to this working. Any subs can be exported including normal subs and those generated by constant etc.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

    ;
      Thanks very much. I see now that one has to great lengths to achieve this, I might as well seperate my files into different files for each...
        I see now that one has to great lengths to achieve this,

        Hm. When I compare that single import function with the guts of Exporter, I know which one I consider "great lengths".

        But if you prefer to use a module, you should consider the latter's export_to_level() function.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        RIP Neil Armstrong

Re: How to export multiple packages in one file to another Perl program?
by Wcool (Novice) on Oct 05, 2012 at 08:31 UTC
    Thanks for all the replies. I know about the MixedCase notation, I use it myself. The use of abstract variable names and uppercase was only to indicate it could be any class name or variable. Let me rephrase the problem: Let's say I have a OO package called MyFiles which maintains some sort of list of files and it supports 3 ways of sorting. $BY_SIZE, $BY_USER, $BY_DATE. I could code 3 sort methods, or 1 sort method taking an extra parameter to indicate how it should be sorted. If I take the 2nd approach these constants have to be exported. I know how to do that using Exporter using EXPORT_OK, and I know I don't have to export any methods. Something like
    package MyFile require Exporter; our @ISA = qw(Exporter); our $BY_SIZE = 0; our $BY_USER = 1; our $BY_DATE = 3; @EXPORT_OK = qw( $BY_SIZE $BY_USER $BY_DATE );
    But I have multiple packages in one .pm file. And now I don't seem to be able to do this. If you say it is bad practice to have 1 method, consider than an object that returns a specific error message number. In that case also you would like to export the possible values of the error number as the programmer would be able to use that predefined value in his code rather than some vague number. In C you would put these values in a .h file. Considering the replies it seems that everyone recommends seperate files for each package. Something I find cumbersome for distribution. But it seems I have no choice.

      "In C you would put these values in a .h file."

      In C, you wouldn't have multiple namespaces. That's the primary difference here.

      In C++, if memory serves, you could have multiple namespaces and then in your .cpp (or .cxx or whatever) file, you would have using std; using object1; ... and there's no way to auto-import a bunch of namespaces, and doing so in the header would defeat much of the purpose of namespaces (they don't export just small pieces like we can in Perl).

      And, in my experience, one tarball isn't any more cumbersome with one file in it or a hundred. :-) With very very few exceptions, I would say targeting a single file for deployment is misguided and/or optimistic. You'll end up with a config file, and maybe some data files (e.g., DDL for database deployment), and you'll quickly be beyond a single file anyway. May as well embrace the inevitable from the beginning. You'll also end up with code that's easier to write and maintain.

      It's not that you have no choice. It's that it will take you more effort to do than if you were to follow the convention. I do have a module I just wrote for $work the other week that does something similar to what you say. But it's definitely more work to set up than following convention. I've only done this this way because of some experience with our setup that says that this will make things a little easier as we keep forgetting to import some functions that we need nearly everywhere. Normally, that's not an issue, so for everything else, it's use Module; as normal.

      If you want MyThing to export the stuff from Object1, check out Import::Into. This might work:

      package MyThing; use Exporter qw(export_to_level); # no need for inheritance here. use Import::Into; # magic our @EXPORT = qw( $BY_SIZE $BY_DATE $BY_USER ); sub import { shift->export_to_level(1); # handle our exports Object1->import::into(scalar caller); # calls Object1's import metho +d but uses magic. } #... package Object1; # has to handle its exports here as if it were in its own file. 1;
      Still more work than normal, but probably will work. Though, again, if you don't know what you're doing, this probably isn't what you want. Use separate files.