Object Oriented In Perl Script - perl

sub new
{
my $class = shift; #off the first element
my $self = { };
bless $self, $class;
return $self;
}
Could anyone explain that? What is the use of the following three lines of code?
my $self = { };
bless $self, $class;
return $self;

my $self = { }; creates an anonymous hash reference and stores it in the lexical variable $self.
bless $self, $class; tells Perl that $self is not just any reference but actually an object of the class stored in $class. See bless in perldoc. bless $x, $y returns $x, and a subroutine always returns the value of the last executed statement unless explicetly told otherwise with a return statement, so the next line is optional, but good for readability.
return $self; hands the value in $self (our special object reference) back to the calling function. See return in perldoc.
Edit:
To clarify, if you don't bless your reference, you won't be able to call methods on it. With bless you tell Perl, "look, from now on, associate the reference in $self with the class in $class, so that I can use the methods in that class on the reference."

Related

Not a code reference in Perl class

I'm stumped. I'm new to Perl and after reading some articles, I still can't figure this one out. It's a very small class.
package Haha;
sub new {
$class = shift;
$self = {
path => shift
};
bless $self, $class;
return $self;
}
sub setPath {
my ($self, $new_path) = shift;
$self->(path) = $new_path if defined $new_path;
return $self->(path);
}
sub getPath {
my $self = shift;
return $self->(path);
}
1;
And I used it like this:
use lib 'lib';
use Haha;
my $new_excel = new Haha("sample path");
print $new_excel->getPath() ;
<>;
Class Haha line 23 raises the "Not a code reference" error.
The line that says return $self->(path);
Your class (like most Perl classes) is implemented on top of hashes. When you create a new object in your constructor, you do it like this:
sub new {
$class = shift;
$self = {
path => shift
};
bless $self, $class;
return $self;
}
The line $self = { ... } creates an anonymous hash and stores a reference to that hash in $self. So, $self is a hash reference. Which means that you should access its contents using hash syntax. So your accessor and mutator methods are wrong.
sub setPath {
my ($self, $new_path) = shift;
$self->(path) = $new_path if defined $new_path;
return $self->(path);
}
You are using parentheses, not braces, to access the path value in your hash. The line:
$self->(path) = $new_path if defined $new_path;
Should be:
# Note: braces, not parentheses
$self->{path} = $new_path if defined $new_path;
And the line:
return $self->(path);
Should be:
# Note: braces, not parentheses
return $self->{path};
You need to make a similar fix to getPath().
Unfortunately, the syntax $reference->($value) is completely valid. It means "call the subroutine that you have a reference to in $reference, passing it $value". But, of course, this requires $reference to contain a subroutine reference, not a hash reference.
A few other suggestions.
Always use strict and use warnings.
Indirect object notation ($new_excel = new Haha("sample path")) is likely to burn you at some point. Please use $new_excel = Haha->new("sample path") instead.
Your line my ($self, $new_path) = shift doesn't do what you think it does. You want my ($self, $new_path) = #_.
path is an attribute of the object, use curly brackets:
sub getPath {
my $self = shift;
return $self->{path};
}
In the sub setPath, the variable $new_path is never assigned, use instead:
sub setPath {
my ($self, $new_path) = #_;
$self->{path} = $new_path if defined $new_path;
return $self->{path};
}

Perl - Can't Call new Method

I have been working on a small project and I decided to try and use Method::Signatures because I find it neater.
This is without using Method::Signatures and it works, I'm able to use the package and call methods.
sub new {
my $self = {};
bless($self);
shift;
$self->{parent} = shift;
return $self;
}
But when I try this, it doesn't work:
method new($parent) {
bless {}, $self;
$self->{parent} = $parent;
return $self;
}
I get an error saying: "Can't use string ("PackageName") as hashref while strict refs in use".
Method::Signatures automatically shifts the first argument off the argument list and puts it in $self for you. When you call an object method like $obj->foo, then $self is just $obj. But when you call a class method like Class->method, then $self will be the string Class.
Your $self contains the string PackageName, as it should since you're using new as a class method. Then you use PackageName as an argument to bless, but throw away the result!
bless {}, $self;
This blesses a new empty hashref (the { } ) into the package name contained in $self and then throws the result away. You're then trying to use $self as a hashref when it's just a string, which is causing the error. So try this:
method new($parent) {
my $obj = bless {}, $self;
$obj->{parent} = $parent;
return $obj;
}
method new($parent) {
my $instance = bless {} => $self;
$instance->{parent} = $parent;
return $instance;
}
In this case, $self in your new method will the package name. You then bless an empty anonymous hash into that package which gives you your instantiated object.
Incidentally, you should re-write your plain new method:
sub new {
my $class = shift;
my $self = bless {} => $class;
$self->{parent} = shift;
return $self;
}

Perl object, toString output from within module

I'm doing a class assignment to learn about Object Oriented programming in Perl. I've got a real basic class that looks like this.
sub new{
my $class = shift;
my $self = {
'Sides' => 3,
'SL' => \#sidelengths};
bless $self, $class;
return $self;
}
I've got two modules to change the sides and length(can't figure out how to modify the sidelegnths with an accessor though) but I have a requirement for my work that I have a method like this
"a method: toString() which returns all of the file attributes in a printable
string. If this is done correctly, the PERL
print $file->toString() . "\n";
should print a readable summary of the file."
I already think I want to use Data::Dumper to do this and that works within a script but it sounds like I need to use it within a module and call that to print a string of whats in the object. So far I have this
sub toString{
my $self = #_;
Dumper( $self );
}
Which just prints out "$VAR1 = 1"
What you want here is to shift an argument out of #_.
sub toString {
my $self = shift #_;
Dumper( $self );
}
When you have $var = #array, that evaluates the array in a scalar context, and that returns the number of elements in the array. So, your statement my $self = #_; set $self to the number of arguments passed to toString, which in this case was 1. (The $self argument.)
Alternately, you can capture the first element of #_ this way:
sub toString {
my ($self) = #_;
Dumper( $self );
}
What this does is evaluate #_ in list context since it uses list assignment. It assigns the first element of #_ to $self.
my $self = #_;
is a scalar assignment operator, so it #_ in scalar context, which is the number of elements it contains. You want to use the list assignment operator.
sub toString {
my ($self) = #_;
return Dumper( $self );
}

