How to get the value of class member? - perl

There is a following class:
package MyClass;
use strict;
use warnings;
sub new
{
my $class = shift();
my $self = {
_class_member => "default"
};
bless ($self, $class);
return $self;
}
How can I set/get the value of the _class_member?
I tried the following code:
sub set_name
{
my $self = shift();
$self->_class_member = shift();
}
But I get the following error:
Can't locate object method "_class_member" via package "MyClass" ...
What am I doing wrong here?

$self is a blessed hash. Unless the you or the original author provided the method _class_member, there's no such method.
You can however, "reach in" the hash to access it:
$self->{'_class_member'} = shift;
This is not a recommended practice for instance values because it's just as easy to type:
$self->{'_vlass_member'} = shift;
without a complaint. Hence the value of accessors.

Related

Perl - Can't locate object method via "Module::SUPER"

This is my first time using OOP with perl. I am in the processes of refactoring a 4k line procedural program at work. It seems pretty straight forward but I am having an issue with inheritance and SUPER.
Error message:
"Can't locate object method "New" via package "Module::SUPER" at Module.pm line 10"
I have tried, use base, parent and setting #ISA but they all get the same error. I'm sure I have to be overlooking something.
(This is not code from the program I am working on. Just an example that produces the same error)
All .pm and .pl files are in the same directory in this example. In the program I am working on the main program is in bin and the modules will be in ../modules(relative to bin).
I would assume this would be all I need to make that work:
use lib "../modules";
If I am wrong in thinking that please let me know.
Parent Module
package BaseModule;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {
ARRAY => shift,
DIVIDER => ","
};
bless ($self, $class);
return $self;
}
sub array {
my $self = shift;
if(#_) { $self->{ARRAY} = shift };
return $self->{ARRAY};
}
sub divider {
my $self = shift;
if(#_) { $self->{DIVIDER} = shift };
return $self->{DIVIDER};
}
sub testSub {
my $self = shift;
print join($self->{DIVIDER}, #{ $self->{ARRAY} } );
return 1;
}
1;
Child Module
package Module;
use strict;
use warnings;
#use base qw(BaseModule);
#require BaseModule;
#our #ISA = qw(BaseModule);
use parent qw(BaseModule);
sub new {
my $class = shift;
my $self = $class->SUPER::New(#_);
$self->{STRING} = shift;
bless ($self, $class);
return $self;
}
sub string {
my $self = shift;
if(#_) { $self->{STRING} = shift };
return $self->{STRING};
}
sub testSub {
my $self = shift;
print "$self->{STRING}:\n";
$self->SUPER::testSub();
return 1;
}
1;
Do I need to bless the child class if the parent class returns an already blessed $self?
Main Script
#!/usr/bin/perl
use strict;
use warnings;
use Module;
my $module = Module->new([1, 2, 3, 4, 5], "Example");
$module->divider(" | "); # Test Changing divider;
$module->testSub();
Any help is greatly appreciated.
"Can't locate object method "New" via package "Module::SUPER" at Module.pm line 10"
You try to call BaseModule::New whis hasn't been defined (did you mean BaseModule::new? Perl is case sensitive).
Do I need to bless the child class if the parent class returns an
already blessed $self?
No, $self at that point is already blesses (you could check that by means of Scalar::Util::blessed().

Unable to return the default value for the object attribute in perl

I have a module , when I am trying to get the default attribute set in initialization, is giving the following error when subroutine is being called get_name
Use of uninitialized value
sample code
package test;
#....
#....
sub new {
my ($class) = #_;
my $self = {};
bless $self,$class;
$self->_initialize();
return $self;
}
sub _initailalize {
my($self) = #_;
$self = {
_name => 'NA'
};
}
sub get_name {
return $_[0]->{_name};
}
valuable inputs required.
You are redefining $self in the local scope of _initialize and assigning a new hashref. That way you are not adding the _name key to the blessed object. After _initialize is done, your newly assigned $self (with a plain hashref) is gone. Thus in get_name it cannot access the key _name.
sub _initailalize { # btw typo here
my ($self) = #_;
$self->{_name} = 'NA';
}
If you do it like this, you will need to assign each member on an individual line.
As a suggestion, this built-in style of OO is very tedious. Try looking at Moose or it's derivatives as they are pretty powerful.

Access 'self' within an object when other arguments are also commited (perl)

I get an error message while trying to access $self and other variables in a sub inside an object. When the sub is called from outside the object everything works fine. But when I try to access it within the object I get an error (see below).
Here is an example code describing my problem:
package input;
use warnings;
use strict;
sub new {
my $class = shift;
my $self = { };
$self->{_name} = shift;
bless ($self, $class);
return $self;
}
sub test1{
my $self = shift;
my $person = shift;
return $self->{_name}." and ".$person;
}
sub test2{
my $self = shift;
my $person = shift;
print test1($self,$person);
}
package Main;
use warnings;
use strict;
my $i = input->new("Jon");
print $i->test1("Me")."\n";
$i->test2();
The call for print $i->test1("Me")."\n"; does work fine.
I like to access test1() within a different function inside the object.
But for $i->test2(); I get the error
Use of uninitiated value $person in concatenation (.) or String at Line 22.
If I would write
sub test2{
my $self = shift;
my $person = "Jim";
print test1($self,$person);
}
It would work, too.
But I explicitly want to pass some other Variables to the sub besides $self. Since I want to use $self and the other variables. I think it has something to do with passing $self to the sub or not, but I can't figure out how to access $self without the my $self = shift; command.
Use of uninitiated value $person in concatenation (.) or String at Line 22.
When calling test2 method you've forgot $person argument, ie.
$i->test2();
should be
$i->test2("Someone");

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.

Extending a Perl non-Moose respecting encapsulation

I have a legacy project and I would like to extend a couple of classes in it with a few attributes and methods. I have access to the source code and know that the class uses a blessed hashref. I can of course go ahead and extend that hashref adding the keys that I want and re-bless into my class. But obviously this breaks encapsulation and I would like to avoid it as much as I can.
Is there a way to extend a (non-Moose) Perl class with attributes, not just methods, in a way that does not break encapsulation of the orginal class? The option to use Moose to do this is not available. Thank you.
First, one best practice for writing objects based on hashrefs is to prefix all fields with the package name, e.g.
package Parent;
sub new {
my ($class, $x, $y) = #_;
bless { "Parent::x" => $x, "Parent::y" => $y } => $class;
}
sub x { shift()->{"Parent::x"} }
sub y { shift()->{"Parent::y"} }
In that case, the issue doesn't arise, as every class has its own attribute namespace. But who writes his classes that way?
There are two ways I can think of to circumvent any problems: Proxying the original object via Autoload, or using inside-out object patterns. The third solution is to use prefixed attributes in your class, and hope that the parent never ever uses these names.
Inside-Out Objects
An inside-out object uses the blessed reference as an ID, and stores the attributes in lexical variables inside your class:
package Child;
use Scalar::Util qw/refaddr/;
use parent 'Parent';
my %foo;
sub new {
my ($class, $foo, #args) = #_;
my $self = $class->SUPER::new(#args);
$foo{refaddr $self} = $foo;
return $self;
}
sub foo {
my ($self) = #_;
$foo{refaddr $self};
}
sub set_foo {
my ($self, $val) = #_;
$foo{refaddr $self} = $val;
}
sub DESTROY {
my ($self) = #_;
# remove entries for this object
delete $foo{refaddr $self};
$self->SUPER::DESTROY if $self->SUPER::can('DESTROY');
}
This is a slightly dated pattern, but it works extremely well for your use case.
Proxy objects
We can contain a parent instance in a field of our class (i.e. both has-a and is-a relationship). Whenever we encounter unknown methods, we delegate to that object:
package Child;
use Parent ();
our $SUPER = 'Parent';
use Carp;
sub new {
my ($class, $foo, #args) = #_;
bless {
parent => $SUPER->new(#args),
foo => $foo,
} => $class;
}
sub foo {
my ($self) = #_;
$self->{foo};
}
sub set_foo {
my ($self, $val) = #_;
$self->{foo} = $val;
}
# manually establish pseudo-inheritance
# return true if our class inherits a given package
sub isa {
my ($self, $class) = #_;
return !!1 if $class eq __PACKAGE__;
return +(ref $self ? $self->{parent} : $SUPER)->isa($class);
}
# return a coderef to that method, or false
sub can {
my ($self, $meth) = #_;
my %methods = (new => \&new, foo => \&foo, set_foo => \&set_foo, DESTROY => \&DESTROY);
if (my $code = $methods{$meth}) {
return $code;
}
# check parent
my $code = ( ref $self ? $self->{parent} : $SUPER)->can($meth);
return undef unless $code;
return sub {
my $self = shift;
unshift #_, ref $self ? $self->{parent} : $self;
goto &$code;
};
}
# write explicit destroy to satisfy autoload
sub DESTROY {
my ($self) = #_;
$self->{parent}->DESTROY if ref $self and $SUPER->can('DESTROY');
}
sub AUTOLOAD {
# fetch appropriate method coderef
my $meth = our $AUTOLOAD;
$meth =~ s/.*:://; # clean package name from name
my $code = $_[0]->can($meth);
$code or croak qq(Can't locate object method "$meth" via package "#{[__PACKAGE__]}");
goto &$code;
}
The ugly part is to fake methods defined in superclasses in the can code: We have to wrap the actual method inside a anonymous sub that unpacks our object to call the method on the proxied object. The gotos make our extra levels invisible to the called code, which is neccessary when somebody uses caller.
Most of this boilerplate proxying code can be abstracted into another module (and probably is, somewhere on CPAN).