Is a cyclic dependency solved with require? - perl

I noticed that I had a cyclic dependency in 2 of my modules. So I did the following:
package A::B::ModuleA;
sub foo {
my ($class, $params) = #_;
# some processing
require A::C::ModuleB;
my $mb = A::C::ModuleB->new();
$mb->bar($params);
# some other processing
}
1;
package A::C::ModuleB;
sub process {
my ($class, $input) = #_;
# Some processing
require A::B::ModuleA;
my $ma = A::B::ModuleA;
$ma->submit($input);
# some other processing
}
1;
So my question is, if the way that I have addressed the cyclic dependency problem via the require inside the function solves any kind of issue that could be a result of such a dependency.

For purely object-oriented code, there is no circular dependency problem. You can quite happily have something like:
# AAAA.pm
package AAAA;
use strict;
use warnings;
use BBBB;
sub new {
my $class = shift;
my ($i) = #_;
bless {
b => $i > 0 ? BBBB->new($i-1) : $i
}, $class;
}
1;
# BBBB.pm
package BBBB;
use strict;
use warnings;
use AAAA;
sub new {
my $class = shift;
my ($i) = #_;
bless {
a => $i > 0 ? AAAA->new($i-1) : $i
}, $class;
}
1;
# script.pl
use strict;
use warnings;
use AAAA;
use Data::Dumper;
print Dumper( AAAA->new(4) );
Circular dependencies only become an issue if you need do something with a module at compile-time. Exporters are the most common example of this.

Related

What is the preferred convention for Perl Inheritance

