I need to define some modules and use them all in the same file. No, I can't change the requirement.
I would like to do something like the following:
{
package FooObj;
sub new { ... }
sub add_data { ... }
}
{
package BarObj;
use FooObj;
sub new {
...
# BarObj "has a" FooObj
my $self = ( myFoo => FooObj->new() );
...
}
sub some_method { ... }
}
my $bar = BarObj->new();
However, this results in the message:
Can't locate FooObj.pm in @INC ...
BEGIN failed...
How do I get this to work?
Drop the use
. Seriously.
use
tells perl to read in the code from another file, which you don't need to do because the code is in the same file.
Unless I'm trying to create a private package that no one should know about, I put one package per file. That solves the problem. But, let's put them in the same file.
The use loads a file and calls the import
method in that package. It's really only incidently that its argument looks like a module name. It's looking for the file. If the file is not there, it barfs.
You can do this, where BarObj
assumes that FooObj
is already there:
{
package FooObj;
sub new { bless { _count => 0 }, $_[0] }
sub add_data { $_[0]->{_count}++ }
}
{
package BarObj;
use Data::Dumper;
sub new {
bless { myFoo => FooObj->new }, $_[0];
}
sub foo { $_[0]->{myFoo} }
sub some_method { print Dumper( $_[0] ) }
}
my $bar = BarObj->new;
$bar->some_method;
If you need to interact with a package (and that's all it is: not a module or an object), you just need to have it defined before you want to use it. If you need to import something, you can call the import
directly:
FooObj->import( ... );
Suppose there's something from FooObj
that you want to import (but not inherit!), you call import
directly with no loading;
{
package FooObj;
use Data::Dumper;
sub new { bless { _count => 0 }, $_[0] }
sub add_data { $_[0]->{_count}++ }
use Exporter qw(import);
our @EXPORT = qw(dumper);
sub dumper { print Dumper( $_[0] ) }
}
{
package BarObj;
FooObj->import;
sub new {
bless { myFoo => FooObj->new }, $_[0];
}
sub foo { $_[0]->{myFoo} }
# dumper mixin, not inherited.
sub some_method { dumper( $_[0] ) }
}
my $bar = BarObj->new;
$bar->some_method;
By convention we put one package in one file and name them the same thing, but that is just for convenience. You can put multiple packages in a single file. Since they are already loaded, you do not need to use use
.
You also do not need to create special scoping for the packages, as the package keyword takes care of that. Using the braces does help with scoping of our
variables. So you don't strictly need those brace blocks, but they're a good idea.
use
uses a package naming convention to find the appropriate file to load. The package
keyword inside the module defines the namespace. And the import functions handle the package loading (generally inherited from Exporter).
#!/usr/bin/perl
use strict;
use warnings;
package FooObj;
sub new
{
my $this = shift;
my $class = ref($this) || $this;
my $self = {};
bless $self, $class;
$self->initialize();
return $self;
}
sub initialize { }
sub add_data { }
package BarObj;
#use FooObj; <-- not needed.
sub new
{
my $this = shift;
my $class = ref($this) || $this;
my $self = { myFoo => FooObj->new() };
bless $self, $class;
$self->initialize();
return $self;
}
sub initialize { }
sub some_method { }
sub myFoo { return $_[0]->{myFoo} }
package main;
use Test::More;
my $bar = BarObj->new();
isa_ok( $bar, 'BarObj', "bar is a BarObj" );
isa_ok( $bar->myFoo, 'FooObj', "bar->myFoo is a FooObj" );
done_testing();
__DATA__
ok 1 - bar is a BarObj isa BarObj
ok 2 - bar->myFoo is a FooObj isa FooObj
1..2