Perl Use Variable declared in 'new' - perl

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.

Related

Access Fully Qualified Variable Name from Variable in Perl Strict Mode

I have a series of modules that perform output functions for my scripts. Sometimes the module is called directly -- it is called View -- and sometimes a child class that extends it is used instead (View::ChildName). View declares our $THEMENAME = 'default'; when it is loaded, but the child declares its own specific $THEMENAME when it loads.
Problem: When new() is called on a child theme, it calls my $self = $class->next::method(%params); (using mro) to get some core things set by the parent class before extending it. One of the core bits is that the parent class sets $self->{'themeName'}. However, if it simply calls $THEMENAME, it gets the parent's setting: "default."
The only way I've reliably and successfully solved this is to turn off strict temporarily and do this:
my $packName = ref $self;
{
no strict;
$self->{'themeName'} = ${${packName} . "::THEMENAME"};
}
This works, but in profiling the code, if objects are created frequently, this actually adds more overhead than I expected. I tried the alternative of always using the parent's package name, e.g. the child sets $View::THEMENAME. This works, but only if the theme name is set within new and not on the load of the module; if it is on load, there is erratic behavior if several different child objects (of different children) are created over the course of the script.
These options both seem less than ideal. Is there a good way to do this? The only thing I found was this old question and I think incorporating Moo would probably add more overhead than I'm trying to avoid by getting rid of my current no strict block. Has anything been added to more modern versions of Perl that might solve my issue?
The alternative is to dodge the issue all together and simply set $self->{'themeName'} within each child object's new method, although I'm trying to avoid that change since there's a fair number of legacy child classes that expect $THEMENAME to exist.
Minimal reproducible example of View.pm:
use strict;
package View;
our $THEMENAME = 'default';
sub new {
my $class = shift;
my $params = shift;
my $self = { 'setting' => $params{'setting'} };
bless $self, $class;
$self->{'themeName'} = $THEMENAME;
return $self;
}
And of View/Child.pm:
use strict;
use mro;
package View::Child;
use parent 'View';
our $THEMENAME = 'child';
sub new {
my $class = shift;
my $params = shift;
my $self = $class->next::method($params);
bless $self, $class;
say STDOUT $self->{'themeName'};
# Prints 'default' not 'child'.
return $self;
}
Now a script to call it:
use View::Child;
my $object = View::Child->new();
If you added the first code block to View.pm, it gives the expected result instead, but seems to add about 9 ms to each call to new -- more than the time it takes for it to handle everything else I have in the much longer full length new method -- which adds up if the program runs many iterations:
use strict;
package View;
our $THEMENAME = 'default';
sub new {
my $class = shift;
my $params = shift;
my $self = { };
bless $self, $class;
my $packName = ref $self;
{
no strict;
$self->{'themeName'} = ${${packName} . "::THEMENAME"};
}
return $self;
}
The concept of class properties is one you should forget (in Perl). It's fine for the module to have constants and possibly variables, but they shouldn't be considered part of the class.
I see four approaches you could take:
Update the property in the child's constructor.
Provide the value as a parameter
Override the accessor
Provide the default as a method
Update the property in the child's constructor
# View.pm
sub new {
my ($class, $params) = #_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = 'default';
return $self;
}
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
sub new {
my ($class, $params) = #_;
my $self = $class->next::method($params);
$self->{ themeName } = 'child';
return $self;
}
Provide the value as a parameter
# View.pm
sub new {
my ($class, $params) = #_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = $params->{ themeName } // 'default';
return $self;
}
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
sub new {
my ($class, $params) = #_;
my $self = $class->next::method({ themeName => 'child', %$params });
return $self;
}
Override the accessor
# View.pm
sub new {
my ($class, $params) = #_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
return $self;
}
sub themeName { 'default' }
# Child/View.pm
# No need to override `new`.
sub themeName { 'child' }
Provide the default as a method
# View.pm
sub new {
my ($class, $params) = #_;
my $self = bless({}, $class);
$self->{ setting } = $params->{ setting };
$self->{ themeName } = $class->defaultThemeName;
return $self;
}
sub defaultThemeName { 'default' }
sub themeName { $_[0]->{themeName} } # Optional
# Child/View.pm
# No need to override `new`.
sub defaultThemeName { 'child' }
One potential solution would be to add THEMENAME as an overridable method.
View.pm:
use strict;
package View;
our $THEMENAME = 'default';
sub THEMENAME {return 'default'}
sub new {
my $class = shift;
my $params = shift;
my $self = { 'setting' => $params->{'setting'} };
bless $self, $class;
$self->{'themeName'} = $self->THEMENAME;
return $self;
}
View/Child.pm:
use strict;
use mro;
package View::Child;
use parent 'View';
our $THEMENAME = 'child';
sub THEMENAME {return 'child'}
sub new {
my $class = shift;
my $params = shift;
my $self = $class->next::method($params);
bless $self, $class;
say STDOUT $self->{'themeName'};
# Prints 'default' not 'child'.
#
return $self;
}
# perl -Mlib=. -MView::Child -e 'View::Child->new()'
child
My final solution follows what #plentyofcoffee and #ikegami outlined, but I wanted to see if I could come up with a way to set it automatically without each child module implementing it (keeping in mind legacy code). Assuming the child does want to set it, it passes $param{'themeName'} to the parent's constructor, which sets it to $self->{'themeName'}. If themeName is undefined, I came up with this regex in the parent class that extracts the name of the child as a fallback themeName:
unless ($self->{'themeName'}) {
state $getThemeNameRegEx = qr#^SAFARI::(.*::)+(.*?)$#;
$class =~ /$getThemeNameRegEx/;
$self->{'themeName'} = $2 // "default";
}
This sets to default if the name doesn't contain at least two levels below SAFARI, e.g. SAFARI::View is default (the parent module is in use without a child) and SAFARI::View::mysite is mysite.

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;
}

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.

