How to test module functions which use hardcoded configuration file? - perl

I want to make some tests on my modules.
Unfortunately, some functions in these modules use hardcoded configurations files.
package My::Module;
use strict;
use warnings;
use Readonly;
Readonly my $CONF_FILE => '/my/conf_file.xml';
=head1 FUNCTIONS
=head2 Info($appli)
Returns Application Information
=cut
sub Info
{
my $appli = shift;
my $conf = MyXML::Read($CONF_FILE);
foreach my $a (ARRAY($conf->{application}))
{
return ($a) if ($a->{name} eq $appli);
}
return (undef);
}
[some others functions that use this config file...]
The solution that came to my mind is to create a new function in each module that will change this default config file when I need it.
Then I will use that function in my tests...
Do you have any other (better ?) ideas ?

Well, the proper thing for me to tell you would be "don't use hard coded paths". It'll come back and bite you at some point in the future, I promise.
But... assuming you're resolved to using them, there are a number of ways to allow an override. You're right you could add a function that would let you change it, or you could use an environmental variable:
Readonly my $CONF_FILE => $ENV{'MY_CONF_FILE'} || '/foo/bar';
But the right thing to do is still to allow for other items to be passed in properly if you have a choice.

Related

Import perl variables into the module

Is there any way in perl to import variables from the main script to the module?
Here is my main.pl:
#!/usr/bin/perl -w
use DBI;
our $db = DBI->connect(...);
__END__
Now I want to use the $db variable in my modules, because I want to avoid duplicate connections and duplicate codes... It is possible?
You can do that by referring to $main::db in other packages. The main namespace always point to globals in the primary namespace if there is none other given. You should read up on package.
Note that this is not a very good idea as your modules will be dependent on main having the connection. Instead, you should construct your objects in a way that let you pass a database handle in. If you require a db connection at all cost, either let them throw an exception or create their own db handle.
If you are not using OO code, make the database handle an argument of every function call.
Also note that it's best practice to name the database handle $dbh.
Let's look at this for non-OO (Foo) and OO (Bar).
# this is package main (but you don't need to say so)
use strictures;
use DBI;
use Foo;
use Bar;
my $dbh = DBI->connect($dsn);
Foo::frobnicate($dbh, 1, 2)
my $bar = Bar->new(dbh => $dbh);
$bar->frobnicate(23);
package Foo;
use strictures;
sub frobnicate {
my ($dbh, $one, $two) = #_;
die q{No dbh given} unless $dbh; # could check ref($dbh)
$dbh->do( ... );
return;
}
package Bar;
use strictures;
sub new {
my ($class, %args) = #_;
die q{No dbh given} unless $args{dbh};
return bless \%args, $class;
}
sub frobnicate {
my ($self, $stuff) = #_;
$self->{dbh}->do(q{INSERT INTO bar SET baz=?}, undef, $stuff);
return;
}
__END__
You can always pass a db handle into a method. I'm not a fan of this approach, but we have code that functions using this approach.
The problem IMHO is in the debugging. It makes it difficult to know anything about the db handle itself from the code in your module, though that might not be an issue for you. Imagine, however going in to debug code that uses a db handle, but you have no idea where it came from. If you get your db handle from a method in your class, you can trace it to that subroutine and immediately you have some information. This is definitely my preferred way of doing things.
If you do pass in a DB handle, you should do some input validation, such as checking for $dbh->isa('DBI::db') (I think that's the class into which db handles are blessed).
My preference, however would be to have a subroutine in your class that gets the db handle, either based on information you pass in, or by information in the sub itself. One thing to consider is that if you're using DBI, the connect_cached() method is very helpful. From the DBI docs:
connect_cached is like "connect", except that the database handle returned is also stored in a hash associated with the given parameters. If another call is made to connect_cached with the same parameter values, then the corresponding cached $dbh will be returned if it is still valid. The cached database handle is replaced with a new connection if it has been disconnected or if the ping method fails.
Using db handle caching of some sort will, regardless of whether you were to have created the db handle in your script or in the class, give you the same connection.
So, I recommend creating a method in your class that takes all of the parameters required to replicate the creation of the db handle as you'd do it in your script, and consider using connect_cached, Apache2::DBI or something that will handle the db connection pooling/abstraction.

How does this call to a subroutine in a Perl module work?

I recently saw some Perl code that confused me. I took out all of the extra parts to see how it was working, but I still don't understand why it works.
Basically, I created this dummy "module" (TTT.pm):
use strict;
use warnings;
package TTT;
sub new {
my $class = shift;
return bless {'Test' => 'Test'}, $class;
}
sub acquire {
my $tt = new TTT();
return $tt;
}
1;
Then I created this script to use the module (ttt.pl):
#!/usr/bin/perl
use strict;
use warnings;
use TTT;
our $VERSION = 1;
my $tt = acquire TTT;
print $tt->{Test};
The line that confuses me, that I thought would not work, is:
my $tt = acquire TTT;
I thought it would not work since the "acquire" sub was never exported. But it does work.
I was confused by the "TTT" after the call to acquire, so I removed that, leaving the line like this:
my $tt = acquire;
And it complained of a bareword, like I thought it would. I added parens, like this:
my $tt = acquire();
And it complained that there wasn't a main::acquire, like I thought it would.
I'm used to the subroutines being available to the object, or subroutines being exported, but I've never seen a subroutine get called with the package name on the end. I don't even know how to search for this on Google.
So my question is, How does adding the package name after the subroutine call work? I've never seen anything like that before, and it probably isn't good practice, but can someone explain what Perl is doing?
Thanks!
You are using the indirect object syntax that Perl allows (but in modern code is discouraged). Basically, if a name is not predeclared, it can be placed in front of an object (or class name) separated with a space.
So the line acquire TTT actually means TTT->acquire. If you actually had a subroutine named acquire in scope, it would instead be interpreted as aquire(TTT) which is can lead to ambiguity (hence why it is discouraged).
You should also update the new TTT(); line in the method to read TTT->new;
It's the indirect object syntax for method calls, which lets you put the method name before the object name.
As the documentation there shows, it's best avoided because it's unwieldy and it can break in unpredictable ways, for example if there is an imported or defined subroutine named acquire — but it used to be more common than it is today, and so you will find it pretty often in old code and docs.

Should I use Internals::SvREADONLY for creating readonly variables in Perl?

Looking into the Const::Fast source I noticed that it used the built-in function Internals::SvREADONLY internally. Is it safe to use that function directly in my Perl script? It seems to be present in core from Perl 5.8.
my $PI = 4 * atan2 1, 1;
Internals::SvREADONLY($PI => 1);
$PI = 2.718; # Modification of a read-only value attempted at ..
C:\>perldoc Internals
No documentation found for "Internals".
No.
More specifically, the package is named "Internals" for a reason. It is not intended for use outside the core. It could change without notice.
This is not quite answering your question, but I think it is worth mentioning so others don't experience the same pain as I have: don't use any readonly value if you're running on a version of Perl earlier than 5.10.1. Consider this little example:
{
package Foo;
sub foo { print "I'm in foo!\n"; }
}
use strict;
use warnings;
use Readonly;
Readonly my #classes => qw(Foo);
foreach my $class (#classes)
{
# this dies with "Can't call method "foo" without a package or object reference"
$class->foo;
}
Since my XS-fu is not very high, I can't explain what is going on here very coherently (but Devel::Peek shows some interesting things in the $class variable).

Is it good practice to export variables in Perl?

I'm finding it very convenient to pass configuration and other data that is read or calculated once but then used many times throughout a program by using Perl's use mechanism. I'm doing this by exporting a hash into the caller's namespace. For example:
package Myconfiguration;
my %config;
sub import {
my $callpkg = caller(0);
my $expsym = $_[1];
configure() unless %config;
*{"$callpkg\::$expsym"} = \%config;
}
and then in other modules:
use MyConfiguration (loc_config_sym);
if ( $loc_config_sym{paramater} ) {
# ... do stuff ...
}
However, I'm not sure about this as a best practice. Is it better to add a method that returns a hash ref with the data? Something else?
If you only want to read the values of %config, then why not have a routine to do it for you?
my %config;
sub config_value
{
my ($value) = #_;
return $config{$value};
}
You could export this by default if you wanted to:
package Mypackage;
require Exporter;
#EXPORT = qw/config_value/;
The reason that I would not allow access to the hash all over the place in lots of different modules is that I would have a hard time mentally keeping track of all the places it was being used. I would rather make the above kind of access routine so that, if some bug happened, I could add a print statement to the routine, or something, to find out when the value was being accessed. I don't know if that is related to "best practices" or it is just because I'm stupid, but the kind of confusion created by global variables scares me.
There's no reason you can't have a set routine too:
sub set_value
{
my ($key, $value) = #_;
$config{$key} = $value;
}
I think it's better to work with a copy of the config hash. This way, if you modify some elements, this won't affect the rest of your code.
I usually use simple object (optionally Singleton) for this with a single method like get_property().
I suggest never exporting variables. Create a class that can return a reference to a private variable instead. People can then store it in a variable with whichever name they like, and only when they decide they want to use it.
In general, it's best to let the user decide whether or not to import symbols. Exporter makes this easy. Writing a custom import method to let the user decide what to name imported symbols can be useful on rare occasions but I don't think this is one of them.
package MyConfiguration;
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT_OK = qw(Config);
our %Config;
And then, in your script:
use MyConfiguration;
print $MyConfiguration::Config{key};
or
use MyConfiguration qw(Config);
print $Config{key};

How can I call a Perl class with a shorter name?

I am writing a Perl module Galaxy::SGE::MakeJobSH with OO.
I want to use MakeJobSH->new() instead of Galaxy::SGE::MakeJobSH->new(),
or some other shortnames. How can I do that?
You can suggest that your users use the aliased module to load yours:
use aliased 'Galaxy::SGE::MakeJobSH';
my $job = MakeJobSH->new();
Or you could export your class name in a variable named $MakeJobSH;
use Galaxy::SGE::MakeJobSH; # Assume this exports $MakeJobSH = 'Galaxy::SGE::MakeJobSH';
my $job = $MakeJobSH->new();
Or you could export a MakeJobSH function that returns your class name:
use Galaxy::SGE::MakeJobSH; # Assume this exports the MakeJobSH function
my $job = MakeJobSH->new();
I'm not sure this is all that great an idea, though. People don't usually have to type the class name all that often.
Here's what you'd do in your class for the last two options:
package Galaxy::SGE::MakeJobSH;
use Exporter 'import';
our #EXPORT = qw(MakeJobSH $MakeJobSH);
our $MakeJobSH = __PACKAGE__;
sub MakeJobSH () { __PACKAGE__ };
Of course, you'd probably want to pick just one of those methods. I've just combined them to avoid duplicating examples.
I don't bother with aliasing. I think it's the wrong way to go. If you're just looking for less to type, it might be the answer (but is a new dependency more benefit than risk?). I don't like the idea of tricking a maintenance programmer by hiding the real name from him since the aliasing happens a long way away from its use and there's no indication that what looks like a class name isn't a real class.
I'm mostly looking for easy subclassing, so I let the class decide for itself which module will implement a part.
For instance, I might start with a class that wants to use Foo to handle part of the job. I know that I might want to subclass Foo later, so I don't hard-code it:
package Foo::Bar;
sub foo_class { 'Foo' }
sub new {
....
eval "require $self->foo_class";
$self->foo_class->do_something;
}
In the application, I choose to use 'Foo::Bar':
#!perl
use Foo::Bar;
my $obj = Foo::Bar->new();
Later, I need to specialise Foo, so I create a subclass overrides the parts I need:
package Foo::Bar::Baz;
use parent 'Foo::Bar';
sub foo_class { 'Local::Foo::SomeFeature' }
1;
Another application uses almost all of the same stuff, but with the small tweak:
#!perl
use Foo::Bar::Baz;
my $obj = Foo::Bar::Baz->new();
You can also do a similar thing at the application level if you want to write one program and let users choose the class through configuration.
Thanks cjm.
I just choose to inline aliased.
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(MakeJobSH);
sub MakeJobSH() {return 'Galaxy::SGE::MakeJobSH';}
aliased works well when you want to only affect calls from packages that explicitly request the aliasing. If you want global aliasing of one namespace to another, use Package::Alias instead.
It is almost exactly same approach as aliased but using standard Perl module:
use constant MakeJobSH => 'Galaxy::SGE::MakeJobSH';
my $job = MakeJobSH->new();