In the example below, I have 3 different syntax/mechanisms for defining inheritance. All of them work. Can someone tell me which one is preferred and why (yes, I know "there is more than ..."). Also, why do I need "use WB" in 1 case and not the other 2. TIA
The main -
#!/usr/local/bin/perl -w
#######################
use strict;
use River;
use Ocean;
use Lake;
my $pName = $0; $pName =~ s/.*\///; $pName =~ s/\.\w+$//;
my #sources = (Ocean->new ('Pacific', 1),
River->new ('Brazos', 0),
Lake->new ('Tahoe', 0) );
foreach (#sources) {
$_->printIfSaline ($pName);
}
The modules (Lake, River and Ocean inherit from WaterBody):
######################
# File: Lake.pm
######################
package Lake;
use strict;
use WaterBody;
our #ISA = ('WaterBody');
sub new {
my $class = shift;
$class->SUPER::new(#_)
}
sub printIfSaline {
my ($self, $prompt) = #_;
my $name = $self->getName();
my $taste = $self->isSaline() ? "salty" : "sweet";
print "$prompt: Lake $name has $taste water\n";
}
1
######################
# File: Ocean.pm
######################
package Ocean;
use strict;
use parent 'WaterBody';
sub new {
my $class = shift;
$class->SUPER::new(#_);
}
sub printIfSaline {
my ($self, $prompt) = #_;
my $name = $self->getName;
my $taste = $self->SUPER::isSaline() ? "salty" : "sweet";
print "$prompt: $name Ocean has $taste water\n";
}
1
######################
# File: River.pm
######################
package River;
use strict;
use base 'WaterBody';
sub new {
my $class = shift;
$class->SUPER::new(#_);
}
sub printIfSaline {
my ($self, $prompt) = #_;
my $name = $self->getName;
my $taste = $self->isSaline ? "salty" : "sweet";
print "$prompt: $name river has $taste water\n";
}
1
######################
# File: WaterBody.pm
######################
package WaterBody;
sub new {
my $class = shift;
my $self = {};
$self->{name} = shift;
$self->{saline} = shift;
bless $self, $class;
return $self;
}
sub getName {
my ($self) = #_;
$self->{name}
}
sub isSaline {
my ($self) = #_;
$self->{saline}
}
1
The use parent pragma sets up the #ISA at compile time. From parent
Establish an ISA relationship with base classes at compile time
When you use ParentClass; and then manually set up #ISA that happens at run time. In this case code in BEGIN, CHECK, or INIT blocks won't be able to use the inheritance hierarchy without extra work.
The use base pragma is older and parent is recommended in docs. From base
Unless you are using the fields pragma, consider this module discouraged in favor of the lighter-weight parent
Thus I'd say go with use parent.
Manipulating #ISA is the oldest way. base was the second way, and parent is even newer. So, I'd recommend parent for new projects.
use parent 'Foo::Bar';
is cleanest, although the repetitious
use Foo::Bar qw( );
our #ISA = 'Foo::Bar';
is still commonly used. The former also has the advantage of happening sooner (when the file is compiled) than the latter (when the file is executed), though that rarely matters.
base is discouraged because it silences errors that occur when a module is loaded.

Can't locate object method XX via package "1"

I am well aware that there are several questions on a similar subjects but I fail to see how to apply the answers to my problem :
< Can't locate object method "idx" via package "1" >
What I don't understand is that I am using the same architecture in two packages and that it is OK in the first one... Where is the package "1" coming from ?
Here is the package that works fine :
package ObjA;
use warnings;
use strict;
use Data::Dumper;
use Carp;
use ObjB;
#CONSTRUCTOR AND INITIALISATION
sub new {
my $class = shift;
my $self = {#_};
bless($self,$class);
$self->language();
return $self;
}
sub load {
my $self = shift;
open (my $stream,"<",$self ->{name});
my #glob_xs=();
my $i = 0;
while (<$stream>){
$i += 1;
my #x = extract($stream,());
#glob_xs=(#glob_tokens,#x);
}
$self->tokens(\#glob_xs);
}
sub extract{
my ($stream,#x) = #_;
my $line = <$stream>;
chomp $line;
if ($line =~ /^\s*$/){
return #x;
}
print join("/",split("\t",$line));
my $b = ObjB::new(split("\t",$line));
push #x,$b->form;
extract_sentence($stream,#x);
}
# OBJECT ACCESSOR METHODS
sub language {$_[0]->{language}=$_[1] if defined $_[1] ; $_[0]->{language}}
1;
And here is the one that produces the error :
package ObjB;
use warnings;
use strict;
use Data::Dumper;
use Carp;
# CONSTRUCTOR AND INITIALISATION
sub new {
my $class = shift;
my $self = {#_};
bless($self,$class);
$self->idx(); # Dies here.
return $self;
}
# OBJECT ACCESSOR METHODS
sub idx {$_[0]->{idx}=$_[1] if defined $_[1] ; $_[0]->{idx}}
1;
Would it be because ObjB is called inside ObjA ? Or because they are declared in two different files ?
I truly hope someone will have an answer because I have been running in circles ...
Thank you !!
Obj::new is a method, but you call it as a subroutine.
ObjB::new(split("\t",$line));
This results in the value of first field of the line being used as the class, and that value is probably 1. You probably meant to use
ObjB->new(split("\t",$line));

How do you write wrapper module?

I'm writing a download sub module, I would like it looks like this:
Download.pm
Download/Wget.pm
Download/LWP.pm
Download/Curl.pm
Download/Socket.pm
My Download.pm should provide an api sub download($url). It will look for LWP module, then wget command, then curl command, if non of these exist, it will use Socket.
How can I write wrapper module?
Here is some example, how i did it:
How it works? It checks for some condition, and creates object depends on this condition. And subroutine also checks for reference type and calls the right method
file /tmp/Adapt/Base.pm (base module):
#!/usr/bin/perl
package Adapt::Base;
use strict;
use warnings;
sub new {
my $class = shift;
my $self;
if ( time % 3 ) {
require "/tmp/Adapt/First.pm";
$self = \Adapt::First->new(#_);
}
elsif ( time % 2 ){
require "/tmp/Adapt/Second.pm";
$self = \Adapt::Second->new(#_);
}
else {
require "/tmp/Adapt/Default.pm";
$self = \Adapt::Default->new(#_);
}
bless( $self, $class );
}
sub somesub {
my $s = shift;
my $self = $$s;
if ( ref( $self ) eq 'Adapt::First' ) {
$self->firstsub();
}
elsif ( ref( $self ) eq 'Adapt::Second' ) {
$self->secondsub();
}
else {
$self->defaultsub();
}
}
1;
file /tmp/Adapt/First.pm (some module):
#!/usr/bin/perl
package Adapt::First;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {};
bless( $self, $class );
}
sub firstsub {
print "I am 1st sub.\n";
}
1;
file /tmp/Adapt/Second.pm (another module):
#!/usr/bin/perl
package Adapt::Second;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {};
bless( $self, $class );
}
sub secondsub {
print "I am 2nd sub.\n";
}
1;
and file /tmp/Adapt/Default.pm (default module):
#!/usr/bin/perl
package Adapt::Default;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {};
bless( $self, $class );
}
sub defaultsub {
print "I am default sub.\n";
}
1;
and test script:
#!/usr/bin/perl
use strict;
use warnings;
require '/tmp/Adapt/Base.pm';
for (0..10) {
my $test = Adapt::Base->new;
$test->somesub;
sleep 1;
}
output:
dev# perl /tmp/adapt.pl
I am default sub.
I am 1st sub.
I am 1st sub.
I am 2nd sub.
I am 1st sub.
I am 1st sub.
I am default sub.
I am 1st sub.
I am 1st sub.
I am 2nd sub.
I am 1st sub.
dev#

Inheritance and child methods

For some reason I'm not able to access the child methods on the boundary object. I would appreciate as much detail with an answer as possible as I'm still a bit confused on inheritance with perl, especially the bless portion. Also any constructive criticism would be great about overall design.
Generic.pm (Base Class)
package AccessList::Generic;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {
rules => [],
#_
};
bless $self, $class;
return $self;
}
sub get_line_count {
my $self = shift;
return scalar #{$self->{rules}};
}
1;
Extended.pm
package AccessList::Extended;
use strict;
use warnings;
use AccessList::Generic;
use base qw(AccessList::Generic);
sub new {
my ($class, #args) = #_;
my $self = $class->SUPER::new(#args);
return $self;
}
1;
Boundary.pm
package AccessList::Extended::Boundary;
use strict;
use warnings;
use AccessList::Extended;
use base qw(AccessList::Extended);
sub new {
my ($class, #args) = #_;
my $self = $class->SUPER::new(#args);
return $self;
}
sub get_acl_information {
my ($self) = #_;
return;
}
1;
Failing Test
can_ok('AccessList::Extended::Boundary', 'get_acl_information');
Error Message
# Failed test 'AccessList::Extended::Boundary->can('get_acl_information')'
# at t/b1.t line 42.
# AccessList::Extended::Boundary->can('get_acl_information') failed
# Looks like you failed 1 test of 2.
I don't see any problems in what you posted. The problem is surely in what you didn't post. Did you forget to load AccessList::Extended::Boundary?
$ find -type f
./AccessList/Extended/Boundary.pm
./AccessList/Extended.pm
./AccessList/Generic.pm
$ perl -E'
use Test::More tests => 1;
use AccessList::Extended::Boundary;
can_ok("AccessList::Extended::Boundary", "get_acl_information");
'
1..1
ok 1 - AccessList::Extended::Boundary->can('get_acl_information')

Can I access a static method in a dynamically specified class in Perl?

Is it possible to dynamically specify a class in Perl and access a static method in that class? This does not work, but illustrates what I'd like to do:
use Test::Class1;
my $class = 'Test::Class1';
$class::static_method();
I know I can do this:
$class->static_method();
and ignore the class name passed to static_method, but I wonder if there's a better way.
Yup! The way to do it with strictures is to use can.
package Foo::Bar;
use strict;
use warnings;
sub baz
{
return "Passed in '#_' and ran baz!";
}
package main;
use strict;
use warnings;
my $class = 'Foo::Bar';
if (my $method = $class->can('baz'))
{
print "yup it can, and it ";
print $method->();
}
else
{
print "No it can't!";
}
can returns a reference to the method, undef / false. You then just have to call the method with the dereferene syntax.
It gives:
> perl foobar.pl
yup it can, and it Passed in '' and ran baz!
As always with Perl, there is more than one way to do it.
use strict;
use warnings;
{
package Test::Class;
sub static_method{ print join(' ', #_), "\n" }
}
You can use the special %:: variable to access the symbol table.
my $class = 'Test::Class';
my #depth = split '::', $class;
my $ref = \%::;
$ref = $glob->{$_.'::'} for #depth; # $::{'Test::'}{'Class::'}
$code = $glob->{'static_method'};
$code->('Hello','World');
You could just simply use a symbolic reference;
no strict 'refs';
my $code = &{"${class}::static_method"};
# or
my $code = *{"${class}::static_method"}{CODE};
$code->('Hello','World');
You could also use a string eval.
eval "${class}::static_method('Hello','World')";
The simplest in this case, would be to use UNIVERSAL::can.
$code = $class->can('static_method');
$code->('Hello','World');
I am unaware of a particularly nice way of doing this, but there are some less nice ways, such as this program:
#!/usr/bin/perl -w
use strict;
package Test::Class1;
sub static_method {
print join(", ", #_) . "\n";
}
package main;
my $class = "Test::Class1";
{
no strict "refs";
&{${class}. "::static_method"}(1, 2, 3);
}
I have included a $class variable, as that was how you asked the question, and it illustrates how the class name can be chosen at runtime, but if you know the class beforehand, you could just as easily call &{"Test::Class1::static_method"}(1, 2, 3);
Note that you have to switch off strict "refs" if you have it on.
There are three main ways to call a static function:
$object->static_method()
Classname->static_method()
Classname::static_method()
You could define your function like this:
# callable as $object->static_method() or Classname->static_method()
sub static_method
{
my $class = shift; # ignore; not needed
# ...
}
or like this, which works in all three calling scenarios, and doesn't incur any overhead on the caller's side like Robert P's solution does:
use UNIVERSAL qw(isa);
sub static_method
{
my $class = shift if $_[0] and isa($_[0], __PACKAGE__);
# ...
}
You can use string eval:
#!/usr/bin/perl
use strict; use warnings;
package Test::Class1;
sub static_method {
print join(", ", #_) . "\n";
}
package main;
my $class = 'Test::Class1';
my $static_method = 'static_method';
my $subref = eval q{ \&{ "${class}::${static_method}" } };
$subref->(1, 2, 3);
Output:
C:\Temp> z
1, 2, 3
Benchmarks:
#!/usr/bin/perl
use strict; use warnings;
package Test::Class1;
sub static_method { "#_" }
package main;
use strict; use warnings;
use Benchmark qw( cmpthese );
my $class = 'Test::Class1';
my $static_method = 'static_method';
cmpthese -1, {
'can' => sub { my $r = $class->can($static_method); $r->(1, 2, 3) },
'eval' => sub {
my $r = eval q/ \&{ "${class}::${static_method}" } /;
$r->(1, 2, 3);
},
'nostrict' => sub {
no strict "refs";
my $r = \&{ "${class}::static_method" };
$r->(1, 2, 3);
}
};
Output:
Rate eval can nostrict
eval 12775/s -- -94% -95%
can 206355/s 1515% -- -15%
nostrict 241889/s 1793% 17% --