How to create (or not) class instance methods at construction time based on inputs?

How would I create my class such that some methods will exist in the instance only if certain values were passed to the constructor?
Perhaps a more generic way of asking is: How can I add a method to an existing class instance?
You can attach an anonymous sub to an object based on flags:
use strict;
use warnings;
package Object;
sub new {
my $class = shift;
my $self = bless {}, $class;
my %args = #_;
if ($args{method}) {
$self->{method} = sub { print "hello\n" }
}
return $self;
}
sub method {
my $self = shift;
if (not defined $self->{method}) {
warn "Not bound\n";
return;
}
$self->{method}->();
}
1;
to use:
use Object;
my $obj1 = Object->new(method=>1);
$obj1->method();
my $obj2 = Object->new();
$obj2->method();
You can extend this to a number of methods through the same interface.
You can use Moose to apply a role at runtime.
package My::Class;
use Moose;
has foo => ( isa => 'Str', is => 'ro', required => 1 );
sub BUILD {
my $self = shift;
if ($self->foo eq 'bar') {
My::Class::Role->meta->apply($self);
}
}
no Moose;
package My::Class::Role;
use Moose::Role;
sub frobnicate {
my $self = shift;
print "Frobnicated!\n";
}
no Moose;
my $something = My::Class->new( foo => 'bar' );
print $something, "\n";
$something->frobnicate;
my $something_else = My::Class->new( foo => 'baz' );
print $something_else, "\n";
$something_else->frobnicate;
Gives:
Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x2fd5a10)
Frobnicated!
My::Class=HASH(0x2fd2c08)
Can't locate object method "frobnicate" via package "My::Class" at testmoose.pl line 32.
use AUTOLOAD to define the function. As a example method foo is called if $self->{foo} exists
sub AUTOLOAD {
my $methodname = $AUTOLOAD;
if ($methodname eq "foo" && exists($_[0]->{foo})){
goto &fooimplementationsub;
}
return;
}
An alternative technique is to use globs to define a new method at runtime
*PACKAGE::method = sub {
#code here
};
This has the disadvantage that the method is now visible to all instances of the class so is not quite what you want.
A third and possibly more risky/inefficient method is to use string eval
eval <<EOF
sub foo {
#code here
};
EOF
Again this has the disadvantage that the method is now visible to all instances of the class so is not quite what you want.
Methods are just subroutines in a package, and a package is just a hash holding typeglobs. And hashes can be modified at runtime.
So you could, in theory, add or remove methods given values in a constructor.
package WeirdClass;
sub new {
my ($class, $name, $code) = #_;
if ($name) {
no strict;
*{__PACKAGE__ . "::$name"} = $code;
}
bless {} => $class;
}
And then use it like:
my $object = WeirdClass->new(foo => sub {say "foo"});
$object->foo(); # prints "foo\n";
However, this method is available for all objects of that class:
my $another_object = WeirdClass->new();
$another_object->foo; # works too.
Using autoload, one can mock arbitrary methods:
package BetterClass;
sub new {
my ($class, %args) = #_;
bless \%args => $class;
}
# destructor will be called at cleanup, catch with empty implementation
sub DESTROY {};
sub AUTOLOAD {
my $self = shift;
(my $method = our $AUTOLOAD) =~ s/.*://; # $AUTOLOAD is like "BetterClass::foo"
# check if method is allowed
die "forbidden method $method" unless $self->{can}{$method};
# mock implementations
given ($method) {
say "foo" when "foo";
say "bar" when "bar";
when ("add") {
my ($x, $y) = #_;
return $x + $y;
}
default { die "unknown method $method" }
}
}
Then:
my $o = BetterClass->new(can => { foo => 1, bar => 0});
$o->foo;
my $p = BetterClass->new(can => {bar => 1, add => 1});
$p->bar;
say $p->add(5, 6);
Of course, these techniques can be combined freely.
Edit: can()
To make the AUTOLOAD work with can, the protected methods should be moved into a data structure:
my %methods;
BEGIN {
%methods = (
foo => sub {say "foo"},
bar => sub {say "bar"},
add => sub {
my ($self, $x, $y) = #_;
$x + $y;
},
);
}
Then override the can method:
# save a reference to the origional `can` before we override
my $orig_can;
BEGIN{ $orig_can = __PACKAGE__->can("can") }
sub can {
my ($self, $meth) = #_;
# check if we have a special method
my $code = $methods{$meth} if ref $self and $self->{can}{$meth};
return $code if $code;
# check if we have a normal method
return $self->$orig_can($meth);
}
And AUTOLOAD would change to
my ($self) = #_; # do not `shift`
(my $method = our $AUTOLOAD) =~ s/.*://;
my $code = $self->can($method) or die "unknown method $method";
goto &$code; # special goto. This is a AUTOLOAD idiom, and avoids extra call stack frames
Don't do too much magic. I've gotten away from AUTOLOAD because it causes maintenance issues where mysterious methods suddenly appear and disappear.
One way to handle what you want is to define all the methods you need, and if a particular object is of the wrong type, simply cause that method to croak:
sub Foo {
my $self = shift;
my $parameter = shift;
if ( $self->Class_type ne "Foo" ) {
croak qq(Invalid method 'Foo' on object #{[ref $self]});
}
print "here be dragons\";
return "Method 'Foo' successfully called";
}
The above will not allow method Foo to be called unless the class type is Foo.
If your objects won't change (or you don't want them to change) once an object is created, you can define that object as a sub-class.
Before you bless a newly created object, check that special value and decide whether or not you need to create a specific sub-class instead.
package My_class;
sub new {
my $class = shift;
my $class_type = shift;
my $self = shift;
if ( $class_type eq "Foo" ) {
bless $self, "My_class::Foo";
}
else {
bless $self, $class;
}
package My_class::Foo;
use base qw(My_class);
sub Foo {
my $self = shift;
return "Foo Method successfully called!";
}
Notice that my class My_class::Foo is a sub-class of My_class via the use base pragma. That means all methods for My_class are valid with objects of My_class::Foo. However, only objects of My_class::Foo can call the Foo method.
When I create my object (via the new subroutine), I look at the $class_type parameter. If it's a type Foo, I bless the class as My_class::Foo.
Here's an example where I use sub-classes to do what you want.
Every object is a class type of Question. You can see my constructor on line 1129. I pass in a question type as one of the parameters to my constructor.
In line 1174 to 1176, I create my object, but then append the question type to the class, and then bless the question as that sub-class type. All of my subclasses are a type Question (see my use base qw(Question); below each package declaration. However, only questions of sub-class Question::Date and Question::Regex have a method Format. And, only objects of type Question::Words have a method Force.
Hope this helps.
None of the answers so far given actually handle the question actually asked.
Adding methods to an instance in Perl is not directly supported. Object instances are always instances of some class, and that class is the thing that actually has methods. You cannot add a method to a single instance of a class, without making that method also available on every other instance of the same class.
For your problem you have two basic solutions:
Provide the methods always, but test a flag to see whether the method should apply to the given instance or not. This is by far the simplest.
Bless each object into subclasses depending on the flags. Subclass the main class to provide those methods as appropriate.
If you truely want to add methods on individual instances, then what you'll have to do is arrange that every instance is a single instance of a newly-derived class for every object. This gets harder to arrange for, doubly-so if you want to avoid leaking memory and cleaning up the classes once the objects are DESTROYed. This would however allow truely per-instance methods.
Since it is highly unlikely you'll truely need this third option it is far better to go with one of the first.