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

Hello Monks

I'm doing some preliminary tests on factory design pattern with PERL and I've found the following issue. If I implement my factory like this, I have no problem.

package GreetFactory; use strict; use warnings; use lib './Greet'; sub instantiate { my $class = shift; my $requested_type = shift; my $location = "Greet/$requested_type.pm"; $class = "Greet::$requested_type"; require $location; return $class->new(@_); } 1;

But if I implement my factory like that, then a load error is displayed (Can't locate object method "new" via package "lib::Repeat" (perhaps you forgot to load "lib::Repeat"?) at GreetFactory.pm line 13).

package GreetFactory; use strict; use warnings; use lib './lib'; sub instantiate { my $class = shift; my $requested_type = shift; my $location = "lib/$requested_type.pm"; $class = "lib::$requested_type"; require $location; return $class->new(@_); } 1;

Of course, with the 1st test, Repeat.pm is located into a 'Greet' subdirectory, and I rename it 'lib' before doing the 2nd test. Everything happens as if 'lib::' is badly interpreted because 'lib' may be a PERL reserved word ?

Replies are listed 'Best First'.
Re: Design pattern factory : use lib issue on lib directory
by Eily (Monsignor) on Apr 01, 2016 at 16:32 UTC

    Renaming the directory isn't enough, you also have to change the package declaration at the top of Repeat.pm.

      Hello, You are fully right. I've just checked the package was named package Greet.::Repeat; Sorry !
Re: Design pattern factory : use lib issue on lib directory
by Corion (Patriarch) on Apr 01, 2016 at 16:34 UTC

    What is the namespace of the loaded file?

    In Perl , the filename and the namespace of the loaded class(es) can be different. If you didn't change the contents of the file Greet/Repeat.pm when you moved it to lib/Repeat.pm, it will still fill in a namespace Great::Repeat.

    Also see Module::Pluggable.

Re: Design pattern factory : use lib issue on lib directory
by haukex (Archbishop) on Apr 02, 2016 at 08:49 UTC

    Hi CraigWalker,

    I'm not sure I understand your intent in the second example. Are you trying to relocate the class Greet::Repeat into the lib/ subdirectory, or do you actually want to rename the Greet::Repeat class to lib::Repeat, as Eily and Corion have pointed out?

    If it's the former, and you want to follow the normal Perl convention of class and module naming, that means you'll have a file lib/Greet/Repeat.pm that contains the Greet::Repeat class. If so, then all you need to do to make your first piece of example code work is change the use lib statement to use lib './lib';. lib will prepend that path to @INC, so when you tell require to load the file Greet/Repeat.pm, it will look for ./lib/Greet/Repeat.pm first.

    Also, the use lib './Greet'; you've got in the first code example actually doesn't help you if the file is named Greet/Repeat.pm - require will first look for ./Greet/Greet/Repeat.pm, which it doesn't find, then it looks through the rest of @INC. The first piece of example code works because the current directory . is in @INC by default. I think this can vary, but in my case . is last in @INC, so that perl actually looks through all the other @INC directories for a file named Greet/Repeat.pm first, which might not be what you want either. And AFAIK there are cases where the current directory isn't even in @INC, so that the script would fail there.

    Similarly, if your intent really was to rename Greet::Repeat to lib::Repeat, then the use lib './lib'; in your second code example is also not necessary, since you've already added that path to the filename in my $location = "lib/$requested_type.pm";.

    In any case, I recommend that you don't rely on the current directory . as much. It's acceptable for initial testing, but in production I'd strongly recommend you use absolute paths for lib, or you use a different way of setting up @INC properly. Sometimes FindBin can help you (but it's not perfect, so be careful and test it first). (For example, if I've got a script I call from the command line and it needs a module that I know will always be in the same directory as the script, I'll sometimes write use FindBin; use lib $FindBin::Bin; use ModuleName;.)

    Hope this helps,
    -- Hauke D