Perl Use Variable declared in 'new'

If in a Perl module I have a 'new' function that declares:
my $self = $class->SUPER::new($pArgsProcessor, $pResponse, kStrFormatHtml);
$self->{mStrRunType} = $pArgsProcessor->readQueryString('runType');
$self->{mStrStartDate} = $pArgsProcessor->readQueryString('startdate');
$self->{mStrEndDate} = $pArgsProcessor->readQueryString('enddate');
bless $self, $class;
return $self;
Is there a way to use the data stored in '$self' in another function? I'm trying to use 'mStrRunType'
$self is probably an object, and all the subs in your package can be called as methods. Then:
my $object = Your::Class->new(...);
$object->foo(42);
Inside the foo method, the object will be the first argument:
sub foo {
my ($self, $meaning_of_life) = #_;
say "mStrEndDate = $self->{mStrEndDate}";
...;
}
Notes:
You should not generally rebless the $self in your constructor. If the superclasses are written to support inheritance, then $class->SUPER::new(...) ensures that the reference is blessed into the correct $class.
You naming scheme suggests you might want to use a more complex data structure:
$self->{mStr} = {
RunType => ...,
StartDate => ...,
EndDate => ...,
};
Your constructor looks correct. Assuming that your constructor is similar to this:
sub new {
my $class = shift;
my $pArgsProcessor, $pResponse, kStrFormatHtml; #shift your constructor params..
my $self = $class->SUPER::new($pArgsProcessor, $pResponse, kStrFormatHtml);
$self->{mStrRunType} = $pArgsProcessor->readQueryString('runType');
$self->{mStrStartDate} = $pArgsProcessor->readQueryString('startdate');
$self->{mStrEndDate} = $pArgsProcessor->readQueryString('enddate');
bless $self, $class;
return $self;
}
Then your method should be able to use your parameters:
sub test {
my $self = shift;
if (defined $self->{mStrEndDate}) {
print $self->{mStrEndDate};
} else {
print "not defined?";
}
}
If your keys are still undefined then make sure that $pArgsProcessor methods are returning defined values.

Can one pass Perl object references between modules?

Example code:
testClass1.pm
package testClass1;
{
my $testClass2Ref;
sub new
{
my($class) = shift;
$testClass2Ref= shift;
bless $self, $class;
return $self;}
}
sub testRef
{
$testClass2Ref->testRef;
}
}
testClass2.pm
package testClass2;
{
sub new
{
my($class) = shift;
bless $self, $class;
return $self;}
}
sub testRef
{
print "Test 2";
}
}
test.pl
use testClass1;
use testClass2;
my $testClass2 = testClass2->new();
my $testClass1 = testClass2->new($testClass2);
$testClass1->testRef;
When I try call $testClass1->testRef, $testClass2Ref=undef.
How can I pass reference on the object from parent?
Update
Oh, sorry, I missed string in example's constructors.
sub new
{
my($class) = shift;
$testClass2Ref = shift;
my $self = {name=>'testClass1'};
bless $self, $class;
return $self;
}
This test is working, but Eclipse debugger show this variables as 'undef'.
Thanks for your help.
Besides the syntax errors, you aren't using strict mode. Turning it on will reveal that $self isn't being declared in either package. By replacing:
bless $self, $class;
with:
my $self = bless {}, $class;
Everything goes through as expected.
When you fix the syntax errors it works.
> ./test.pl
> Test 2
You were missing
my $self = {};
in both new methods.
A useful tool is
perl -wc testClass1.pm
Can one pass Perl object references between modules?
Absolutely!
In this test script I make two classes, one that tests and one to be tested. Remember objects are just references and methods are just subroutines; use them in the same way.
#!/usr/bin/env perl
use strict;
use warnings;
package Tester;
sub new {
my $class = shift;
my ($other) = #_;
my $self = { other => $other };
bless $self, $class;
return $self;
}
sub examine {
my $self = shift;
print "I'm holding a: ", ref( $self->{other} ), "\n";
}
package Candidate;
sub new { return bless {}, shift }
package main;
my $candidate = Candidate->new();
my $tester = Tester->new( $candidate );
$tester->examine();
EDIT: Now using a more modern system, MooseX::Declare (which is based on Moose) with Method::Signatures. This saves a lot of the boilerplate and lets you focus on what you want the objects to do, rather then how they are implemented.
#!/usr/bin/env perl
#technically Moose adds strict and warnings, but ...
use strict;
use warnings;
use MooseX::Declare;
use Method::Signatures::Modifiers;
class Tester {
has 'other' => ( isa => 'Object', is => 'rw', required => 1 );
method examine () {
print "I'm holding a: ", ref( $self->other() ), "\n";
}
}
class Candidate { }
no MooseX::Declare;
package main;
my $candidate = Candidate->new();
my $tester = Tester->new( other => $candidate );
$tester->examine();
For more realistic cases, see how some larger module systems pass object representing complex concepts. Off the top of my head, HTTP::Response object get passed around all through the LWP system