views:

62

answers:

3

I am experiencing a problem with using a constant defined in a configuration file. This is my package:

package myPackage;
require "APIconfig.pl";
APIconfig::import(APIconfig);

use constant SERVICE_URL => APIconfig::SERVICE_URL();

The configuration looks like this:

package APIconfig;
use constant SERVICE_URL => 'http://api.example.org/blah';
1;

When running this code, I get the following error:

Undefined subroutine &APIconfig::SERVICE_URL called at API.pl line 4.

I cannot use 'use' instead of 'require' because this expects the configuration file to be named .pm, and it's called .pl on a lot of servers on our network. How can I use the package without renaming the file?

+2  A: 

That can't be possibly right - there is no subroutine import in package APIconfig. Once you're accessing symbolic names with a full package path, you don't need to export/import anyway.

The solution is to run require at compile time, before use constant. This works:

package myPackage;
BEGIN {
    require "APIconfig.pl";
}

use constant SERVICE_URL => APIconfig::SERVICE_URL();
daxim
+7  A: 

There are two differences between 'use' and 'require'. One of them affects your current problem, the other doesn't. Unfortunately you are working around one that has no effect.

The differences are:

1/ 'use' calls the import() function, 'require' doesn't.

2/ 'use' happens at compile time, 'require' happens at runtime.

You're working around the fact that 'require' doesn't call import() by calling it explicitly. This has no effect as your module doesn't export any symbols and doesn't have an import() subroutine.

You're not working around the fact that 'use' statements are executed at runtime. The problem is that "use constant SERVICE_URL => APIconfig::SERVICE_URL();" is executed at compile time and your 'require' hasn't run by then so myPackage knows nothing about APIconfig.

The (nasty, hacky) solution is to put the 'require' statement into a BEGIN block - to force it to be executed at compile time. You'll also want to remove the call to import() as that gives a runtime error (due to the absence of the subroutine).

The test files that I used to work this out are as follows:

$ cat APIconfig.pl 
package APIconfig;
use constant SERVICE_URL => 'http://api.example.org/blah';
1;

$ cat api.pl 
#!/usr/bin/perl

package myPackage;
BEGIN {
  require "APIconfig.pl";
}
# APIconfig::import(APIconfig);

use constant SERVICE_URL => APIconfig::SERVICE_URL();

print SERVICE_URL, "\n";
$ ./api.pl 
http://api.example.org/blah

The real solution is to rewrite APIconfig as a real module. You hint that you know that, but that environmental issues prevent you taking this approach. I highly recommend trying to work around those issues and doing things correctly.

davorg
+1  A: 

If it's a configuration file, don't make it code. I have a whole chapter in Mastering Perl about that, and there are many modules on CPAN to help you with almost any configuration format.

If it's code, why not just make it a module so you can use use. Modules are so much easier to control and manipulate within another program.

The easiest solutions are the ones where you don't swim against the tide. :)

Beyond that, use is the same as:

 BEGIN {
      require Module;
      Module->import;
      }

You just do the same thing with filename and the namespace it defines (as long as the code in the file looks like a module):

 BEGIN {
      require "file.pl";  # defines SomeNamespace
      SomeNamespace->import;
      }
brian d foy